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}