zod_core/
lib.rs

1//! **_NOTE:_**  This crate is not ready for production yet!
2
3#![deny(unsafe_code)]
4
5#[cfg(feature = "rpc")]
6pub mod rpc;
7
8mod build_ins;
9
10#[cfg(debug_assertions)]
11pub mod docs;
12
13use std::collections::BTreeMap;
14
15pub use build_ins::*;
16
17pub trait ZodType {
18    fn schema() -> String;
19    fn type_def() -> TsTypeDef;
20
21    fn docs() -> Option<&'static str> {
22        None
23    }
24
25    fn inline() -> InlinedType {
26        InlinedType::Literal(Self::type_def().to_string())
27    }
28}
29
30pub enum InlinedType {
31    Literal(String),
32    Ref {
33        ns_name: &'static str,
34        name: &'static str,
35    },
36}
37
38impl std::fmt::Display for InlinedType {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        match self {
41            Self::Literal(inner) => write!(f, "{}", inner),
42            Self::Ref { ns_name, name } => {
43                write!(f, "{}.{}", ns_name, name)
44            }
45        }
46    }
47}
48
49pub trait Namespace {
50    const NAME: &'static str;
51
52    fn docs() -> Option<&'static str> {
53        None
54    }
55
56    #[cfg(feature = "inventory")]
57    fn members() -> Vec<&'static NamespaceMemberDefinition> {
58        let members = inventory::iter::<NamespaceMemberDefinition>()
59            .filter(|namespace| namespace.ns_name == Self::NAME);
60
61        members.collect()
62    }
63}
64
65type RuntimeValue<T> = &'static (dyn Fn() -> T + Sync);
66
67pub struct NamespaceMemberDefinition {
68    ns_name: &'static str,
69    name: &'static str,
70    schema: RuntimeValue<String>,
71    type_def: RuntimeValue<TsTypeDef>,
72}
73
74impl NamespaceMemberDefinition {
75    #[doc(hidden)]
76    pub const fn new_for<T: ZodType + 'static>(ns_name: &'static str, name: &'static str) -> Self {
77        Self {
78            ns_name,
79            name,
80            schema: &<T as ZodType>::schema,
81            type_def: &<T as ZodType>::type_def,
82        }
83    }
84
85    pub fn namespace(&self) -> &'static str {
86        self.ns_name
87    }
88    pub fn name(&self) -> &'static str {
89        self.name
90    }
91
92    pub fn schema(&self) -> String {
93        (self.schema)()
94    }
95
96    pub fn type_def(&self) -> TsTypeDef {
97        (self.type_def)()
98    }
99
100    pub fn collect() -> BTreeMap<&'static str, Vec<&'static NamespaceMemberDefinition>> {
101        let mut out = BTreeMap::<&'static str, Vec<&'static NamespaceMemberDefinition>>::default();
102        for def in inventory::iter::<NamespaceMemberDefinition>() {
103            out.entry(def.namespace()).or_default().push(def);
104        }
105        out
106    }
107}
108
109#[derive(Debug, Clone, PartialEq)]
110pub enum TsTypeDef {
111    Interface(String),
112    Type(String),
113}
114
115impl std::ops::Deref for TsTypeDef {
116    type Target = String;
117
118    fn deref(&self) -> &Self::Target {
119        match self {
120            Self::Type(inner) => inner,
121            Self::Interface(inner) => inner,
122        }
123    }
124}
125
126impl std::fmt::Display for TsTypeDef {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        match self {
129            TsTypeDef::Interface(s) => write!(f, "{s}"),
130            TsTypeDef::Type(s) => write!(f, "{s}"),
131        }
132    }
133}
134
135impl std::cmp::PartialEq<&str> for TsTypeDef {
136    fn eq(&self, other: &&str) -> bool {
137        let s: &str = self;
138        (&s).eq(other)
139    }
140}
141
142impl std::cmp::PartialEq<str> for TsTypeDef {
143    fn eq(&self, other: &str) -> bool {
144        let s: &str = self;
145        s.eq(other)
146    }
147}
148
149impl std::cmp::PartialEq<String> for TsTypeDef {
150    fn eq(&self, other: &String) -> bool {
151        self.eq(other.as_str())
152    }
153}
154
155inventory::collect!(NamespaceMemberDefinition);
156
157pub trait TypeRegister<T> {}