use phlow::{AnySendObject, PhlowVTable, ViewInstance, vtable_of_any};
use std::any::{Any, type_name};
use value_box::{BorrowedPtr, BoxerError, OwnedPtr, Result, ReturnBoxerResult};
phlow_extensions::link!();
#[macro_export]
macro_rules! phlow_vtable_ffi {
(@build $ty:ty) => {{
::phlow::vtable_of_type::<$ty>()
}};
() => {};
(
$(#[$meta:meta])*
$vis:vis fn $name:ident($ty:ty);
$($rest:tt)*
) => {
$(#[$meta])*
#[unsafe(no_mangle)]
$vis extern "C" fn $name() -> ::value_box::OwnedPtr<::phlow::PhlowVTable> {
::value_box::OwnedPtr::new($crate::phlow_vtable_ffi!(@build $ty))
}
$crate::phlow_vtable_ffi! {
$($rest)*
}
};
(
$(#[$meta:meta])*
$vis:vis fn $name:ident($ty:ty)
) => {
$(#[$meta])*
#[unsafe(no_mangle)]
$vis extern "C" fn $name() -> ::value_box::OwnedPtr<::phlow::PhlowVTable> {
::value_box::OwnedPtr::new($crate::phlow_vtable_ffi!(@build $ty))
}
};
(
$(#[$meta:meta])*
$vis:vis fn $name:ident($ty:ty);
) => {
$(#[$meta])*
#[unsafe(no_mangle)]
$vis extern "C" fn $name() -> ::value_box::OwnedPtr<::phlow::PhlowVTable> {
::value_box::OwnedPtr::new($crate::phlow_vtable_ffi!(@build $ty))
}
};
}
pub use phlow_info_view::*;
pub use phlow_list_view::*;
pub use phlow_text_view::*;
pub use phlow_view::*;
pub use phlow_view_instance::*;
pub use phlow_vtable::*;
mod phlow_info_view;
mod phlow_list_view;
mod phlow_text_view;
mod phlow_view;
mod phlow_view_instance;
mod phlow_vtable;
#[unsafe(no_mangle)]
pub fn phlow_test() -> bool {
true
}
#[unsafe(no_mangle)]
pub fn phlow_get_vtable_of_any(object: BorrowedPtr<AnySendObject>) -> OwnedPtr<PhlowVTable> {
object
.with_ref_ok(|object| OwnedPtr::new(vtable_of_any(object.as_any())))
.or_log(OwnedPtr::null())
}
crate::phlow_vtable_ffi! {
pub fn phlow_get_vtable_of_phlow_vtable(phlow::PhlowVTable);
}
pub(crate) fn with_view_instance<T: ViewInstance + 'static, R: Any>(
view_instance: BorrowedPtr<Box<dyn ViewInstance>>,
op: impl FnOnce(&T) -> Result<R>,
) -> Result<R> {
view_instance.with_ref(|view_instance| {
view_instance
.as_any()
.downcast_ref::<T>()
.ok_or_else(|| {
BoxerError::AnyError(
format!("Expected view instance of type {}", type_name::<T>()).into(),
)
})
.and_then(op)
})
}
#[cfg(test)]
mod tests {
use string_box::StringBox;
use value_box::{BorrowedPtr, OwnedPtr};
crate::phlow_vtable_ffi! {
fn phlow_test_get_u32_vtable(u32);
fn phlow_test_get_string_vtable(String);
}
#[test]
fn generated_get_views_ffi_returns_a_vtable() {
phlow_test_get_u32_vtable()
.with_value_ok(|vtable| {
let object = 42_u32;
let any_object = phlow::AnySendObject::new(42_u32);
let mut string = StringBox::default();
let mut any_string = StringBox::default();
crate::phlow_vtable_to_string_for_typed_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&object).erase(),
BorrowedPtr::from_mut(&mut string),
);
assert_eq!(string.as_str(), "42");
crate::phlow_vtable_to_string_for_any_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&any_object),
BorrowedPtr::from_mut(&mut any_string),
);
assert_eq!(any_string.as_str(), "42");
let views = crate::phlow_vtable_get_views_for_typed_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&object).erase(),
);
assert_eq!(views.with_value_ok(|views| views.len()).unwrap(), 1);
let any_views = crate::phlow_vtable_get_views_for_any_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&any_object),
);
assert_eq!(any_views.with_value_ok(|views| views.len()).unwrap(), 1);
})
.unwrap();
}
#[test]
fn generated_get_views_ffi_supports_multiple_definitions() {
phlow_test_get_string_vtable()
.with_value_ok(|vtable| {
let object = String::from("hello");
let any_object = phlow::AnySendObject::new(String::from("hello"));
let mut string = StringBox::default();
let mut any_string = StringBox::default();
crate::phlow_vtable_to_string_for_typed_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&object).erase(),
BorrowedPtr::from_mut(&mut string),
);
assert_eq!(string.as_str(), "hello");
crate::phlow_vtable_to_string_for_any_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&any_object),
BorrowedPtr::from_mut(&mut any_string),
);
assert_eq!(any_string.as_str(), "hello");
})
.unwrap();
}
#[test]
fn phlow_vtable_ffi_exposes_string_and_views() {
phlow_test_get_u32_vtable()
.with_value_ok(|vtable| {
let object = 42_u32;
let any_object = phlow::AnySendObject::new(42_u32);
let mut type_name = StringBox::default();
let mut string = StringBox::default();
let mut any_string = StringBox::default();
assert!(crate::phlow_vtable_supports_type_name(
BorrowedPtr::from_ref(&vtable)
));
crate::phlow_vtable_get_type_name(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_mut(&mut type_name),
);
assert_eq!(type_name.as_str(), std::any::type_name::<u32>());
assert!(crate::phlow_vtable_supports_to_string(
BorrowedPtr::from_ref(&vtable)
));
crate::phlow_vtable_to_string_for_typed_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&object).erase(),
BorrowedPtr::from_mut(&mut string),
);
assert_eq!(string.as_str(), "42");
crate::phlow_vtable_to_string_for_any_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&any_object),
BorrowedPtr::from_mut(&mut any_string),
);
assert_eq!(any_string.as_str(), "42");
let views = crate::phlow_vtable_get_views_for_typed_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&object).erase(),
);
assert_eq!(views.with_value_ok(|views| views.len()).unwrap(), 1);
let any_views = crate::phlow_vtable_get_views_for_any_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&any_object),
);
assert_eq!(any_views.with_value_ok(|views| views.len()).unwrap(), 1);
})
.unwrap();
}
#[test]
fn phlow_vtable_ffi_reports_missing_type_name_and_to_string() {
let vtable = phlow::PhlowVTable {
type_name_fn: None,
to_string_fn: None,
as_view_fn: None,
defining_methods_fn: None,
};
let object = 42_u32;
let any_object = phlow::AnySendObject::new(42_u32);
let mut type_name = StringBox::default();
let mut string = StringBox::default();
let mut any_string = StringBox::default();
assert!(!crate::phlow_vtable_supports_type_name(
BorrowedPtr::from_ref(&vtable)
));
assert!(!crate::phlow_vtable_supports_to_string(
BorrowedPtr::from_ref(&vtable)
));
crate::phlow_vtable_get_type_name(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_mut(&mut type_name),
);
assert_eq!(type_name.as_str(), "");
crate::phlow_vtable_to_string_for_typed_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&object).erase(),
BorrowedPtr::from_mut(&mut string),
);
assert_eq!(string.as_str(), "");
crate::phlow_vtable_to_string_for_any_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&any_object),
BorrowedPtr::from_mut(&mut any_string),
);
assert_eq!(any_string.as_str(), "");
}
#[test]
fn phlow_get_vtable_of_phlow_vtable_exposes_extensions() {
let object = phlow::vtable_of_type::<u32>();
crate::phlow_get_vtable_of_phlow_vtable()
.with_value_ok(|vtable| {
let mut type_name = StringBox::default();
assert!(crate::phlow_vtable_supports_type_name(
BorrowedPtr::from_ref(&vtable)
));
crate::phlow_vtable_get_type_name(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_mut(&mut type_name),
);
assert_eq!(
type_name.as_str(),
std::any::type_name::<phlow::PhlowVTable>()
);
let views = crate::phlow_vtable_get_views_for_typed_object(
BorrowedPtr::from_ref(&vtable),
BorrowedPtr::from_ref(&object).erase(),
);
assert_eq!(views.with_value_ok(|views| views.len()).unwrap(), 1);
})
.unwrap();
}
#[test]
fn phlow_any_send_object_drop_releases_box() {
crate::phlow_any_send_object_drop(OwnedPtr::new(phlow::AnySendObject::new(42_u32)));
}
}