use super::*;
#[test]
fn takeable_callback_param() {
let loc = SourceLocation::default();
let st: syn::ItemStruct = syn::parse_quote!(
pub struct Sample {
pub _0: u64,
}
);
let func: syn::ItemFn = syn::parse_quote!(
pub fn z_declare_sub(cb: impl Fn(Sample) + Send + Sync + 'static) {
unimplemented!()
}
);
let registry = crate::test_util::reg_from_items(declare_referenced([
(syn::Item::Struct(st), loc.clone()),
(syn::Item::Fn(func), loc.clone()),
]))
.expect("index items");
let cbindgen = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.opaque_owned_struct(syn::parse_quote!(Sample), syn::parse_quote!(z_sample_t))
.callback(syn::parse_quote!(impl Fn(Sample) + Send + Sync + 'static))
.base_name("z_closure_sample_t")
.takeable_param(0)
.function(syn::parse_quote!(z_declare_sub));
let src = write(cbindgen, registry, "takeable");
let compact: String = src.split_whitespace().collect();
assert!(
compact.contains("call:::core::option::Option<unsafeextern\"C\"fn(*mutz_sample_t,*mut"),
"{src}"
);
// Trampoline passes `&mut __w0` and drops it after the call.
assert!(compact.contains("&mut__w0as*mutz_sample_t"), "{src}");
assert!(
compact.contains("<z_sample_tas::prebindgen_c_runtime::Transmute>::into_rust(__w0)"),
"{src}"
);
// Public take (move) function emitted (no name mangler in this test ⇒
// `sample_take`; a real adapter mangles to `z_sample_take`).
assert!(
compact
.contains("pubunsafeextern\"C\"fnsample_take(dst:*mutz_sample_t,src:*mutz_sample_t)"),
"{src}"
);
assert!(
compact.contains(
"ptr::write(src,<z_sample_tas::prebindgen_c_runtime::Gravestone>::gravestone(),)"
),
"{src}"
);
}
#[test]
fn callback_subscriber_emits_closure_structs() {
let loc = SourceLocation::default();
let func: syn::ItemFn = syn::parse_quote!(
pub fn z_sub(
session: &ZSession,
callback: impl Fn(ZSample) + Send + Sync + 'static,
on_close: impl Fn() + Send + Sync + 'static,
) -> Result<ZSubscriber, Error> {
unimplemented!()
}
);
let registry = crate::test_util::reg_from_items(declare_referenced([
(syn::Item::Fn(func), loc.clone()),
(syn::Item::Struct(error_struct()), loc.clone()),
]))
.expect("index items");
let cbindgen = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.free_memory_function("z_free")
.opaque_ptr(syn::parse_quote!(ZSession))
.base_name("z_session_t")
.opaque_ptr(syn::parse_quote!(ZSample))
.base_name("z_sample_t")
.opaque_ptr(syn::parse_quote!(ZSubscriber))
.base_name("z_subscriber_t")
.data_struct(syn::parse_quote!(Error))
.base_name("z_error")
.error()
.callback(syn::parse_quote!(impl Fn(ZSample) + Send + Sync + 'static))
.base_name("z_closure_sample_t")
.callback(syn::parse_quote!(impl Fn() + Send + Sync + 'static))
.base_name("z_closure_drop_t")
.function(syn::parse_quote!(z_sub));
let src = write(cbindgen, registry, "cb_sub");
let compact: String = src.split_whitespace().collect();
assert!(compact.contains("structz_closure_sample_t"), "{src}");
assert!(
compact.contains(
"pubcall:::core::option::Option<unsafeextern\"C\"fn(*mutz_sample_t,*mut::core::ffi::c_void),>"
),
"{src}"
);
assert!(compact.contains("structz_closure_drop_t"), "{src}");
assert!(
compact.contains(
"fn__cbg_in_z_closure_sample_t(c:z_closure_sample_t,)->implFn(zenoh_flat::ZSample)+Send+Sync+'static"
),
"{src}"
);
assert!(
compact.contains("Arc::new(__Ctx{context:c.context,drop:c.drop"),
"{src}"
);
assert!(
compact.contains("let__w0=__cbg_out_ZSample(__a0);"),
"{src}"
);
assert!(compact.contains("__f(__w0,__ctx.context)"), "{src}");
assert!(compact.contains("move|__a0:zenoh_flat::ZSample|"), "{src}");
assert!(
compact.contains(
"fn__cbg_in_z_closure_drop_t(c:z_closure_drop_t,)->implFn()+Send+Sync+'static"
),
"{src}"
);
assert!(compact.contains("move||{"), "{src}");
assert!(compact.contains("__f(__ctx.context)"), "{src}");
assert!(compact.contains("Some(__d)=self.drop"), "{src}");
assert!(compact.contains("__d(self.context)"), "{src}");
assert!(compact.contains("callback:z_closure_sample_t"), "{src}");
assert!(compact.contains("on_close:z_closure_drop_t"), "{src}");
assert!(
compact.contains("letcallback=__cbg_in_z_closure_sample_t(callback);"),
"{src}"
);
assert!(
compact.contains("leton_close=__cbg_in_z_closure_drop_t(on_close);"),
"{src}"
);
assert!(compact.contains("->*mutz_subscriber_t"), "{src}");
assert!(compact.contains("e:*mutz_error"), "{src}");
}
#[test]
fn callback_scalar_arg_not_module_qualified() {
let loc = SourceLocation::default();
let func: syn::ItemFn = syn::parse_quote!(
pub fn z_on_value(callback: impl Fn(f64) + Send + Sync + 'static) -> Result<(), Error> {
unimplemented!()
}
);
let registry = crate::test_util::reg_from_items(declare_referenced([
(syn::Item::Fn(func), loc.clone()),
(syn::Item::Struct(error_struct()), loc.clone()),
]))
.expect("index items");
let cbindgen = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.free_memory_function("z_free")
.data_struct(syn::parse_quote!(Error))
.base_name("z_error")
.error()
.callback(syn::parse_quote!(impl Fn(f64) + Send + Sync + 'static))
.base_name("z_closure_value_t")
.function(syn::parse_quote!(z_on_value));
let src = write(cbindgen, registry, "cb_scalar");
let compact: String = src.split_whitespace().collect();
assert!(!compact.contains("zenoh_flat::f64"), "{src}");
// Closure param + `impl Fn` return keep `f64` bare.
assert!(compact.contains("move|__a0:f64|"), "{src}");
assert!(
compact.contains(
"fn__cbg_in_z_closure_value_t(c:z_closure_value_t,)->implFn(f64)+Send+Sync+'static"
),
"{src}"
);
}
/// Without a `.name(...)` override the closure-struct C name is composed
/// generically from the args' configured C type names (`closure_<argCname>`)
/// — `lang::CbindgenBuilder` invents no target-language convention of its own.
#[test]
fn callback_struct_name_defaults_generically() {
let loc = SourceLocation::default();
let func: syn::ItemFn = syn::parse_quote!(
pub fn z_sub2(
session: &ZSession,
callback: impl Fn(ZSample) + Send + Sync + 'static,
) -> Result<ZSubscriber, Error> {
unimplemented!()
}
);
let registry = crate::test_util::reg_from_items(declare_referenced([
(syn::Item::Fn(func), loc.clone()),
(syn::Item::Struct(error_struct()), loc.clone()),
]))
.expect("index items");
let cbindgen = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.free_memory_function("z_free")
.opaque_ptr(syn::parse_quote!(ZSession))
.base_name("z_session_t")
.opaque_ptr(syn::parse_quote!(ZSample))
.base_name("z_sample_t")
.opaque_ptr(syn::parse_quote!(ZSubscriber))
.base_name("z_subscriber_t")
.data_struct(syn::parse_quote!(Error))
.base_name("z_error")
.error()
// No `.name(...)` on the callback ⇒ generic default.
.callback(syn::parse_quote!(impl Fn(ZSample) + Send + Sync + 'static))
.function(syn::parse_quote!(z_sub2));
let src = write(cbindgen, registry, "cb_default");
let compact: String = src.split_whitespace().collect();
// Composed from the arg's configured C name `z_sample_t`.
assert!(compact.contains("structclosure_z_sample_t"), "{src}");
assert!(compact.contains("callback:closure_z_sample_t"), "{src}");
}