Skip to main content

flowjs_rs/
impls.rs

1//! Built-in Flow trait implementations for Rust standard types.
2
3use crate::{flow_type, Config, Dummy, Flow, TypeVisitor};
4
5// Wrapper types: delegate all methods to the inner type T
6macro_rules! impl_wrapper {
7    ($($t:tt)*) => {
8        $($t)* {
9            type WithoutGenerics = Self;
10            type OptionInnerType = Self;
11            fn name(cfg: &Config) -> String { <T as crate::Flow>::name(cfg) }
12            fn inline(cfg: &Config) -> String { <T as crate::Flow>::inline(cfg) }
13            fn inline_flattened(cfg: &Config) -> String { <T as crate::Flow>::inline_flattened(cfg) }
14            fn visit_dependencies(v: &mut impl TypeVisitor)
15            where
16                Self: 'static,
17            {
18                <T as crate::Flow>::visit_dependencies(v);
19            }
20            fn visit_generics(v: &mut impl TypeVisitor)
21            where
22                Self: 'static,
23            {
24                <T as crate::Flow>::visit_generics(v);
25                v.visit::<T>();
26            }
27            fn decl(_: &Config) -> String { panic!("wrapper type cannot be declared") }
28            fn decl_concrete(_: &Config) -> String { panic!("wrapper type cannot be declared") }
29        }
30    };
31}
32
33// Shadow types: delegate to a different impl
34macro_rules! impl_shadow {
35    (as $s:ty: $($impl:tt)*) => {
36        $($impl)* {
37            type WithoutGenerics = <$s as crate::Flow>::WithoutGenerics;
38            type OptionInnerType = <$s as crate::Flow>::OptionInnerType;
39            fn ident(cfg: &Config) -> String { <$s as crate::Flow>::ident(cfg) }
40            fn name(cfg: &Config) -> String { <$s as crate::Flow>::name(cfg) }
41            fn inline(cfg: &Config) -> String { <$s as crate::Flow>::inline(cfg) }
42            fn inline_flattened(cfg: &Config) -> String { <$s as crate::Flow>::inline_flattened(cfg) }
43            fn visit_dependencies(v: &mut impl crate::TypeVisitor)
44            where
45                Self: 'static,
46            {
47                <$s as crate::Flow>::visit_dependencies(v);
48            }
49            fn visit_generics(v: &mut impl crate::TypeVisitor)
50            where
51                Self: 'static,
52            {
53                <$s as crate::Flow>::visit_generics(v);
54            }
55            fn decl(cfg: &Config) -> String { <$s as crate::Flow>::decl(cfg) }
56            fn decl_concrete(cfg: &Config) -> String { <$s as crate::Flow>::decl_concrete(cfg) }
57            fn output_path() -> Option<std::path::PathBuf> { <$s as crate::Flow>::output_path() }
58        }
59    };
60}
61
62macro_rules! impl_flow_primitive {
63    ($rust_ty:ty, $flow_ty:expr) => {
64        impl Flow for $rust_ty {
65            type WithoutGenerics = Self;
66            type OptionInnerType = Self;
67            fn name(_: &Config) -> String {
68                $flow_ty.to_owned()
69            }
70            fn inline(cfg: &Config) -> String {
71                <Self as crate::Flow>::name(cfg)
72            }
73        }
74    };
75}
76
77// Primitives
78impl_flow_primitive!(bool, flow_type::BOOLEAN);
79impl_flow_primitive!(i8, flow_type::NUMBER);
80impl_flow_primitive!(i16, flow_type::NUMBER);
81impl_flow_primitive!(i32, flow_type::NUMBER);
82impl_flow_primitive!(u8, flow_type::NUMBER);
83impl_flow_primitive!(u16, flow_type::NUMBER);
84impl_flow_primitive!(u32, flow_type::NUMBER);
85impl_flow_primitive!(i64, flow_type::NUMBER);
86impl_flow_primitive!(u64, flow_type::NUMBER);
87impl_flow_primitive!(i128, flow_type::NUMBER);
88impl_flow_primitive!(u128, flow_type::NUMBER);
89impl_flow_primitive!(f32, flow_type::NUMBER);
90impl_flow_primitive!(f64, flow_type::NUMBER);
91impl_flow_primitive!(char, flow_type::STRING);
92impl_flow_primitive!(String, flow_type::STRING);
93impl_flow_primitive!(str, flow_type::STRING);
94impl_flow_primitive!((), flow_type::VOID);
95
96// Option<T> → ?T
97impl<T: Flow> Flow for Option<T> {
98    type WithoutGenerics = Self;
99    type OptionInnerType = T;
100    const IS_OPTION: bool = true;
101
102    fn name(cfg: &Config) -> String {
103        format!("?{}", T::name(cfg))
104    }
105    fn inline(cfg: &Config) -> String {
106        format!("?{}", T::inline(cfg))
107    }
108    fn visit_dependencies(v: &mut impl TypeVisitor)
109    where
110        Self: 'static,
111    {
112        <T as crate::Flow>::visit_dependencies(v);
113    }
114    fn visit_generics(v: &mut impl TypeVisitor)
115    where
116        Self: 'static,
117    {
118        <T as crate::Flow>::visit_generics(v);
119        v.visit::<T>();
120    }
121}
122
123// Vec<T> → $ReadOnlyArray<T>
124impl<T: Flow> Flow for Vec<T> {
125    type WithoutGenerics = Vec<Dummy>;
126    type OptionInnerType = Self;
127
128    fn ident(_: &Config) -> String {
129        flow_type::READ_ONLY_ARRAY.to_owned()
130    }
131    fn name(cfg: &Config) -> String {
132        format!("{}<{}>", flow_type::READ_ONLY_ARRAY, T::name(cfg))
133    }
134    fn inline(cfg: &Config) -> String {
135        format!("{}<{}>", flow_type::READ_ONLY_ARRAY, T::inline(cfg))
136    }
137    fn visit_dependencies(v: &mut impl TypeVisitor)
138    where
139        Self: 'static,
140    {
141        <T as crate::Flow>::visit_dependencies(v);
142    }
143    fn visit_generics(v: &mut impl TypeVisitor)
144    where
145        Self: 'static,
146    {
147        <T as crate::Flow>::visit_generics(v);
148        v.visit::<T>();
149    }
150}
151
152// &[T] → $ReadOnlyArray<T>
153impl_shadow!(as Vec<T>: impl<T: Flow> Flow for [T]);
154
155// Box<T> → T
156impl_wrapper!(impl<T: Flow + ?Sized> Flow for Box<T>);
157
158// &T → T
159impl_wrapper!(impl<'a, T: Flow + ?Sized> Flow for &'a T);
160
161// std::collections::HashMap → { [key: K]: V }
162impl<K: Flow, V: Flow> Flow for std::collections::HashMap<K, V> {
163    type WithoutGenerics = std::collections::HashMap<Dummy, Dummy>;
164    type OptionInnerType = Self;
165
166    fn ident(_: &Config) -> String {
167        panic!()
168    }
169    fn name(cfg: &Config) -> String {
170        format!("{{ [key: {}]: {} }}", K::name(cfg), V::name(cfg))
171    }
172    fn inline(cfg: &Config) -> String {
173        format!("{{ [key: {}]: {} }}", K::inline(cfg), V::inline(cfg))
174    }
175    fn inline_flattened(cfg: &Config) -> String {
176        format!("({})", Self::inline(cfg))
177    }
178    fn visit_dependencies(v: &mut impl TypeVisitor)
179    where
180        Self: 'static,
181    {
182        K::visit_dependencies(v);
183        V::visit_dependencies(v);
184    }
185    fn visit_generics(v: &mut impl TypeVisitor)
186    where
187        Self: 'static,
188    {
189        K::visit_generics(v);
190        v.visit::<K>();
191        V::visit_generics(v);
192        v.visit::<V>();
193    }
194}
195
196// std::collections::BTreeMap → { [key: K]: V }
197impl_shadow!(as std::collections::HashMap<K, V>: impl<K: Flow, V: Flow> Flow for std::collections::BTreeMap<K, V>);
198
199// HashSet → $ReadOnlyArray
200impl_shadow!(as Vec<T>: impl<T: Flow> Flow for std::collections::HashSet<T>);
201
202// BTreeSet → $ReadOnlyArray
203impl_shadow!(as Vec<T>: impl<T: Flow> Flow for std::collections::BTreeSet<T>);
204
205// VecDeque → $ReadOnlyArray
206impl_shadow!(as Vec<T>: impl<T: Flow> Flow for std::collections::VecDeque<T>);
207
208// LinkedList → $ReadOnlyArray
209impl_shadow!(as Vec<T>: impl<T: Flow> Flow for std::collections::LinkedList<T>);
210
211// Tuples
212macro_rules! impl_flow_tuples {
213    ( impl $($i:ident),* ) => {
214        impl<$($i: Flow),*> Flow for ($($i,)*) {
215            type WithoutGenerics = (Dummy, );
216            type OptionInnerType = Self;
217            fn name(cfg: &Config) -> String {
218                let parts: Vec<String> = vec![$($i::name(cfg)),*];
219                format!("[{}]", parts.join(", "))
220            }
221            fn inline(cfg: &Config) -> String {
222                let parts: Vec<String> = vec![$($i::inline(cfg)),*];
223                format!("[{}]", parts.join(", "))
224            }
225            fn inline_flattened(cfg: &Config) -> String {
226                format!("({})", Self::inline(cfg))
227            }
228            fn decl(_: &Config) -> String {
229                panic!("tuple cannot be declared")
230            }
231            fn decl_concrete(_: &Config) -> String {
232                panic!("tuple cannot be declared")
233            }
234            fn visit_generics(v: &mut impl TypeVisitor)
235            where
236                Self: 'static
237            {
238                $(
239                    v.visit::<$i>();
240                    <$i as crate::Flow>::visit_generics(v);
241                )*
242            }
243        }
244    };
245    ( $i2:ident $(, $i:ident)* ) => {
246        impl_flow_tuples!(impl $i2 $(, $i)* );
247        impl_flow_tuples!($($i),*);
248    };
249    () => {};
250}
251
252impl_flow_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
253
254// serde_json::Value → mixed (declared as `type JsonValue = mixed` for import parity with ts-rs)
255#[cfg(feature = "serde-json-impl")]
256impl Flow for serde_json::Value {
257    type WithoutGenerics = Self;
258    type OptionInnerType = Self;
259    fn name(_: &Config) -> String {
260        "JsonValue".to_owned()
261    }
262    fn inline(_: &Config) -> String {
263        flow_type::MIXED.to_owned()
264    }
265    fn decl(_: &Config) -> String {
266        format!("type JsonValue = {};", flow_type::MIXED)
267    }
268    fn decl_concrete(_: &Config) -> String {
269        format!("type JsonValue = {};", flow_type::MIXED)
270    }
271    fn output_path() -> Option<std::path::PathBuf> {
272        Some(std::path::PathBuf::from("JsonValue.js.flow"))
273    }
274}
275
276// PathBuf / &Path → string
277impl Flow for std::path::PathBuf {
278    type WithoutGenerics = Self;
279    type OptionInnerType = Self;
280    fn name(_: &Config) -> String {
281        flow_type::STRING.to_owned()
282    }
283    fn inline(_: &Config) -> String {
284        flow_type::STRING.to_owned()
285    }
286}
287
288impl Flow for std::path::Path {
289    type WithoutGenerics = Self;
290    type OptionInnerType = Self;
291    fn name(_: &Config) -> String {
292        flow_type::STRING.to_owned()
293    }
294    fn inline(_: &Config) -> String {
295        flow_type::STRING.to_owned()
296    }
297}
298
299// Cow<'_, T> → T
300impl_wrapper!(impl<'a, T: Flow + ToOwned + ?Sized> Flow for std::borrow::Cow<'a, T>);
301
302// Result<T, E> → { ok: T } | { err: E }
303impl<T: Flow, E: Flow> Flow for Result<T, E> {
304    type WithoutGenerics = Result<Dummy, Dummy>;
305    type OptionInnerType = Self;
306
307    fn name(cfg: &Config) -> String {
308        format!(
309            "{{| +ok: {} |}} | {{| +err: {} |}}",
310            T::name(cfg),
311            E::name(cfg)
312        )
313    }
314    fn inline(cfg: &Config) -> String {
315        format!(
316            "{{| +ok: {} |}} | {{| +err: {} |}}",
317            T::inline(cfg),
318            E::inline(cfg)
319        )
320    }
321    fn visit_dependencies(v: &mut impl TypeVisitor)
322    where
323        Self: 'static,
324    {
325        <T as crate::Flow>::visit_dependencies(v);
326        <E as crate::Flow>::visit_dependencies(v);
327    }
328    fn visit_generics(v: &mut impl TypeVisitor)
329    where
330        Self: 'static,
331    {
332        <T as crate::Flow>::visit_generics(v);
333        v.visit::<T>();
334        <E as crate::Flow>::visit_generics(v);
335        v.visit::<E>();
336    }
337}
338
339// Fixed-size arrays [T; N] → $ReadOnlyArray<T> (or tuple if small)
340impl<T: Flow, const N: usize> Flow for [T; N] {
341    type WithoutGenerics = [Dummy; N];
342    type OptionInnerType = Self;
343
344    fn name(cfg: &Config) -> String {
345        if N > cfg.array_tuple_limit() {
346            return <Vec<T> as crate::Flow>::name(cfg);
347        }
348        format!(
349            "[{}]",
350            (0..N).map(|_| T::name(cfg)).collect::<Vec<_>>().join(", ")
351        )
352    }
353    fn inline(cfg: &Config) -> String {
354        if N > cfg.array_tuple_limit() {
355            return <Vec<T> as crate::Flow>::inline(cfg);
356        }
357        format!(
358            "[{}]",
359            (0..N)
360                .map(|_| T::inline(cfg))
361                .collect::<Vec<_>>()
362                .join(", ")
363        )
364    }
365    fn visit_dependencies(v: &mut impl TypeVisitor)
366    where
367        Self: 'static,
368    {
369        <T as crate::Flow>::visit_dependencies(v);
370    }
371    fn visit_generics(v: &mut impl TypeVisitor)
372    where
373        Self: 'static,
374    {
375        <T as crate::Flow>::visit_generics(v);
376        v.visit::<T>();
377    }
378}
379
380// Arc<T> → T
381impl_wrapper!(impl<T: Flow + ?Sized> Flow for std::sync::Arc<T>);
382
383// Rc<T> → T
384impl_wrapper!(impl<T: Flow + ?Sized> Flow for std::rc::Rc<T>);
385
386// Cell<T> → T
387impl_wrapper!(impl<T: Flow> Flow for std::cell::Cell<T>);
388
389// RefCell<T> → T
390impl_wrapper!(impl<T: Flow> Flow for std::cell::RefCell<T>);
391
392// Mutex<T> → T
393impl_wrapper!(impl<T: Flow> Flow for std::sync::Mutex<T>);
394
395// RwLock<T> → T
396impl_wrapper!(impl<T: Flow> Flow for std::sync::RwLock<T>);
397
398// usize / isize
399impl_flow_primitive!(usize, flow_type::NUMBER);
400impl_flow_primitive!(isize, flow_type::NUMBER);
401
402// NonZero* types → number
403impl_shadow!(as u8: impl Flow for std::num::NonZeroU8);
404impl_shadow!(as u16: impl Flow for std::num::NonZeroU16);
405impl_shadow!(as u32: impl Flow for std::num::NonZeroU32);
406impl_shadow!(as u64: impl Flow for std::num::NonZeroU64);
407impl_shadow!(as u128: impl Flow for std::num::NonZeroU128);
408impl_shadow!(as usize: impl Flow for std::num::NonZeroUsize);
409impl_shadow!(as i8: impl Flow for std::num::NonZeroI8);
410impl_shadow!(as i16: impl Flow for std::num::NonZeroI16);
411impl_shadow!(as i32: impl Flow for std::num::NonZeroI32);
412impl_shadow!(as i64: impl Flow for std::num::NonZeroI64);
413impl_shadow!(as i128: impl Flow for std::num::NonZeroI128);
414impl_shadow!(as isize: impl Flow for std::num::NonZeroIsize);
415
416// PhantomData<T> → void
417impl<T: ?Sized> Flow for std::marker::PhantomData<T> {
418    type WithoutGenerics = Self;
419    type OptionInnerType = Self;
420    fn name(_: &Config) -> String {
421        flow_type::VOID.to_owned()
422    }
423    fn inline(_: &Config) -> String {
424        flow_type::VOID.to_owned()
425    }
426}
427
428// Range<T> → { +start: T, +end: T }
429impl<T: Flow> Flow for std::ops::Range<T> {
430    type WithoutGenerics = std::ops::Range<Dummy>;
431    type OptionInnerType = Self;
432
433    fn ident(_: &Config) -> String {
434        panic!()
435    }
436    fn name(cfg: &Config) -> String {
437        format!("{{ +start: {}, +end: {} }}", T::name(cfg), T::name(cfg))
438    }
439    fn inline(cfg: &Config) -> String {
440        format!("{{ +start: {}, +end: {} }}", T::inline(cfg), T::inline(cfg))
441    }
442    fn inline_flattened(cfg: &Config) -> String {
443        format!("({})", Self::inline(cfg))
444    }
445    fn visit_dependencies(v: &mut impl TypeVisitor)
446    where
447        Self: 'static,
448    {
449        T::visit_dependencies(v);
450    }
451    fn visit_generics(v: &mut impl TypeVisitor)
452    where
453        Self: 'static,
454    {
455        T::visit_generics(v);
456        v.visit::<T>();
457    }
458}
459
460// RangeInclusive<T> → { +start: T, +end: T }
461impl_shadow!(as std::ops::Range<T>: impl<T: Flow> Flow for std::ops::RangeInclusive<T>);
462
463// Duration → { +secs: number, +nanos: number }
464impl Flow for std::time::Duration {
465    type WithoutGenerics = Self;
466    type OptionInnerType = Self;
467    fn name(_: &Config) -> String {
468        format!(
469            "{{| +secs: {}, +nanos: {} |}}",
470            flow_type::NUMBER,
471            flow_type::NUMBER
472        )
473    }
474    fn inline(cfg: &Config) -> String {
475        Self::name(cfg)
476    }
477}
478
479// SystemTime → string (ISO 8601 serialization)
480impl_flow_primitive!(std::time::SystemTime, flow_type::STRING);
481
482// Network address types → string
483impl_flow_primitive!(std::net::IpAddr, flow_type::STRING);
484impl_flow_primitive!(std::net::Ipv4Addr, flow_type::STRING);
485impl_flow_primitive!(std::net::Ipv6Addr, flow_type::STRING);
486impl_flow_primitive!(std::net::SocketAddr, flow_type::STRING);
487impl_flow_primitive!(std::net::SocketAddrV4, flow_type::STRING);
488impl_flow_primitive!(std::net::SocketAddrV6, flow_type::STRING);
489
490// chrono types → string
491#[cfg(feature = "chrono-impl")]
492impl_flow_primitive!(chrono::NaiveDate, flow_type::STRING);
493#[cfg(feature = "chrono-impl")]
494impl_flow_primitive!(chrono::NaiveTime, flow_type::STRING);
495#[cfg(feature = "chrono-impl")]
496impl_flow_primitive!(chrono::NaiveDateTime, flow_type::STRING);
497#[cfg(feature = "chrono-impl")]
498impl<T: chrono::TimeZone> Flow for chrono::DateTime<T> {
499    type WithoutGenerics = Self;
500    type OptionInnerType = Self;
501    fn name(_: &Config) -> String {
502        flow_type::STRING.to_owned()
503    }
504    fn inline(_: &Config) -> String {
505        flow_type::STRING.to_owned()
506    }
507}
508#[cfg(feature = "chrono-impl")]
509impl_flow_primitive!(chrono::Duration, flow_type::STRING);
510
511// uuid::Uuid → string
512#[cfg(feature = "uuid-impl")]
513impl_flow_primitive!(uuid::Uuid, flow_type::STRING);
514
515// url::Url → string
516#[cfg(feature = "url-impl")]
517impl_flow_primitive!(url::Url, flow_type::STRING);