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
50pub 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
58mod 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}