Skip to content

Commit 359e67d

Browse files
committed
Fix the error and callback types
1 parent f603ddc commit 359e67d

File tree

1 file changed

+68
-34
lines changed

1 file changed

+68
-34
lines changed

nickel/src/capi.rs

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ impl nickel_context {
2929
}
3030
}
3131

32+
/// A Nickel error.
33+
///
34+
/// If you want to collect an error message from a fallible function
35+
/// (like `nickel_context_eval_deep`), first allocate an error using
36+
/// `nickel_error_alloc`, and then pass the resulting pointer to your fallible
37+
/// function. If that function fails, it will save the error data in your
38+
/// `nickel_error`.
39+
pub struct nickel_error {
40+
inner: Option<Error>,
41+
}
42+
3243
/// A Nickel expression.
3344
///
3445
/// This might be fully evaluated (for example, if you got it from [`nickel_context_eval_deep`])
@@ -105,6 +116,15 @@ impl<'a> From<Record<'a>> for *const nickel_record {
105116
}
106117
}
107118

119+
/// A Nickel string.
120+
// It would be nice to put `repr(transparent)` here, but (1) we don't
121+
// actually need to cast `String` pointers to `nickel_string` pointers,
122+
// and (2) adding `repr(transparent)` makes cbindgen expose `String`
123+
// even though it's private.
124+
pub struct nickel_string {
125+
inner: String,
126+
}
127+
108128
/// A Nickel number.
109129
///
110130
/// See [`nickel_expr_is_number`] and [`nickel_expr_as_number`].
@@ -153,11 +173,14 @@ pub unsafe extern "C" fn nickel_context_free(ctx: *mut nickel_context) {
153173
/// This function will be called with a buffer (`buf`) of data, having length
154174
/// `len`. It need not consume the entire buffer, and should return the number
155175
/// of bytes consumed.
176+
// This Option<fn> pattern seems to be cbindgen's preferred way of encoding
177+
// a nullable function pointer (since rust fns are never null).
178+
// https://github.com/mozilla/cbindgen/issues/326#issuecomment-584288686
156179
pub type nickel_write_callback =
157-
extern "C" fn(context: *const c_void, buf: *const u8, len: usize) -> usize;
180+
Option<extern "C" fn(context: *const c_void, buf: *const u8, len: usize) -> usize>;
158181

159182
/// A callback function for flushing data that was written by a write callback.
160-
pub type nickel_flush_callback = extern "C" fn(context: *const c_void);
183+
pub type nickel_flush_callback = Option<extern "C" fn(context: *const c_void)>;
161184

162185
/// For functions that can fail, these are the interpretations of the return value.
163186
#[repr(C)]
@@ -170,13 +193,17 @@ pub enum nickel_result {
170193

171194
struct CTrace {
172195
write: nickel_write_callback,
173-
flush: Option<nickel_flush_callback>,
196+
flush: nickel_flush_callback,
174197
context: *const c_void,
175198
}
176199

177200
impl Write for CTrace {
178201
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
179-
let count = (self.write)(self.context, buf.as_ptr(), buf.len());
202+
let count = if let Some(w) = self.write {
203+
w(self.context, buf.as_ptr(), buf.len())
204+
} else {
205+
buf.len()
206+
};
180207
if count == usize::MAX {
181208
Err(std::io::Error::other("trace failed to write"))
182209
} else {
@@ -198,8 +225,7 @@ impl Write for CTrace {
198225
pub unsafe extern "C" fn nickel_context_set_trace_callback(
199226
mut ctx: *mut nickel_context,
200227
write: nickel_write_callback,
201-
// TODO: if this is non-optional, are they allowed to pass NULL?
202-
flush: Option<nickel_flush_callback>,
228+
flush: nickel_flush_callback,
203229
user_data: *const c_void,
204230
) {
205231
let trace = Trace::new(CTrace {
@@ -232,7 +258,7 @@ unsafe fn do_eval<F: FnOnce(&mut crate::Context, &str) -> Result<Expr, Error>>(
232258
mut ctx: *mut nickel_context,
233259
src: *const c_char,
234260
mut out_expr: *mut nickel_expr,
235-
out_error: *mut *mut Error,
261+
out_error: *mut nickel_error,
236262
) -> nickel_result {
237263
let src = CStr::from_ptr(src).to_str().unwrap();
238264
match f(nickel_context::as_rust_mut(&mut ctx), src) {
@@ -244,7 +270,7 @@ unsafe fn do_eval<F: FnOnce(&mut crate::Context, &str) -> Result<Expr, Error>>(
244270
}
245271
Err(e) => {
246272
if !out_error.is_null() {
247-
*out_error = Box::into_raw(Box::new(e));
273+
(*out_error).inner = Some(e);
248274
}
249275
nickel_result::NICKEL_RESULT_ERR
250276
}
@@ -273,7 +299,7 @@ pub unsafe extern "C" fn nickel_context_eval_deep(
273299
ctx: *mut nickel_context,
274300
src: *const c_char,
275301
out_expr: *mut nickel_expr,
276-
out_error: *mut *mut Error,
302+
out_error: *mut nickel_error,
277303
) -> nickel_result {
278304
do_eval(|ctx, src| ctx.eval_deep(src), ctx, src, out_expr, out_error)
279305
}
@@ -300,7 +326,7 @@ pub unsafe extern "C" fn nickel_context_eval_deep_for_export(
300326
ctx: *mut nickel_context,
301327
src: *const c_char,
302328
out_expr: *mut nickel_expr,
303-
out_error: *mut *mut Error,
329+
out_error: *mut nickel_error,
304330
) -> nickel_result {
305331
do_eval(
306332
|ctx, src| ctx.eval_deep_for_export(src),
@@ -343,7 +369,7 @@ pub unsafe extern "C" fn nickel_context_eval_shallow(
343369
src: *const c_char,
344370
mut out_expr: *mut nickel_expr,
345371
out_virtual_machine: *mut nickel_virtual_machine,
346-
out_error: *mut *mut Error,
372+
out_error: *mut nickel_error,
347373
) -> nickel_result {
348374
let src = CStr::from_ptr(src).to_str().unwrap();
349375
match nickel_context::as_rust_mut(&mut ctx).eval_shallow(src) {
@@ -358,19 +384,13 @@ pub unsafe extern "C" fn nickel_context_eval_shallow(
358384
}
359385
Err(e) => {
360386
if !out_error.is_null() {
361-
*out_error = Box::into_raw(Box::new(e));
387+
(*out_error).inner = Some(e);
362388
}
363389
nickel_result::NICKEL_RESULT_ERR
364390
}
365391
}
366392
}
367393

368-
/// Frees a Nickel error message.
369-
#[no_mangle]
370-
pub unsafe extern "C" fn nickel_error_free(err: *mut Error) {
371-
let _ = Box::from_raw(err);
372-
}
373-
374394
/// Allocate a new Nickel expression.
375395
///
376396
/// The returned expression pointer can be used to store the results of
@@ -391,16 +411,16 @@ pub unsafe extern "C" fn nickel_error_free(err: *mut Error) {
391411
///
392412
/// nickel_context_eval_deep(ctx, "{ foo = 1 }", expr, NULL);
393413
///
394-
/// /* now expr is a record */
414+
/// // now expr is a record
395415
/// printf("record: %d\n", nickel_expr_is_record(expr));
396416
///
397417
/// nickel_context_eval_deep(ctx, "[1, 2, 3]", expr, NULL);
398418
///
399-
/// /* now expr is an array */
419+
/// // now expr is an array
400420
/// printf("array: %d\n", nickel_expr_is_array(expr));
401421
///
402-
/// /* the calls to nickel_context_eval_deep haven't created any new exprs:
403-
/// we only need to free it once */
422+
/// // the calls to nickel_context_eval_deep haven't created any new exprs:
423+
/// // we only need to free it once
404424
/// nickel_expr_free(expr);
405425
/// nickel_context_free(ctx);
406426
/// ```
@@ -650,12 +670,12 @@ pub unsafe extern "C" fn nickel_number_as_f64(num: *const nickel_number) -> f64
650670
#[no_mangle]
651671
pub unsafe extern "C" fn nickel_number_as_rational(
652672
num: *const nickel_number,
653-
out_numerator: *mut String,
654-
out_denominator: *mut String,
673+
out_numerator: *mut nickel_string,
674+
out_denominator: *mut nickel_string,
655675
) {
656676
let (numerator, denominator) = nickel_number::as_rust(&num).as_rational();
657-
*out_numerator = numerator;
658-
*out_denominator = denominator;
677+
*out_numerator = nickel_string { inner: numerator };
678+
*out_denominator = nickel_string { inner: denominator };
659679
}
660680

661681
/// The number of elements of this Nickel array.
@@ -748,13 +768,15 @@ pub unsafe extern "C" fn nickel_record_value_by_name(
748768
/// (see `nickel_expr_alloc`). It gets allocated here, modified by various other
749769
/// functions, and finally is freed by a call to `nickel_string_free`.
750770
#[no_mangle]
751-
pub unsafe extern "C" fn nickel_string_alloc() -> *mut String {
752-
Box::into_raw(Box::new(String::new()))
771+
pub unsafe extern "C" fn nickel_string_alloc() -> *mut nickel_string {
772+
Box::into_raw(Box::new(nickel_string {
773+
inner: String::new(),
774+
}))
753775
}
754776

755777
/// Frees a string.
756778
#[no_mangle]
757-
pub unsafe extern "C" fn nickel_string_free(s: *mut String) {
779+
pub unsafe extern "C" fn nickel_string_free(s: *mut nickel_string) {
758780
let _ = Box::from_raw(s);
759781
}
760782

@@ -766,13 +788,13 @@ pub unsafe extern "C" fn nickel_string_free(s: *mut String) {
766788
/// freed or overwritten.
767789
#[no_mangle]
768790
pub unsafe extern "C" fn nickel_string_data(
769-
s: *const String,
791+
s: *const nickel_string,
770792
data: *mut *const c_char,
771793
len: *mut usize,
772794
) {
773795
let s = s.as_ref().unwrap();
774-
*data = s.as_ptr() as *const c_char;
775-
*len = s.len();
796+
*data = s.inner.as_ptr() as *const c_char;
797+
*len = s.inner.len();
776798
}
777799

778800
/// Allocate space for a virtual machine.
@@ -803,7 +825,7 @@ pub unsafe extern "C" fn nickel_virtual_machine_eval_shallow(
803825
vm: *mut nickel_virtual_machine,
804826
expr: *const nickel_expr,
805827
mut out_expr: *mut nickel_expr,
806-
out_error: *mut *mut Error,
828+
out_error: *mut nickel_error,
807829
) -> nickel_result {
808830
// We clone `expr` instead of consuming it (as the rust API does). The clone is
809831
// cheap (it's only a refcount bump) and this makes the allocation/free pairing
@@ -830,9 +852,21 @@ pub unsafe extern "C" fn nickel_virtual_machine_eval_shallow(
830852
}
831853
Err(e) => {
832854
if !out_error.is_null() {
833-
*out_error = Box::into_raw(Box::new(e));
855+
(*out_error).inner = Some(e);
834856
}
835857
nickel_result::NICKEL_RESULT_ERR
836858
}
837859
}
838860
}
861+
862+
/// Allocate a new `nickel_error`.
863+
#[no_mangle]
864+
pub unsafe extern "C" fn nickel_error_alloc() -> *mut nickel_error {
865+
Box::into_raw(Box::new(nickel_error { inner: None }))
866+
}
867+
868+
/// Frees a `nickel_error`.
869+
#[no_mangle]
870+
pub unsafe extern "C" fn nickel_error_free(err: *mut nickel_error) {
871+
let _ = Box::from_raw(err);
872+
}

0 commit comments

Comments
 (0)