tapi/
lib.rs

1pub mod builder;
2#[cfg(feature = "endpoints")]
3pub mod endpoints;
4pub mod kind;
5pub mod targets;
6
7#[cfg(test)]
8mod tests;
9
10use std::{
11    cell::{Cell, RefCell},
12    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
13    marker::PhantomData,
14    rc::Rc,
15    sync::Arc,
16};
17
18use indexmap::{IndexMap, IndexSet};
19use kind::{BuiltinTypeKind, TypeKind};
20pub use tapi_macro::{tapi, Tapi};
21
22pub trait Tapi: 'static {
23    fn name() -> &'static str;
24    fn id() -> std::any::TypeId {
25        std::any::TypeId::of::<Self>()
26    }
27    fn kind() -> TypeKind;
28    fn dependencies() -> Vec<DynTapi> {
29        match Self::kind() {
30            TypeKind::Struct(s) => s.fields.iter().map(|f| f.ty).collect(),
31            TypeKind::TupleStruct(s) => s.fields.iter().map(|f| f.ty).collect(),
32            TypeKind::Enum(e) => e
33                .variants
34                .iter()
35                .flat_map(|v| match &v.kind {
36                    kind::VariantKind::Unit => Vec::new(),
37                    kind::VariantKind::Tuple(fields) => fields.to_vec(),
38                    kind::VariantKind::Struct(fields) => fields.iter().map(|f| f.ty).collect(),
39                })
40                .collect(),
41            TypeKind::List(ty) => vec![ty],
42            TypeKind::Option(ty) => vec![ty],
43            TypeKind::Tuple(fields) => fields.to_vec(),
44            TypeKind::Builtin(_) => Vec::new(),
45            TypeKind::Record(k, v) => vec![k, v],
46            TypeKind::Any => Vec::new(),
47        }
48    }
49    fn path() -> Vec<&'static str> {
50        let mut path = std::any::type_name::<Self>()
51            .split('<')
52            .next()
53            .unwrap()
54            .split("::")
55            .collect::<Vec<_>>();
56        path.pop();
57        path
58    }
59    fn boxed() -> DynTapi
60    where
61        Self: Sized + 'static,
62    {
63        &TypedWrap::<Self>(PhantomData)
64    }
65    fn all_dependencies() -> Vec<DynTapi>
66    where
67        Self: Sized,
68    {
69        let mut deps = Self::dependencies();
70        deps.push(Self::boxed());
71        transitive_closure(deps)
72    }
73}
74
75pub trait TapiDyn: std::fmt::Debug {
76    fn name(&self) -> &'static str;
77    fn id(&self) -> std::any::TypeId;
78    fn kind(&self) -> TypeKind;
79    fn dependencies(&self) -> Vec<DynTapi>;
80    fn path(&self) -> Vec<&'static str>;
81}
82
83pub type DynTapi = &'static dyn TapiDyn;
84
85impl<T: Tapi> TapiDyn for TypedWrap<T> {
86    fn name(&self) -> &'static str {
87        <T as Tapi>::name()
88    }
89    fn id(&self) -> std::any::TypeId {
90        <T as Tapi>::id()
91    }
92    fn kind(&self) -> TypeKind {
93        <T as Tapi>::kind()
94    }
95    fn dependencies(&self) -> Vec<DynTapi> {
96        <T as Tapi>::dependencies()
97    }
98    fn path(&self) -> Vec<&'static str> {
99        <T as Tapi>::path()
100    }
101}
102
103pub struct TypedWrap<T>(PhantomData<T>);
104impl<T> TypedWrap<T> {
105    pub const fn new() -> Self {
106        Self(PhantomData)
107    }
108}
109impl<T> Clone for TypedWrap<T> {
110    fn clone(&self) -> Self {
111        Self(PhantomData)
112    }
113}
114impl<T> std::fmt::Debug for TypedWrap<T> {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        f.debug_struct(std::any::type_name::<Self>()).finish()
117    }
118}
119
120macro_rules! impl_typed {
121    ($($ty:ty = $ts_name:literal & $kind:expr,)*) => {
122        $(
123            impl Tapi for $ty {
124                fn name() -> &'static str {
125                    std::any::type_name::<$ty>()
126                }
127                fn id() -> std::any::TypeId {
128                    std::any::TypeId::of::<$ty>()
129                }
130                fn kind() -> TypeKind {
131                    TypeKind::Builtin($kind)
132                }
133                fn path() -> Vec<&'static str> {
134                    Vec::new()
135                }
136            }
137        )*
138    };
139}
140macro_rules! impl_generic {
141    ($($ty:ident = $ts_name:literal & $zod_name:literal & $kind:expr,)*) => {
142        $(
143            impl<T: Tapi + 'static> Tapi for $ty<T> {
144                fn name() -> &'static str {
145                    std::any::type_name::<$ty<T>>()
146                }
147                fn id() -> std::any::TypeId {
148                    std::any::TypeId::of::<$ty<T>>()
149                }
150                fn kind() -> TypeKind {
151                    $kind
152                }
153                fn path() -> Vec<&'static str> {
154                    Vec::new()
155                }
156            }
157        )*
158    };
159}
160impl_typed!(
161    () = "unknown" & BuiltinTypeKind::Unit,
162    String = "string" & BuiltinTypeKind::String,
163    i8 = "number" & BuiltinTypeKind::I8,
164    i16 = "number" & BuiltinTypeKind::I16,
165    i32 = "number" & BuiltinTypeKind::I32,
166    i64 = "number" & BuiltinTypeKind::I64,
167    i128 = "number" & BuiltinTypeKind::I128,
168    u8 = "number" & BuiltinTypeKind::U8,
169    u16 = "number" & BuiltinTypeKind::U16,
170    u32 = "number" & BuiltinTypeKind::U32,
171    u64 = "number" & BuiltinTypeKind::U64,
172    u128 = "number" & BuiltinTypeKind::U128,
173    usize = "number" & BuiltinTypeKind::Usize,
174    f32 = "number" & BuiltinTypeKind::F32,
175    f64 = "number" & BuiltinTypeKind::F64,
176    bool = "boolean" & BuiltinTypeKind::Bool,
177    char = "string" & BuiltinTypeKind::Char,
178);
179#[cfg(feature = "chrono")]
180impl_typed!(
181    chrono::DateTime<chrono::Utc> = "string" & BuiltinTypeKind::String,
182    chrono::DateTime<chrono::FixedOffset> = "string" & BuiltinTypeKind::String,
183    chrono::NaiveDate = "string" & BuiltinTypeKind::String,
184    chrono::NaiveTime = "string" & BuiltinTypeKind::String,
185    chrono::NaiveDateTime = "string" & BuiltinTypeKind::String,
186);
187#[cfg(feature = "toml")]
188impl_typed!(
189    toml::value::Date = "string" & BuiltinTypeKind::String,
190    toml::value::Datetime = "string" & BuiltinTypeKind::String,
191    toml::value::Time = "string" & BuiltinTypeKind::String,
192);
193#[cfg(feature = "smol_str")]
194impl_typed!(smol_str::SmolStr = "string" & BuiltinTypeKind::String,);
195impl_generic!(
196    Vec = "{}[]" & "z.array({})" & TypeKind::List(T::boxed()),
197    Option = "({} | null)" & "z.optional({})" & TypeKind::Option(T::boxed()),
198    HashSet = "{}[]" & "z.array({})" & TypeKind::List(T::boxed()),
199    BTreeSet = "{}[]" & "z.array({})" & TypeKind::List(T::boxed()),
200    IndexSet = "{}[]" & "z.array({})" & TypeKind::List(T::boxed()),
201    Box = "{}" & "{}" & T::kind(),
202    Rc = "{}" & "{}" & T::kind(),
203    Arc = "{}" & "{}" & T::kind(),
204    Cell = "{}" & "{}" & T::kind(),
205    RefCell = "{}" & "{}" & T::kind(),
206);
207impl<const N: usize, T: Tapi + 'static> Tapi for [T; N] {
208    fn name() -> &'static str {
209        std::any::type_name::<[T; N]>()
210    }
211    fn id() -> std::any::TypeId {
212        std::any::TypeId::of::<[T; N]>()
213    }
214    fn kind() -> TypeKind {
215        TypeKind::List(T::boxed())
216    }
217    fn path() -> Vec<&'static str> {
218        Vec::new()
219    }
220}
221impl<K: 'static + Tapi, V: 'static + Tapi> Tapi for HashMap<K, V> {
222    fn name() -> &'static str {
223        std::any::type_name::<Self>()
224    }
225    fn id() -> std::any::TypeId {
226        std::any::TypeId::of::<Self>()
227    }
228    fn kind() -> TypeKind {
229        TypeKind::Record(K::boxed(), V::boxed())
230    }
231    fn path() -> Vec<&'static str> {
232        Vec::new()
233    }
234}
235impl<K: 'static + Tapi, V: 'static + Tapi> Tapi for BTreeMap<K, V> {
236    fn name() -> &'static str {
237        std::any::type_name::<Self>()
238    }
239    fn id() -> std::any::TypeId {
240        std::any::TypeId::of::<Self>()
241    }
242    fn kind() -> TypeKind {
243        TypeKind::Record(K::boxed(), V::boxed())
244    }
245    fn path() -> Vec<&'static str> {
246        Vec::new()
247    }
248}
249impl<K: 'static + Tapi, V: 'static + Tapi> Tapi for IndexMap<K, V> {
250    fn name() -> &'static str {
251        std::any::type_name::<Self>()
252    }
253    fn id() -> std::any::TypeId {
254        std::any::TypeId::of::<Self>()
255    }
256    fn kind() -> TypeKind {
257        TypeKind::Record(K::boxed(), V::boxed())
258    }
259    fn path() -> Vec<&'static str> {
260        Vec::new()
261    }
262}
263
264macro_rules! impl_tuple {
265    ($($ty:ident),*) => {
266        impl<$($ty: 'static + Tapi),*> Tapi for ($($ty,)*) {
267            fn name() -> &'static str {
268                std::any::type_name::<Self>()
269            }
270            fn id() -> std::any::TypeId {
271                std::any::TypeId::of::<Self>()
272            }
273            fn kind() -> TypeKind {
274                TypeKind::Tuple(vec![$(<$ty as Tapi>::boxed()),*])
275            }
276            fn path() -> Vec<&'static str> {
277                Vec::new()
278            }
279        }
280    };
281}
282impl_tuple!(A, B);
283impl_tuple!(A, B, C);
284impl_tuple!(A, B, C, D);
285
286impl Tapi for serde_json::Value {
287    fn name() -> &'static str {
288        std::any::type_name::<Self>()
289    }
290    fn id() -> std::any::TypeId {
291        std::any::TypeId::of::<Self>()
292    }
293    fn kind() -> TypeKind {
294        TypeKind::Any
295    }
296    fn path() -> Vec<&'static str> {
297        Vec::new()
298    }
299}
300
301fn transitive_closure(mut closure: Vec<DynTapi>) -> Vec<DynTapi> {
302    let mut next = Vec::new();
303    loop {
304        for c in &closure {
305            next.extend(c.dependencies().into_iter());
306        }
307        let mut done = true;
308        for n in next.drain(..) {
309            if closure.iter().all(|m| m.id() != n.id()) {
310                done = false;
311                closure.push(n);
312            }
313        }
314        if done {
315            break;
316        }
317    }
318    closure
319}