phlow-ffi 3.0.0

Provides C-like api to the phlow crates
Documentation
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_columned_list_view::*;
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_columned_list_view;
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)));
    }
}