#![allow(unsafe_code)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
use std::os::raw::c_char;
use weaveffi::abi::{self, c_ptr_to_string, free_string, string_to_c_ptr, weaveffi_error};
#[weaveffi::module]
pub mod demo {
#[weaveffi::error]
#[derive(Debug)]
pub enum DemoError {
DivisionByZero = 100,
}
#[weaveffi::enumeration]
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Color {
Red = 0,
Green = 1,
Blue = 2,
}
#[weaveffi::record]
#[derive(Clone)]
pub struct Point {
pub x: i32,
pub label: String,
pub nickname: Option<String>,
pub color: Color,
}
#[weaveffi::export]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[weaveffi::export]
pub fn checked_div(a: i32, b: i32) -> Result<i32, DemoError> {
if b == 0 {
return Err(DemoError::DivisionByZero);
}
Ok(a / b)
}
#[weaveffi::export]
pub fn greet(name: String) -> String {
format!("hi {name}")
}
#[weaveffi::export]
pub fn str_len(text: &str) -> i32 {
text.chars().count() as i32
}
#[weaveffi::export]
pub fn maybe_name(present: bool) -> Option<String> {
present.then(|| "present".to_string())
}
#[weaveffi::export]
pub fn sum(xs: Vec<i32>) -> i32 {
xs.iter().sum()
}
#[weaveffi::export]
pub fn join(parts: Vec<String>) -> String {
parts.join(",")
}
#[weaveffi::export]
pub fn byte_count(data: Vec<u8>) -> i32 {
data.len() as i32
}
#[weaveffi::export]
pub fn make_point(x: i32) -> Point {
Point {
x,
label: "origin".to_string(),
nickname: None,
color: Color::Green,
}
}
}
#[weaveffi::module]
pub mod warehouse {
#[weaveffi::record]
#[derive(Clone)]
pub struct Crate {
pub id: i64,
pub label: String,
}
#[weaveffi::export]
pub fn make_crate(id: i64, label: String) -> Crate {
Crate { id, label }
}
}
#[weaveffi::module]
pub mod dispatch {
use super::warehouse::Crate;
#[weaveffi::export]
pub fn crate_id(item: Crate) -> i64 {
item.id
}
#[weaveffi::export]
pub fn relabel(item: Crate, label: String) -> Crate {
Crate { id: item.id, label }
}
}
#[weaveffi::module]
pub mod maps {
use std::collections::BTreeMap;
#[weaveffi::export]
pub fn double_scores(scores: BTreeMap<String, i32>) -> BTreeMap<String, i32> {
scores.into_iter().map(|(k, v)| (k, v * 2)).collect()
}
#[weaveffi::export]
pub fn total(scores: BTreeMap<String, i32>) -> i32 {
scores.values().sum()
}
}
#[weaveffi::module]
pub mod build {
#[weaveffi::record]
#[weaveffi::builder]
#[derive(Clone)]
pub struct Widget {
pub name: String,
pub qty: i32,
pub note: Option<String>,
}
}
#[weaveffi::module]
pub mod geom {
#[weaveffi::enumeration]
#[derive(Clone)]
pub enum Shape {
Empty,
Circle {
radius: f64,
},
Labeled {
label: String,
count: u8,
},
}
#[weaveffi::export]
pub fn describe(shape: &Shape) -> String {
match shape {
Shape::Empty => "empty".to_string(),
Shape::Circle { radius } => format!("circle({radius})"),
Shape::Labeled { label, count } => format!("{label}x{count}"),
}
}
}
#[weaveffi::module]
pub mod stream {
#[weaveffi::export]
pub fn greetings(count: i32) -> weaveffi::Iter<String> {
weaveffi::Iter::new((0..count).map(|i| format!("hi {i}")))
}
#[weaveffi::export]
pub fn squares(count: i32) -> weaveffi::Iter<i32> {
weaveffi::Iter::new((0..count).map(|i| i * i))
}
}
#[weaveffi::module]
pub mod bus {
#[weaveffi::callback]
#[allow(dead_code, unused_variables)]
fn on_message(text: String, weight: i32) {}
#[weaveffi::listener(event = "on_message")]
#[allow(dead_code)]
fn subscribers() {}
#[weaveffi::export]
pub fn publish(text: String, weight: i32) {
emit_subscribers(&text, weight);
}
}
#[weaveffi::module]
pub mod tasks {
#[weaveffi::error]
#[derive(Debug)]
pub enum TaskError {
Overflow = 1,
}
#[weaveffi::record]
#[derive(Clone)]
pub struct TaskResult {
pub id: i64,
pub value: String,
}
#[weaveffi::export]
pub async fn run_task(name: String) -> TaskResult {
TaskResult {
id: 7,
value: format!("done: {name}"),
}
}
#[weaveffi::export]
pub async fn checked_add(a: i32, b: i32) -> Result<i32, TaskError> {
a.checked_add(b).ok_or(TaskError::Overflow)
}
}
fn ok_err() -> weaveffi_error {
weaveffi_error::default()
}
#[test]
fn scalar_call_sets_ok() {
let mut err = ok_err();
let r = demo::weaveffi_demo_add(2, 40, &mut err);
assert_eq!(r, 42);
assert_eq!(err.code, 0);
assert!(err.message.is_null());
}
#[test]
fn fallible_ok_and_err_paths() {
let mut err = ok_err();
assert_eq!(demo::weaveffi_demo_checked_div(10, 2, &mut err), 5);
assert_eq!(err.code, 0);
let r = demo::weaveffi_demo_checked_div(1, 0, &mut err);
assert_eq!(r, 0, "error path returns the zero sentinel");
assert_eq!(
err.code, 100,
"domain code from the #[weaveffi::error] enum"
);
assert_eq!(c_ptr_to_string(err.message).unwrap(), "division by zero");
abi::error_clear(&mut err);
}
#[test]
fn owned_string_roundtrip() {
let mut err = ok_err();
let input = string_to_c_ptr("alice");
let out = demo::weaveffi_demo_greet(input, &mut err);
assert_eq!(err.code, 0);
assert_eq!(c_ptr_to_string(out).unwrap(), "hi alice");
free_string(out);
free_string(input);
}
#[test]
fn borrowed_str_param() {
let mut err = ok_err();
let input = string_to_c_ptr("héllo");
assert_eq!(demo::weaveffi_demo_str_len(input, &mut err), 5);
free_string(input);
}
#[test]
fn optional_string_return() {
let mut err = ok_err();
let some = demo::weaveffi_demo_maybe_name(true, &mut err);
assert_eq!(c_ptr_to_string(some).unwrap(), "present");
free_string(some);
let none = demo::weaveffi_demo_maybe_name(false, &mut err);
assert!(none.is_null());
}
#[test]
fn scalar_list_param() {
let mut err = ok_err();
let xs = [3i32, 4, 5];
let total = demo::weaveffi_demo_sum(xs.as_ptr(), xs.len(), &mut err);
assert_eq!(total, 12);
}
#[test]
fn string_list_param() {
let mut err = ok_err();
let parts = ["a", "b", "c"];
let ptrs: Vec<*const c_char> = parts.iter().map(string_to_c_ptr).collect();
let out = demo::weaveffi_demo_join(ptrs.as_ptr(), ptrs.len(), &mut err);
assert_eq!(c_ptr_to_string(out).unwrap(), "a,b,c");
free_string(out);
for p in ptrs {
free_string(p);
}
}
#[test]
fn byte_buffer_param() {
let mut err = ok_err();
let data = [1u8, 2, 3, 4, 5];
assert_eq!(
demo::weaveffi_demo_byte_count(data.as_ptr(), data.len(), &mut err),
5
);
}
#[test]
fn record_create_get_destroy() {
let mut err = ok_err();
let label = string_to_c_ptr("corner");
let nickname = string_to_c_ptr("nw");
let p = demo::weaveffi_demo_Point_create(7, label, nickname, 2, &mut err);
assert_eq!(err.code, 0);
assert!(!p.is_null());
free_string(label);
free_string(nickname);
assert_eq!(demo::weaveffi_demo_Point_get_x(p), 7);
assert_eq!(demo::weaveffi_demo_Point_get_color(p), 2);
let got_label = demo::weaveffi_demo_Point_get_label(p);
assert_eq!(c_ptr_to_string(got_label).unwrap(), "corner");
free_string(got_label);
let got_nick = demo::weaveffi_demo_Point_get_nickname(p);
assert_eq!(c_ptr_to_string(got_nick).unwrap(), "nw");
free_string(got_nick);
demo::weaveffi_demo_Point_destroy(p);
}
#[test]
fn record_optional_field_null() {
let mut err = ok_err();
let label = string_to_c_ptr("solo");
let p = demo::weaveffi_demo_Point_create(1, label, std::ptr::null(), 0, &mut err);
assert!(!p.is_null());
free_string(label);
let got_nick = demo::weaveffi_demo_Point_get_nickname(p);
assert!(got_nick.is_null(), "None lowers to a null string pointer");
demo::weaveffi_demo_Point_destroy(p);
}
#[test]
fn struct_return_by_value() {
let mut err = ok_err();
let p = demo::weaveffi_demo_make_point(99, &mut err);
assert!(!p.is_null());
assert_eq!(demo::weaveffi_demo_Point_get_x(p), 99);
let label = demo::weaveffi_demo_Point_get_label(p);
assert_eq!(c_ptr_to_string(label).unwrap(), "origin");
free_string(label);
demo::weaveffi_demo_Point_destroy(p);
}
#[test]
fn cross_module_struct_param_and_return() {
let mut err = ok_err();
let label = string_to_c_ptr("widget");
let c = warehouse::weaveffi_warehouse_make_crate(7, label, &mut err);
assert_eq!(err.code, 0);
assert!(!c.is_null());
free_string(label);
let id = dispatch::weaveffi_dispatch_crate_id(c, &mut err);
assert_eq!(id, 7);
assert_eq!(err.code, 0);
let new_label = string_to_c_ptr("gadget");
let c2 = dispatch::weaveffi_dispatch_relabel(c, new_label, &mut err);
assert!(!c2.is_null());
free_string(new_label);
let got = warehouse::weaveffi_warehouse_Crate_get_label(c2);
assert_eq!(c_ptr_to_string(got).unwrap(), "gadget");
free_string(got);
warehouse::weaveffi_warehouse_Crate_destroy(c);
warehouse::weaveffi_warehouse_Crate_destroy(c2);
}
#[test]
fn map_param_and_return() {
let mut err = ok_err();
let kb = string_to_c_ptr("b");
let ka = string_to_c_ptr("a");
let keys: [*const c_char; 2] = [kb, ka];
let values: [i32; 2] = [1, 2];
let mut out_keys: *mut *const c_char = std::ptr::null_mut();
let mut out_values: *mut i32 = std::ptr::null_mut();
let mut out_len: usize = 0;
maps::weaveffi_maps_double_scores(
keys.as_ptr(),
values.as_ptr(),
2,
&mut out_keys,
&mut out_values,
&mut out_len,
&mut err,
);
assert_eq!(err.code, 0);
assert_eq!(out_len, 2);
let got_keys: Vec<String> = (0..out_len)
.map(|i| c_ptr_to_string(unsafe { *out_keys.add(i) }).unwrap())
.collect();
let got_vals: Vec<i32> = (0..out_len)
.map(|i| unsafe { *out_values.add(i) })
.collect();
assert_eq!(got_keys, vec!["a".to_string(), "b".to_string()]);
assert_eq!(got_vals, vec![4, 2]);
for i in 0..out_len {
free_string(unsafe { *out_keys.add(i) });
}
unsafe {
drop(Vec::from_raw_parts(out_keys, out_len, out_len));
drop(Vec::from_raw_parts(out_values, out_len, out_len));
}
free_string(kb);
free_string(ka);
}
#[test]
fn map_param_scalar_return() {
let mut err = ok_err();
let ka = string_to_c_ptr("a");
let kb = string_to_c_ptr("b");
let keys: [*const c_char; 2] = [ka, kb];
let values: [i32; 2] = [10, 32];
let total = maps::weaveffi_maps_total(keys.as_ptr(), values.as_ptr(), 2, &mut err);
assert_eq!(err.code, 0);
assert_eq!(total, 42);
free_string(ka);
free_string(kb);
}
#[test]
fn builder_round_trip_and_required_field() {
let mut err = ok_err();
let b = build::weaveffi_build_Widget_Builder_new();
assert!(!b.is_null());
let name = string_to_c_ptr("bolt");
build::weaveffi_build_Widget_Builder_set_name(b, name);
build::weaveffi_build_Widget_Builder_set_qty(b, 7);
let note = string_to_c_ptr("aisle 4");
build::weaveffi_build_Widget_Builder_set_note(b, note);
free_string(name);
free_string(note);
let w = build::weaveffi_build_Widget_Builder_build(b, &mut err);
assert_eq!(err.code, 0);
assert!(!w.is_null());
assert_eq!(build::weaveffi_build_Widget_get_qty(w), 7);
let got_name = build::weaveffi_build_Widget_get_name(w);
assert_eq!(c_ptr_to_string(got_name).unwrap(), "bolt");
free_string(got_name);
let got_note = build::weaveffi_build_Widget_get_note(w);
assert_eq!(c_ptr_to_string(got_note).unwrap(), "aisle 4");
free_string(got_note);
build::weaveffi_build_Widget_destroy(w);
build::weaveffi_build_Widget_Builder_destroy(b);
let b2 = build::weaveffi_build_Widget_Builder_new();
build::weaveffi_build_Widget_Builder_set_qty(b2, 1);
let w2 = build::weaveffi_build_Widget_Builder_build(b2, &mut err);
assert!(w2.is_null());
assert_ne!(err.code, 0);
assert!(c_ptr_to_string(err.message).unwrap().contains("name"));
abi::error_clear(&mut err);
build::weaveffi_build_Widget_Builder_destroy(b2);
}
#[test]
fn rich_enum_tag_constructors_and_getters() {
let mut err = ok_err();
let empty = geom::weaveffi_geom_Shape_Empty_new(&mut err);
assert_eq!(err.code, 0);
assert_eq!(geom::weaveffi_geom_Shape_tag(empty), 0);
let circle = geom::weaveffi_geom_Shape_Circle_new(2.5, &mut err);
assert_eq!(geom::weaveffi_geom_Shape_tag(circle), 1);
assert!((geom::weaveffi_geom_Shape_Circle_get_radius(circle) - 2.5).abs() < 1e-9);
assert_eq!(geom::weaveffi_geom_Shape_Labeled_get_count(circle), 0);
let label = string_to_c_ptr("hex");
let labeled = geom::weaveffi_geom_Shape_Labeled_new(label, 6, &mut err);
free_string(label);
assert_eq!(geom::weaveffi_geom_Shape_tag(labeled), 2);
let got = geom::weaveffi_geom_Shape_Labeled_get_label(labeled);
assert_eq!(c_ptr_to_string(got).unwrap(), "hex");
free_string(got);
assert_eq!(geom::weaveffi_geom_Shape_Labeled_get_count(labeled), 6);
let d = geom::weaveffi_geom_describe(circle, &mut err);
assert_eq!(c_ptr_to_string(d).unwrap(), "circle(2.5)");
free_string(d);
geom::weaveffi_geom_Shape_destroy(empty);
geom::weaveffi_geom_Shape_destroy(circle);
geom::weaveffi_geom_Shape_destroy(labeled);
}
#[test]
fn iterator_string_elements() {
let mut err = ok_err();
let iter = stream::weaveffi_stream_greetings(3, &mut err);
assert_eq!(err.code, 0);
assert!(!iter.is_null());
let mut got = Vec::new();
loop {
let mut item: *const c_char = std::ptr::null();
let has = stream::weaveffi_stream_GreetingsIterator_next(iter, &mut item, &mut err);
assert_eq!(err.code, 0);
if has == 0 {
break;
}
got.push(c_ptr_to_string(item).unwrap());
free_string(item);
}
stream::weaveffi_stream_GreetingsIterator_destroy(iter);
assert_eq!(got, vec!["hi 0", "hi 1", "hi 2"]);
}
#[test]
fn iterator_scalar_elements() {
let mut err = ok_err();
let iter = stream::weaveffi_stream_squares(4, &mut err);
let mut got = Vec::new();
loop {
let mut item: i32 = 0;
if stream::weaveffi_stream_SquaresIterator_next(iter, &mut item, &mut err) == 0 {
break;
}
got.push(item);
}
stream::weaveffi_stream_SquaresIterator_destroy(iter);
assert_eq!(got, vec![0, 1, 4, 9]);
}
#[test]
fn listener_register_emit_unregister() {
use std::os::raw::c_void;
use std::sync::atomic::{AtomicI64, Ordering};
static SUM: AtomicI64 = AtomicI64::new(0);
extern "C" fn on_msg(text: *const c_char, weight: i32, ctx: *mut c_void) {
assert!(!text.is_null());
let counter = unsafe { &*(ctx as *const AtomicI64) };
counter.fetch_add(weight as i64, Ordering::Relaxed);
}
SUM.store(0, Ordering::Relaxed);
let id =
bus::weaveffi_bus_register_subscribers(on_msg, &SUM as *const AtomicI64 as *mut c_void);
assert!(id > 0);
let mut err = ok_err();
let text = string_to_c_ptr("hi");
bus::weaveffi_bus_publish(text, 3, &mut err);
bus::weaveffi_bus_publish(text, 5, &mut err);
assert_eq!(err.code, 0);
assert_eq!(SUM.load(Ordering::Relaxed), 8);
bus::weaveffi_bus_unregister_subscribers(id);
bus::weaveffi_bus_publish(text, 100, &mut err);
assert_eq!(SUM.load(Ordering::Relaxed), 8);
free_string(text);
}
#[test]
fn async_struct_result_completes_via_callback() {
use std::os::raw::c_void;
use std::sync::mpsc;
use std::time::Duration;
type Msg = (bool, i64, String);
extern "C" fn cb(ctx: *mut c_void, err: *mut weaveffi_error, result: *mut tasks::TaskResult) {
let tx = unsafe { &*(ctx as *const mpsc::Sender<Msg>) };
let had_err = !err.is_null() && unsafe { (*err).code } != 0;
let payload = if result.is_null() {
(had_err, 0, String::new())
} else {
let r = unsafe { &*result };
(had_err, r.id, r.value.clone())
};
tx.send(payload).unwrap();
if !result.is_null() {
tasks::weaveffi_tasks_TaskResult_destroy(result);
}
}
let (tx, rx) = mpsc::channel::<Msg>();
let tx_ptr = Box::into_raw(Box::new(tx));
let name = string_to_c_ptr("alpha");
tasks::weaveffi_tasks_run_task_async(name, cb, tx_ptr as *mut c_void);
let (had_err, id, value) = rx.recv_timeout(Duration::from_secs(5)).unwrap();
free_string(name);
unsafe { drop(Box::from_raw(tx_ptr)) };
assert!(!had_err);
assert_eq!(id, 7);
assert_eq!(value, "done: alpha");
}
#[test]
fn async_result_ok_and_err_paths() {
use std::os::raw::c_void;
use std::sync::mpsc;
use std::time::Duration;
type Msg = (bool, i32);
extern "C" fn cb(ctx: *mut c_void, err: *mut weaveffi_error, result: i32) {
let tx = unsafe { &*(ctx as *const mpsc::Sender<Msg>) };
let had_err = !err.is_null() && unsafe { (*err).code } != 0;
tx.send((had_err, result)).unwrap();
}
let (tx, rx) = mpsc::channel::<Msg>();
let tx_ptr = Box::into_raw(Box::new(tx));
tasks::weaveffi_tasks_checked_add_async(2, 3, cb, tx_ptr as *mut c_void);
let (had_err, result) = rx.recv_timeout(Duration::from_secs(5)).unwrap();
assert!(!had_err);
assert_eq!(result, 5);
tasks::weaveffi_tasks_checked_add_async(i32::MAX, 1, cb, tx_ptr as *mut c_void);
let (had_err, result) = rx.recv_timeout(Duration::from_secs(5)).unwrap();
assert!(had_err);
assert_eq!(result, 0);
unsafe { drop(Box::from_raw(tx_ptr)) };
}
#[weaveffi::module]
pub mod outer {
pub struct Session {
pub id: i64,
}
#[weaveffi::export]
pub fn open_session(id: i64) -> *mut Session {
Box::into_raw(Box::new(Session { id }))
}
#[weaveffi::module]
pub mod inner {
#[weaveffi::record]
#[derive(Clone)]
pub struct Report {
pub score: i64,
}
#[weaveffi::export]
pub fn summarize(session: *const super::Session) -> Report {
let id = unsafe { (*session).id };
Report { score: id * 10 }
}
}
}
#[test]
fn nested_module_symbols_and_parent_type_reference() {
let mut err = ok_err();
let session = outer::weaveffi_outer_open_session(7, &mut err);
assert_eq!(err.code, 0);
assert!(!session.is_null());
let report = outer::inner::weaveffi_outer_inner_summarize(session, &mut err);
assert_eq!(err.code, 0);
assert!(!report.is_null());
assert_eq!(
outer::inner::weaveffi_outer_inner_Report_get_score(report),
70
);
outer::inner::weaveffi_outer_inner_Report_destroy(report);
unsafe { drop(Box::from_raw(session)) };
}
#[weaveffi::module]
pub mod vault {
use weaveffi::ErrorReport;
#[weaveffi::error]
#[derive(Debug)]
pub enum VaultError {
NotFound = 2001,
Sealed = 2002,
}
pub enum VaultFailure {
NotFound,
Sealed(String),
}
impl ErrorReport for VaultFailure {
fn code(&self) -> i32 {
match self {
VaultFailure::NotFound => 2001,
VaultFailure::Sealed(_) => 2002,
}
}
fn message(&self) -> String {
match self {
VaultFailure::NotFound => "entry not found".to_string(),
VaultFailure::Sealed(reason) => format!("vault sealed: {reason}"),
}
}
}
#[weaveffi::export]
pub fn fetch(key: i64) -> Result<i64, VaultFailure> {
match key {
0 => Err(VaultFailure::NotFound),
n if n < 0 => Err(VaultFailure::Sealed("negative key".to_string())),
n => Ok(n * 2),
}
}
}
#[test]
fn fallible_with_domain_error_codes() {
let mut err = ok_err();
assert_eq!(vault::weaveffi_vault_fetch(21, &mut err), 42);
assert_eq!(err.code, 0);
let r = vault::weaveffi_vault_fetch(0, &mut err);
assert_eq!(r, 0, "error path returns the zero sentinel");
assert_eq!(err.code, 2001);
assert_eq!(c_ptr_to_string(err.message).unwrap(), "entry not found");
abi::error_clear(&mut err);
let r = vault::weaveffi_vault_fetch(-1, &mut err);
assert_eq!(r, 0);
assert_eq!(err.code, 2002);
assert_eq!(
c_ptr_to_string(err.message).unwrap(),
"vault sealed: negative key"
);
abi::error_clear(&mut err);
}
#[weaveffi::module]
pub mod legacy {
#[weaveffi::export]
pub fn add_one(value: i64) -> i64 {
value + 1
}
#[deprecated(note = "use add_one")]
#[weaveffi::export]
pub fn bump(value: i64) -> i64 {
value + 1
}
}
#[weaveffi::module]
pub mod counters {
use std::sync::atomic::{AtomicI64, Ordering};
#[weaveffi::error]
#[derive(Debug)]
pub enum CounterError {
OutOfRange = 1,
}
#[weaveffi::interface]
pub struct Counter {
value: AtomicI64,
step: i64,
}
impl Counter {
pub fn new(start: i64) -> Self {
Self {
value: AtomicI64::new(start),
step: 1,
}
}
pub fn with_step(start: i64, step: i64) -> Result<Counter, CounterError> {
if step <= 0 {
return Err(CounterError::OutOfRange);
}
Ok(Counter {
value: AtomicI64::new(start),
step,
})
}
pub fn increment(&self) -> i64 {
self.value.fetch_add(self.step, Ordering::Relaxed) + self.step
}
pub fn value(&self) -> i64 {
self.value.load(Ordering::Relaxed)
}
pub fn label(&self, prefix: &str) -> String {
format!("{prefix}{}", self.value())
}
pub fn snapshot(&self) -> Counter {
Counter {
value: AtomicI64::new(self.value()),
step: self.step,
}
}
pub fn explode(&self) {
panic!("counter exploded");
}
pub fn default_start() -> i64 {
0
}
#[allow(dead_code)]
fn internal(&self) -> i64 {
-1
}
}
#[weaveffi::export]
pub fn read_twice(counter: &Counter) -> i64 {
counter.value() * 2
}
}
#[test]
fn interface_constructor_methods_destroy() {
let mut err = ok_err();
let c = counters::weaveffi_counters_Counter_new(10, &mut err);
assert_eq!(err.code, 0);
assert!(!c.is_null());
assert_eq!(
counters::weaveffi_counters_Counter_increment(c, &mut err),
11
);
assert_eq!(
counters::weaveffi_counters_Counter_increment(c, &mut err),
12
);
assert_eq!(counters::weaveffi_counters_Counter_value(c, &mut err), 12);
assert_eq!(err.code, 0);
let prefix = string_to_c_ptr("n=");
let label = counters::weaveffi_counters_Counter_label(c, prefix, &mut err);
assert_eq!(c_ptr_to_string(label).unwrap(), "n=12");
free_string(label);
free_string(prefix);
counters::weaveffi_counters_Counter_destroy(c);
}
#[test]
fn interface_fallible_constructor() {
let mut err = ok_err();
let ok = counters::weaveffi_counters_Counter_with_step(0, 5, &mut err);
assert_eq!(err.code, 0);
assert!(!ok.is_null());
assert_eq!(
counters::weaveffi_counters_Counter_increment(ok, &mut err),
5
);
counters::weaveffi_counters_Counter_destroy(ok);
let bad = counters::weaveffi_counters_Counter_with_step(0, 0, &mut err);
assert!(bad.is_null());
assert_eq!(err.code, 1, "domain code from the #[weaveffi::error] enum");
assert_eq!(
c_ptr_to_string(err.message).unwrap(),
"start value out of range"
);
abi::error_clear(&mut err);
}
#[test]
fn interface_returning_method_and_static() {
let mut err = ok_err();
assert_eq!(
counters::weaveffi_counters_Counter_default_start(&mut err),
0
);
let c = counters::weaveffi_counters_Counter_new(3, &mut err);
let snap = counters::weaveffi_counters_Counter_snapshot(c, &mut err);
assert!(!snap.is_null());
counters::weaveffi_counters_Counter_increment(c, &mut err);
assert_eq!(counters::weaveffi_counters_Counter_value(c, &mut err), 4);
assert_eq!(
counters::weaveffi_counters_Counter_value(snap, &mut err),
3,
"the snapshot is an independent object"
);
counters::weaveffi_counters_Counter_destroy(snap);
counters::weaveffi_counters_Counter_destroy(c);
}
#[test]
fn interface_as_free_function_parameter() {
let mut err = ok_err();
let c = counters::weaveffi_counters_Counter_new(21, &mut err);
assert_eq!(counters::weaveffi_counters_read_twice(c, &mut err), 42);
counters::weaveffi_counters_Counter_destroy(c);
}
#[test]
fn interface_null_self_reports_error() {
let mut err = ok_err();
let r = counters::weaveffi_counters_Counter_value(std::ptr::null(), &mut err);
assert_eq!(r, 0);
assert_ne!(err.code, 0);
abi::error_clear(&mut err);
}
#[test]
fn producer_panic_reports_panic_code() {
let mut err = ok_err();
let c = counters::weaveffi_counters_Counter_new(0, &mut err);
counters::weaveffi_counters_Counter_explode(c, &mut err);
assert_eq!(err.code, abi::PANIC_ERROR_CODE);
assert!(c_ptr_to_string(err.message)
.unwrap()
.contains("counter exploded"));
abi::error_clear(&mut err);
assert_eq!(counters::weaveffi_counters_Counter_value(c, &mut err), 0);
assert_eq!(err.code, 0);
counters::weaveffi_counters_Counter_destroy(c);
}
#[test]
fn deprecated_export_thunk_compiles_and_runs() {
let mut err = ok_err();
assert_eq!(legacy::weaveffi_legacy_add_one(41, &mut err), 42);
assert_eq!(err.code, 0);
#[allow(deprecated)]
let bumped = legacy::weaveffi_legacy_bump(41, &mut err);
assert_eq!(bumped, 42);
assert_eq!(err.code, 0);
}