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);
85// Large integers — configurable via Config::large_int() (default: bigint).
86// These cannot use impl_flow_primitive! because the type is dynamic.
87macro_rules! impl_flow_large_int {
88    ($($rust_ty:ty),+) => {$(
89        impl Flow for $rust_ty {
90            type WithoutGenerics = Self;
91            type OptionInnerType = Self;
92            fn name(cfg: &Config) -> String {
93                cfg.large_int().to_owned()
94            }
95            fn inline(cfg: &Config) -> String {
96                cfg.large_int().to_owned()
97            }
98        }
99    )+};
100}
101
102impl_flow_large_int!(i64, u64, i128, u128);
103impl_flow_primitive!(f32, flow_type::NUMBER);
104impl_flow_primitive!(f64, flow_type::NUMBER);
105impl_flow_primitive!(char, flow_type::STRING);
106impl_flow_primitive!(String, flow_type::STRING);
107impl_flow_primitive!(str, flow_type::STRING);
108impl_flow_primitive!((), flow_type::NULL);
109
110// Option<T> → ?T
111impl<T: Flow> Flow for Option<T> {
112    type WithoutGenerics = Self;
113    type OptionInnerType = T;
114    const IS_OPTION: bool = true;
115
116    fn name(cfg: &Config) -> String {
117        format!("?{}", T::name(cfg))
118    }
119    fn inline(cfg: &Config) -> String {
120        format!("?{}", T::inline(cfg))
121    }
122    fn visit_dependencies(v: &mut impl TypeVisitor)
123    where
124        Self: 'static,
125    {
126        <T as crate::Flow>::visit_dependencies(v);
127    }
128    fn visit_generics(v: &mut impl TypeVisitor)
129    where
130        Self: 'static,
131    {
132        <T as crate::Flow>::visit_generics(v);
133        v.visit::<T>();
134    }
135}
136
137// Vec<T> → $ReadOnlyArray<T>
138impl<T: Flow> Flow for Vec<T> {
139    type WithoutGenerics = Vec<Dummy>;
140    type OptionInnerType = Self;
141
142    fn ident(_: &Config) -> String {
143        flow_type::READ_ONLY_ARRAY.to_owned()
144    }
145    fn name(cfg: &Config) -> String {
146        format!("{}<{}>", flow_type::READ_ONLY_ARRAY, T::name(cfg))
147    }
148    fn inline(cfg: &Config) -> String {
149        format!("{}<{}>", flow_type::READ_ONLY_ARRAY, T::inline(cfg))
150    }
151    fn visit_dependencies(v: &mut impl TypeVisitor)
152    where
153        Self: 'static,
154    {
155        <T as crate::Flow>::visit_dependencies(v);
156    }
157    fn visit_generics(v: &mut impl TypeVisitor)
158    where
159        Self: 'static,
160    {
161        <T as crate::Flow>::visit_generics(v);
162        v.visit::<T>();
163    }
164}
165
166// &[T] → $ReadOnlyArray<T>
167impl_shadow!(as Vec<T>: impl<T: Flow> Flow for [T]);
168
169// Box<T> → T
170impl_wrapper!(impl<T: Flow + ?Sized> Flow for Box<T>);
171
172// &T → T
173impl_wrapper!(impl<'a, T: Flow + ?Sized> Flow for &'a T);
174
175// &mut T → T
176impl_wrapper!(impl<'a, T: Flow + ?Sized> Flow for &'a mut T);
177
178// std::collections::HashMap → { [key: K]: V }
179impl<K: Flow, V: Flow> Flow for std::collections::HashMap<K, V> {
180    type WithoutGenerics = std::collections::HashMap<Dummy, Dummy>;
181    type OptionInnerType = Self;
182
183    fn ident(_: &Config) -> String {
184        panic!()
185    }
186    fn name(cfg: &Config) -> String {
187        format!("{{ [key: {}]: {} }}", K::name(cfg), V::name(cfg))
188    }
189    fn inline(cfg: &Config) -> String {
190        format!("{{ [key: {}]: {} }}", K::inline(cfg), V::inline(cfg))
191    }
192    fn inline_flattened(cfg: &Config) -> String {
193        format!("({})", Self::inline(cfg))
194    }
195    fn visit_dependencies(v: &mut impl TypeVisitor)
196    where
197        Self: 'static,
198    {
199        K::visit_dependencies(v);
200        V::visit_dependencies(v);
201    }
202    fn visit_generics(v: &mut impl TypeVisitor)
203    where
204        Self: 'static,
205    {
206        K::visit_generics(v);
207        v.visit::<K>();
208        V::visit_generics(v);
209        v.visit::<V>();
210    }
211}
212
213// std::collections::BTreeMap → { [key: K]: V }
214impl_shadow!(as std::collections::HashMap<K, V>: impl<K: Flow, V: Flow> Flow for std::collections::BTreeMap<K, V>);
215
216// HashSet → $ReadOnlyArray
217impl_shadow!(as Vec<T>: impl<T: Flow> Flow for std::collections::HashSet<T>);
218
219// BTreeSet → $ReadOnlyArray
220impl_shadow!(as Vec<T>: impl<T: Flow> Flow for std::collections::BTreeSet<T>);
221
222// VecDeque → $ReadOnlyArray
223impl_shadow!(as Vec<T>: impl<T: Flow> Flow for std::collections::VecDeque<T>);
224
225// LinkedList → $ReadOnlyArray
226impl_shadow!(as Vec<T>: impl<T: Flow> Flow for std::collections::LinkedList<T>);
227
228// Tuples
229macro_rules! impl_flow_tuples {
230    ( impl $($i:ident),* ) => {
231        impl<$($i: Flow),*> Flow for ($($i,)*) {
232            type WithoutGenerics = (Dummy, );
233            type OptionInnerType = Self;
234            fn name(cfg: &Config) -> String {
235                let parts: Vec<String> = vec![$($i::name(cfg)),*];
236                format!("[{}]", parts.join(", "))
237            }
238            fn inline(cfg: &Config) -> String {
239                let parts: Vec<String> = vec![$($i::inline(cfg)),*];
240                format!("[{}]", parts.join(", "))
241            }
242            fn inline_flattened(cfg: &Config) -> String {
243                format!("({})", Self::inline(cfg))
244            }
245            fn decl(_: &Config) -> String {
246                panic!("tuple cannot be declared")
247            }
248            fn decl_concrete(_: &Config) -> String {
249                panic!("tuple cannot be declared")
250            }
251            fn visit_generics(v: &mut impl TypeVisitor)
252            where
253                Self: 'static
254            {
255                $(
256                    v.visit::<$i>();
257                    <$i as crate::Flow>::visit_generics(v);
258                )*
259            }
260        }
261    };
262    ( $i2:ident $(, $i:ident)* ) => {
263        impl_flow_tuples!(impl $i2 $(, $i)* );
264        impl_flow_tuples!($($i),*);
265    };
266    () => {};
267}
268
269impl_flow_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
270
271// serde_json::Value → mixed (declared as `type JsonValue = mixed` for import parity with ts-rs)
272#[cfg(feature = "serde-json-impl")]
273impl Flow for serde_json::Value {
274    type WithoutGenerics = Self;
275    type OptionInnerType = Self;
276    fn name(_: &Config) -> String {
277        "JsonValue".to_owned()
278    }
279    fn inline(_: &Config) -> String {
280        flow_type::MIXED.to_owned()
281    }
282    fn decl(_: &Config) -> String {
283        format!("type JsonValue = {};", flow_type::MIXED)
284    }
285    fn decl_concrete(_: &Config) -> String {
286        format!("type JsonValue = {};", flow_type::MIXED)
287    }
288    fn output_path() -> Option<std::path::PathBuf> {
289        Some(std::path::PathBuf::from("JsonValue"))
290    }
291}
292
293// PathBuf / &Path → string
294impl Flow for std::path::PathBuf {
295    type WithoutGenerics = Self;
296    type OptionInnerType = Self;
297    fn name(_: &Config) -> String {
298        flow_type::STRING.to_owned()
299    }
300    fn inline(_: &Config) -> String {
301        flow_type::STRING.to_owned()
302    }
303}
304
305impl Flow for std::path::Path {
306    type WithoutGenerics = Self;
307    type OptionInnerType = Self;
308    fn name(_: &Config) -> String {
309        flow_type::STRING.to_owned()
310    }
311    fn inline(_: &Config) -> String {
312        flow_type::STRING.to_owned()
313    }
314}
315
316// Cow<'_, T> → T
317impl_wrapper!(impl<'a, T: Flow + ToOwned + ?Sized> Flow for std::borrow::Cow<'a, T>);
318
319// Result<T, E> → { ok: T } | { err: E }
320impl<T: Flow, E: Flow> Flow for Result<T, E> {
321    type WithoutGenerics = Result<Dummy, Dummy>;
322    type OptionInnerType = Self;
323
324    fn name(cfg: &Config) -> String {
325        format!(
326            "{{| Ok: {} |}} | {{| Err: {} |}}",
327            T::name(cfg),
328            E::name(cfg)
329        )
330    }
331    fn inline(cfg: &Config) -> String {
332        format!(
333            "{{| Ok: {} |}} | {{| Err: {} |}}",
334            T::inline(cfg),
335            E::inline(cfg)
336        )
337    }
338    fn visit_dependencies(v: &mut impl TypeVisitor)
339    where
340        Self: 'static,
341    {
342        <T as crate::Flow>::visit_dependencies(v);
343        <E as crate::Flow>::visit_dependencies(v);
344    }
345    fn visit_generics(v: &mut impl TypeVisitor)
346    where
347        Self: 'static,
348    {
349        <T as crate::Flow>::visit_generics(v);
350        v.visit::<T>();
351        <E as crate::Flow>::visit_generics(v);
352        v.visit::<E>();
353    }
354}
355
356// Fixed-size arrays [T; N] → $ReadOnlyArray<T> (or tuple if small)
357impl<T: Flow, const N: usize> Flow for [T; N] {
358    type WithoutGenerics = [Dummy; N];
359    type OptionInnerType = Self;
360
361    fn name(cfg: &Config) -> String {
362        if N > cfg.array_tuple_limit() {
363            return <Vec<T> as crate::Flow>::name(cfg);
364        }
365        format!(
366            "[{}]",
367            (0..N).map(|_| T::name(cfg)).collect::<Vec<_>>().join(", ")
368        )
369    }
370    fn inline(cfg: &Config) -> String {
371        if N > cfg.array_tuple_limit() {
372            return <Vec<T> as crate::Flow>::inline(cfg);
373        }
374        format!(
375            "[{}]",
376            (0..N)
377                .map(|_| T::inline(cfg))
378                .collect::<Vec<_>>()
379                .join(", ")
380        )
381    }
382    fn visit_dependencies(v: &mut impl TypeVisitor)
383    where
384        Self: 'static,
385    {
386        <T as crate::Flow>::visit_dependencies(v);
387    }
388    fn visit_generics(v: &mut impl TypeVisitor)
389    where
390        Self: 'static,
391    {
392        <T as crate::Flow>::visit_generics(v);
393        v.visit::<T>();
394    }
395}
396
397// Arc<T> → T
398impl_wrapper!(impl<T: Flow + ?Sized> Flow for std::sync::Arc<T>);
399
400// Rc<T> → T
401impl_wrapper!(impl<T: Flow + ?Sized> Flow for std::rc::Rc<T>);
402
403// Cell<T> → T
404impl_wrapper!(impl<T: Flow> Flow for std::cell::Cell<T>);
405
406// RefCell<T> → T
407impl_wrapper!(impl<T: Flow> Flow for std::cell::RefCell<T>);
408
409// Mutex<T> → T
410impl_wrapper!(impl<T: Flow> Flow for std::sync::Mutex<T>);
411
412// RwLock<T> → T
413impl_wrapper!(impl<T: Flow> Flow for std::sync::RwLock<T>);
414
415// usize / isize
416impl_flow_primitive!(usize, flow_type::NUMBER);
417impl_flow_primitive!(isize, flow_type::NUMBER);
418
419// Infallible → empty (bottom type, never inhabited)
420impl_flow_primitive!(std::convert::Infallible, flow_type::EMPTY);
421
422// Wrapping<T> → T (serializes as inner type)
423impl_wrapper!(impl<T: Flow> Flow for std::num::Wrapping<T>);
424
425// Saturating<T> → T (serializes as inner type)
426impl_wrapper!(impl<T: Flow> Flow for std::num::Saturating<T>);
427
428// NonZero* types → number
429impl_shadow!(as u8: impl Flow for std::num::NonZeroU8);
430impl_shadow!(as u16: impl Flow for std::num::NonZeroU16);
431impl_shadow!(as u32: impl Flow for std::num::NonZeroU32);
432impl_shadow!(as u64: impl Flow for std::num::NonZeroU64);
433impl_shadow!(as u128: impl Flow for std::num::NonZeroU128);
434impl_shadow!(as usize: impl Flow for std::num::NonZeroUsize);
435impl_shadow!(as i8: impl Flow for std::num::NonZeroI8);
436impl_shadow!(as i16: impl Flow for std::num::NonZeroI16);
437impl_shadow!(as i32: impl Flow for std::num::NonZeroI32);
438impl_shadow!(as i64: impl Flow for std::num::NonZeroI64);
439impl_shadow!(as i128: impl Flow for std::num::NonZeroI128);
440impl_shadow!(as isize: impl Flow for std::num::NonZeroIsize);
441
442// PhantomData<T> → void
443impl<T: ?Sized> Flow for std::marker::PhantomData<T> {
444    type WithoutGenerics = Self;
445    type OptionInnerType = Self;
446    fn name(_: &Config) -> String {
447        flow_type::VOID.to_owned()
448    }
449    fn inline(_: &Config) -> String {
450        flow_type::VOID.to_owned()
451    }
452}
453
454// Range<T> → { +start: T, +end: T }
455impl<T: Flow> Flow for std::ops::Range<T> {
456    type WithoutGenerics = std::ops::Range<Dummy>;
457    type OptionInnerType = Self;
458
459    fn ident(_: &Config) -> String {
460        panic!()
461    }
462    fn name(cfg: &Config) -> String {
463        format!("{{ +start: {}, +end: {} }}", T::name(cfg), T::name(cfg))
464    }
465    fn inline(cfg: &Config) -> String {
466        format!("{{ +start: {}, +end: {} }}", T::inline(cfg), T::inline(cfg))
467    }
468    fn inline_flattened(cfg: &Config) -> String {
469        format!("({})", Self::inline(cfg))
470    }
471    fn visit_dependencies(v: &mut impl TypeVisitor)
472    where
473        Self: 'static,
474    {
475        T::visit_dependencies(v);
476    }
477    fn visit_generics(v: &mut impl TypeVisitor)
478    where
479        Self: 'static,
480    {
481        T::visit_generics(v);
482        v.visit::<T>();
483    }
484}
485
486// RangeInclusive<T> → { +start: T, +end: T }
487impl_shadow!(as std::ops::Range<T>: impl<T: Flow> Flow for std::ops::RangeInclusive<T>);
488
489// Duration → { +secs: number, +nanos: number }
490impl Flow for std::time::Duration {
491    type WithoutGenerics = Self;
492    type OptionInnerType = Self;
493    fn name(_: &Config) -> String {
494        format!(
495            "{{| +secs: {}, +nanos: {} |}}",
496            flow_type::NUMBER,
497            flow_type::NUMBER
498        )
499    }
500    fn inline(cfg: &Config) -> String {
501        Self::name(cfg)
502    }
503}
504
505// SystemTime → string (ISO 8601 serialization)
506impl_flow_primitive!(std::time::SystemTime, flow_type::STRING);
507
508// Network address types → string
509impl_flow_primitive!(std::net::IpAddr, flow_type::STRING);
510impl_flow_primitive!(std::net::Ipv4Addr, flow_type::STRING);
511impl_flow_primitive!(std::net::Ipv6Addr, flow_type::STRING);
512impl_flow_primitive!(std::net::SocketAddr, flow_type::STRING);
513impl_flow_primitive!(std::net::SocketAddrV4, flow_type::STRING);
514impl_flow_primitive!(std::net::SocketAddrV6, flow_type::STRING);
515
516// chrono types → string
517#[cfg(feature = "chrono-impl")]
518impl_flow_primitive!(chrono::NaiveDate, flow_type::STRING);
519#[cfg(feature = "chrono-impl")]
520impl_flow_primitive!(chrono::NaiveTime, flow_type::STRING);
521#[cfg(feature = "chrono-impl")]
522impl_flow_primitive!(chrono::NaiveDateTime, flow_type::STRING);
523#[cfg(feature = "chrono-impl")]
524impl<T: chrono::TimeZone> Flow for chrono::DateTime<T> {
525    type WithoutGenerics = Self;
526    type OptionInnerType = Self;
527    fn name(_: &Config) -> String {
528        flow_type::STRING.to_owned()
529    }
530    fn inline(_: &Config) -> String {
531        flow_type::STRING.to_owned()
532    }
533}
534#[cfg(feature = "chrono-impl")]
535impl_flow_primitive!(chrono::Duration, flow_type::STRING);
536
537// uuid::Uuid → string
538#[cfg(feature = "uuid-impl")]
539impl_flow_primitive!(uuid::Uuid, flow_type::STRING);
540
541// url::Url → string
542#[cfg(feature = "url-impl")]
543impl_flow_primitive!(url::Url, flow_type::STRING);
544
545// fn pointers → Flow function types: (arg0: A, arg1: B) => R
546macro_rules! impl_flow_fn {
547    // Base case: fn() -> R (no arguments)
548    (impl fn() -> R) => {
549        impl<R: Flow> Flow for fn() -> R {
550            type WithoutGenerics = Self;
551            type OptionInnerType = Self;
552            fn name(cfg: &Config) -> String {
553                format!("() => {}", R::name(cfg))
554            }
555            fn inline(cfg: &Config) -> String {
556                format!("() => {}", R::inline(cfg))
557            }
558        }
559    };
560    // Recursive case: fn(A, B, ...) -> R
561    (impl fn($($i:tt: $T:ident),+) -> R) => {
562        impl<$($T: Flow,)+ R: Flow> Flow for fn($($T),+) -> R {
563            type WithoutGenerics = Self;
564            type OptionInnerType = Self;
565            fn name(cfg: &Config) -> String {
566                let params = vec![$(format!("arg{}: {}", $i, $T::name(cfg))),+];
567                format!("({}) => {}", params.join(", "), R::name(cfg))
568            }
569            fn inline(cfg: &Config) -> String {
570                let params = vec![$(format!("arg{}: {}", $i, $T::inline(cfg))),+];
571                format!("({}) => {}", params.join(", "), R::inline(cfg))
572            }
573            fn visit_generics(v: &mut impl TypeVisitor)
574            where
575                Self: 'static,
576            {
577                $(v.visit::<$T>();)+
578                v.visit::<R>();
579            }
580        }
581    };
582}
583
584impl_flow_fn!(impl fn() -> R);
585impl_flow_fn!(impl fn(0: A) -> R);
586impl_flow_fn!(impl fn(0: A, 1: B) -> R);
587impl_flow_fn!(impl fn(0: A, 1: B, 2: C) -> R);
588impl_flow_fn!(impl fn(0: A, 1: B, 2: C, 3: D) -> R);
589impl_flow_fn!(impl fn(0: A, 1: B, 2: C, 3: D, 4: E) -> R);
590impl_flow_fn!(impl fn(0: A, 1: B, 2: C, 3: D, 4: E, 5: F) -> R);
591impl_flow_fn!(impl fn(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G) -> R);
592impl_flow_fn!(impl fn(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H) -> R);
593impl_flow_fn!(impl fn(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I) -> R);
594impl_flow_fn!(impl fn(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J) -> R);
595impl_flow_fn!(impl fn(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K) -> R);
596impl_flow_fn!(impl fn(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L) -> R);