Skip to main content

phlow_ffi/
lib.rs

1use phlow::{AnySendObject, PhlowVTable, ViewInstance, vtable_of_any};
2use std::any::{Any, type_name};
3use value_box::{BorrowedPtr, BoxerError, OwnedPtr, Result, ReturnBoxerResult};
4
5phlow_extensions::link!();
6
7#[macro_export]
8macro_rules! phlow_vtable_ffi {
9    (@build $ty:ty) => {{
10        ::phlow::vtable_of_type::<$ty>()
11    }};
12    () => {};
13    (
14        $(#[$meta:meta])*
15        $vis:vis fn $name:ident($ty:ty);
16        $($rest:tt)*
17    ) => {
18        $(#[$meta])*
19        #[unsafe(no_mangle)]
20        $vis extern "C" fn $name() -> ::value_box::OwnedPtr<::phlow::PhlowVTable> {
21            ::value_box::OwnedPtr::new($crate::phlow_vtable_ffi!(@build $ty))
22        }
23
24        $crate::phlow_vtable_ffi! {
25            $($rest)*
26        }
27    };
28    (
29        $(#[$meta:meta])*
30        $vis:vis fn $name:ident($ty:ty)
31    ) => {
32        $(#[$meta])*
33        #[unsafe(no_mangle)]
34        $vis extern "C" fn $name() -> ::value_box::OwnedPtr<::phlow::PhlowVTable> {
35            ::value_box::OwnedPtr::new($crate::phlow_vtable_ffi!(@build $ty))
36        }
37    };
38    (
39        $(#[$meta:meta])*
40        $vis:vis fn $name:ident($ty:ty);
41    ) => {
42        $(#[$meta])*
43        #[unsafe(no_mangle)]
44        $vis extern "C" fn $name() -> ::value_box::OwnedPtr<::phlow::PhlowVTable> {
45            ::value_box::OwnedPtr::new($crate::phlow_vtable_ffi!(@build $ty))
46        }
47    };
48}
49
50//pub use phlow_columned_list_view::*;
51pub use phlow_info_view::*;
52pub use phlow_list_view::*;
53pub use phlow_text_view::*;
54pub use phlow_view::*;
55pub use phlow_view_instance::*;
56pub use phlow_vtable::*;
57
58//mod phlow_columned_list_view;
59mod phlow_info_view;
60mod phlow_list_view;
61mod phlow_text_view;
62mod phlow_view;
63mod phlow_view_instance;
64mod phlow_vtable;
65
66#[unsafe(no_mangle)]
67pub fn phlow_test() -> bool {
68    true
69}
70
71#[unsafe(no_mangle)]
72pub fn phlow_get_vtable_of_any(object: BorrowedPtr<AnySendObject>) -> OwnedPtr<PhlowVTable> {
73    object
74        .with_ref_ok(|object| OwnedPtr::new(vtable_of_any(object.as_any())))
75        .or_log(OwnedPtr::null())
76}
77
78crate::phlow_vtable_ffi! {
79    pub fn phlow_get_vtable_of_phlow_vtable(phlow::PhlowVTable);
80}
81
82pub(crate) fn with_view_instance<T: ViewInstance + 'static, R: Any>(
83    view_instance: BorrowedPtr<Box<dyn ViewInstance>>,
84    op: impl FnOnce(&T) -> Result<R>,
85) -> Result<R> {
86    view_instance.with_ref(|view_instance| {
87        view_instance
88            .as_any()
89            .downcast_ref::<T>()
90            .ok_or_else(|| {
91                BoxerError::AnyError(
92                    format!("Expected view instance of type {}", type_name::<T>()).into(),
93                )
94            })
95            .and_then(op)
96    })
97}
98
99#[cfg(test)]
100mod tests {
101    use string_box::StringBox;
102    use value_box::{BorrowedPtr, OwnedPtr};
103
104    crate::phlow_vtable_ffi! {
105        fn phlow_test_get_u32_vtable(u32);
106        fn phlow_test_get_string_vtable(String);
107    }
108
109    #[test]
110    fn generated_get_views_ffi_returns_a_vtable() {
111        phlow_test_get_u32_vtable()
112            .with_value_ok(|vtable| {
113                let object = 42_u32;
114                let any_object = phlow::AnySendObject::new(42_u32);
115                let mut string = StringBox::default();
116                let mut any_string = StringBox::default();
117
118                crate::phlow_vtable_to_string_for_typed_object(
119                    BorrowedPtr::from_ref(&vtable),
120                    BorrowedPtr::from_ref(&object).erase(),
121                    BorrowedPtr::from_mut(&mut string),
122                );
123                assert_eq!(string.as_str(), "42");
124
125                crate::phlow_vtable_to_string_for_any_object(
126                    BorrowedPtr::from_ref(&vtable),
127                    BorrowedPtr::from_ref(&any_object),
128                    BorrowedPtr::from_mut(&mut any_string),
129                );
130                assert_eq!(any_string.as_str(), "42");
131
132                let views = crate::phlow_vtable_get_views_for_typed_object(
133                    BorrowedPtr::from_ref(&vtable),
134                    BorrowedPtr::from_ref(&object).erase(),
135                );
136                assert_eq!(views.with_value_ok(|views| views.len()).unwrap(), 1);
137
138                let any_views = crate::phlow_vtable_get_views_for_any_object(
139                    BorrowedPtr::from_ref(&vtable),
140                    BorrowedPtr::from_ref(&any_object),
141                );
142                assert_eq!(any_views.with_value_ok(|views| views.len()).unwrap(), 1);
143            })
144            .unwrap();
145    }
146
147    #[test]
148    fn generated_get_views_ffi_supports_multiple_definitions() {
149        phlow_test_get_string_vtable()
150            .with_value_ok(|vtable| {
151                let object = String::from("hello");
152                let any_object = phlow::AnySendObject::new(String::from("hello"));
153                let mut string = StringBox::default();
154                let mut any_string = StringBox::default();
155
156                crate::phlow_vtable_to_string_for_typed_object(
157                    BorrowedPtr::from_ref(&vtable),
158                    BorrowedPtr::from_ref(&object).erase(),
159                    BorrowedPtr::from_mut(&mut string),
160                );
161                assert_eq!(string.as_str(), "hello");
162
163                crate::phlow_vtable_to_string_for_any_object(
164                    BorrowedPtr::from_ref(&vtable),
165                    BorrowedPtr::from_ref(&any_object),
166                    BorrowedPtr::from_mut(&mut any_string),
167                );
168                assert_eq!(any_string.as_str(), "hello");
169            })
170            .unwrap();
171    }
172
173    #[test]
174    fn phlow_vtable_ffi_exposes_string_and_views() {
175        phlow_test_get_u32_vtable()
176            .with_value_ok(|vtable| {
177                let object = 42_u32;
178                let any_object = phlow::AnySendObject::new(42_u32);
179                let mut type_name = StringBox::default();
180                let mut string = StringBox::default();
181                let mut any_string = StringBox::default();
182
183                assert!(crate::phlow_vtable_supports_type_name(
184                    BorrowedPtr::from_ref(&vtable)
185                ));
186                crate::phlow_vtable_get_type_name(
187                    BorrowedPtr::from_ref(&vtable),
188                    BorrowedPtr::from_mut(&mut type_name),
189                );
190                assert_eq!(type_name.as_str(), std::any::type_name::<u32>());
191
192                assert!(crate::phlow_vtable_supports_to_string(
193                    BorrowedPtr::from_ref(&vtable)
194                ));
195                crate::phlow_vtable_to_string_for_typed_object(
196                    BorrowedPtr::from_ref(&vtable),
197                    BorrowedPtr::from_ref(&object).erase(),
198                    BorrowedPtr::from_mut(&mut string),
199                );
200                assert_eq!(string.as_str(), "42");
201
202                crate::phlow_vtable_to_string_for_any_object(
203                    BorrowedPtr::from_ref(&vtable),
204                    BorrowedPtr::from_ref(&any_object),
205                    BorrowedPtr::from_mut(&mut any_string),
206                );
207                assert_eq!(any_string.as_str(), "42");
208
209                let views = crate::phlow_vtable_get_views_for_typed_object(
210                    BorrowedPtr::from_ref(&vtable),
211                    BorrowedPtr::from_ref(&object).erase(),
212                );
213                assert_eq!(views.with_value_ok(|views| views.len()).unwrap(), 1);
214
215                let any_views = crate::phlow_vtable_get_views_for_any_object(
216                    BorrowedPtr::from_ref(&vtable),
217                    BorrowedPtr::from_ref(&any_object),
218                );
219                assert_eq!(any_views.with_value_ok(|views| views.len()).unwrap(), 1);
220            })
221            .unwrap();
222    }
223
224    #[test]
225    fn phlow_vtable_ffi_reports_missing_type_name_and_to_string() {
226        let vtable = phlow::PhlowVTable {
227            type_name_fn: None,
228            to_string_fn: None,
229            as_view_fn: None,
230            defining_methods_fn: None,
231        };
232        let object = 42_u32;
233        let any_object = phlow::AnySendObject::new(42_u32);
234        let mut type_name = StringBox::default();
235        let mut string = StringBox::default();
236        let mut any_string = StringBox::default();
237
238        assert!(!crate::phlow_vtable_supports_type_name(
239            BorrowedPtr::from_ref(&vtable)
240        ));
241        assert!(!crate::phlow_vtable_supports_to_string(
242            BorrowedPtr::from_ref(&vtable)
243        ));
244
245        crate::phlow_vtable_get_type_name(
246            BorrowedPtr::from_ref(&vtable),
247            BorrowedPtr::from_mut(&mut type_name),
248        );
249        assert_eq!(type_name.as_str(), "");
250
251        crate::phlow_vtable_to_string_for_typed_object(
252            BorrowedPtr::from_ref(&vtable),
253            BorrowedPtr::from_ref(&object).erase(),
254            BorrowedPtr::from_mut(&mut string),
255        );
256        assert_eq!(string.as_str(), "");
257
258        crate::phlow_vtable_to_string_for_any_object(
259            BorrowedPtr::from_ref(&vtable),
260            BorrowedPtr::from_ref(&any_object),
261            BorrowedPtr::from_mut(&mut any_string),
262        );
263        assert_eq!(any_string.as_str(), "");
264    }
265
266    #[test]
267    fn phlow_get_vtable_of_phlow_vtable_exposes_extensions() {
268        let object = phlow::vtable_of_type::<u32>();
269        crate::phlow_get_vtable_of_phlow_vtable()
270            .with_value_ok(|vtable| {
271                let mut type_name = StringBox::default();
272
273                assert!(crate::phlow_vtable_supports_type_name(
274                    BorrowedPtr::from_ref(&vtable)
275                ));
276                crate::phlow_vtable_get_type_name(
277                    BorrowedPtr::from_ref(&vtable),
278                    BorrowedPtr::from_mut(&mut type_name),
279                );
280                assert_eq!(
281                    type_name.as_str(),
282                    std::any::type_name::<phlow::PhlowVTable>()
283                );
284
285                let views = crate::phlow_vtable_get_views_for_typed_object(
286                    BorrowedPtr::from_ref(&vtable),
287                    BorrowedPtr::from_ref(&object).erase(),
288                );
289                assert_eq!(views.with_value_ok(|views| views.len()).unwrap(), 1);
290            })
291            .unwrap();
292    }
293
294    #[test]
295    fn phlow_any_send_object_drop_releases_box() {
296        crate::phlow_any_send_object_drop(OwnedPtr::new(phlow::AnySendObject::new(42_u32)));
297    }
298}