1use crate::{
7 EmptySchema, PropertiesSchema, RefSchema, TaggedUnionSchema, ValuesSchema, type_utils,
8};
9use crate::{Serializable, TypeSchema, Types, elements::ElementsSchema};
10use std::cell::{Cell, RefCell};
11use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
12use std::ffi::{OsStr, OsString};
13use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
14use std::num::{
15 NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroIsize, NonZeroU8, NonZeroU16, NonZeroU32,
16 NonZeroU64, NonZeroUsize,
17};
18use std::path::{Path, PathBuf};
19use std::ptr::NonNull;
20use std::rc::Rc;
21use std::sync::atomic::{
22 AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicU8, AtomicU16, AtomicU32,
23 AtomicU64,
24};
25use std::sync::{Arc, Mutex, RwLock};
26use std::time::{Duration, Instant, SystemTime};
27
28thread_local! {
29 static RECURSION_TRACKER: RefCell<HashSet<String>> = RefCell::new(HashSet::new());
31}
32
33pub trait Exportable {
35 fn get_type_name() -> String {
37 type_utils::get_type_name::<Self>()
38 }
39
40 fn export() -> Box<dyn Serializable> {
44 Self::export_with_recursion_check()
45 }
46
47 fn export_internal() -> impl Serializable;
52
53 fn export_with_recursion_check() -> Box<dyn Serializable> {
55 let type_name = Self::get_type_name();
56
57 let is_recursive = RECURSION_TRACKER.with(|tracker| {
58 let mut tracker = tracker.borrow_mut();
59 let is_recursive = tracker.contains(&type_name);
60
61 if !is_recursive {
62 tracker.insert(type_name.clone());
64 }
65
66 is_recursive
67 });
68
69 if is_recursive {
70 return Box::new(RefSchema::new(type_utils::get_type_name_from(type_name)));
71 }
72
73 let result = Self::export_internal();
74
75 RECURSION_TRACKER.with(|tracker| {
77 let mut tracker = tracker.borrow_mut();
78 tracker.remove(&type_name);
79 });
80
81 Box::new(result)
82 }
83}
84
85macro_rules! exportable {
90 (generic: { $($gen:tt)* }) => {
93 exportable!(@parse_impls $($gen)*);
94 };
95 (typeschema: { $($ty:tt)* }) => {
96 exportable!(@parse_typeschema $($ty)*);
97 };
98 (features: { $($feat:tt)* }) => {
99 exportable!(@parse_features $($feat)*);
100 };
101
102 (
104 typeschema: { $($ty:tt)* },
105 generic: { $($gen:tt)* },
106 features: { $($feat:tt)* }
107 ) => {
108 exportable!(typeschema: { $($ty)* });
109 exportable!(generic: { $($gen)* });
110 exportable!(features: { $($feat)* });
111 };
112
113 (
114 typeschema: { $($ty:tt)* },
115 generic: { $($gen:tt)* }
116 ) => {
117 exportable!(typeschema: { $($ty)* });
118 exportable!(generic: { $($gen)* });
119 };
120
121 (@parse_features $feature:literal => { $($body:item)* }, $($rest:tt)*) => {
123 exportable!(@parse_features $feature => { $($body)* });
124 exportable!(@parse_features $($rest)*);
125 };
126 (@parse_features $feature:literal => { $($body:item)* }) => {
127 $(
128 #[cfg(feature = $feature)]
129 $body
130 )*
131 };
132 (@parse_features) => {};
133
134 (@parse_typeschema $ty:ty => $to:ident, $($rest:tt)*) => {
137 exportable!(@parse_typeschema $ty => {
138 TypeSchema::new(Types::$to)
139 }, $($rest)*);
140 };
141
142 (@parse_typeschema static $ty:ty => $to:expr, $($rest:tt)*) => {
143 exportable!(@parse_typeschema $ty => { $to }, $($rest)*);
144 };
145
146 (@parse_typeschema $ty:ty => $implementation:block, $($rest:tt)*) => {
148 impl Exportable for $ty {
149 fn export_internal() -> impl Serializable {
150 $implementation
151 }
152 }
153 exportable!(@parse_typeschema $($rest)*);
154 };
155
156 (@parse_typeschema) => {};
158
159 (@parse_impls $type:ident < $($type_param:ident $(: $trait_bound:path)?),* $(,)? > => $implementation:expr, $($rest:tt)*) => {
162 impl<$($type_param: 'static + Exportable $(+ $trait_bound)?),*> Exportable for $type<$($type_param),*> {
163 fn export_internal() -> impl Serializable {
164 $implementation
165 }
166 fn get_type_name() -> String {
167 format!(
168 "::ronky::--virtual--::generic::{}",
169 vec![$($type_param::get_type_name()),*].join("")
170 )
171 }
172 }
173 exportable!(@parse_impls $($rest)*);
174 };
175
176 (@parse_impls $type:ident < $($type_param:ident $(: $trait_bound:path)?),* $(,)? > => $implementation:block, $($rest:tt)*) => {
178 exportable!(@parse_impls $type < $($type_param $(: $trait_bound)?),* > => {
179 $implementation
180 }, $($rest)*);
181 };
182
183 (@parse_impls) => {};
185}
186
187type SliceOf<T> = [T];
191
192exportable! {
193 typeschema: {
194 static () => EmptySchema::new(),
195 char => String,
196 String => String,
197 &str => String,
198 bool => Boolean,
199 f32 => Float32,
200 f64 => Float64,
201 i8 => Int8,
202 u8 => Uint8,
203 i16 => Int16,
204 u16 => Uint16,
205 i32 => Int32,
206 u32 => Uint32,
207 i64 => Int64,
208 u64 => Uint64,
209 AtomicBool => Boolean,
210 AtomicI8 => Int8,
211 AtomicU8 => Uint8,
212 AtomicI16 => Int16,
213 AtomicU16 => Uint16,
214 AtomicI32 => Int32,
215 AtomicU32 => Uint32,
216 AtomicI64 => Int64,
217 AtomicU64 => Uint64,
218 NonZeroI8 => Int8,
219 NonZeroU8 => Uint8,
220 NonZeroI16 => Int16,
221 NonZeroU16 => Uint16,
222 NonZeroI32 => Int32,
223 NonZeroU32 => Uint32,
224 NonZeroI64 => Int64,
225 NonZeroU64 => Uint64,
226 NonZeroIsize => Int64,
227 NonZeroUsize => Uint64,
228 OsStr => String,
229 OsString => String,
230 PathBuf => String,
231 Path => String,
232 IpAddr => String,
233 Ipv4Addr => String,
234 Ipv6Addr => String,
235 SocketAddr => String,
236 Duration => Int64,
237 Instant => Int64,
238 SystemTime => Int64,
239 },
240 generic: {
241 Option<T> => T::export(), Rc<T> => T::export(),
244 Arc<T> => T::export(),
245 Cell<T> => T::export(),
246 RefCell<T> => T::export(),
247 Mutex<T> => T::export(),
248 RwLock<T> => T::export(),
249 NonNull<T> => T::export(),
250
251 Box<T> => T::export_with_recursion_check(),
253 Result<T, E> => {
254 let mut schema = TaggedUnionSchema::new();
255 let mut ok_props = PropertiesSchema::new();
256 let mut err_props = PropertiesSchema::new();
257
258 ok_props.set_property("value", Box::new(T::export()));
259 err_props.set_property("value", Box::new(E::export()));
260
261 schema.add_mapping("Ok", Box::new(ok_props));
262 schema.add_mapping("Err", Box::new(err_props));
263 schema
264 },
265
266 SliceOf<T> => ElementsSchema::new(Box::new(T::export())),
268 Vec<T> => ElementsSchema::new(Box::new(T::export())),
269 VecDeque<T> => ElementsSchema::new(Box::new(T::export())),
270 LinkedList<T> => ElementsSchema::new(Box::new(T::export())),
271 HashSet<T> => ElementsSchema::new(Box::new(T::export())),
272 BTreeSet<T> => ElementsSchema::new(Box::new(T::export())),
273 BinaryHeap<T> => ElementsSchema::new(Box::new(T::export())),
274
275 HashMap<K: ToString, V> => ValuesSchema::new(Box::new(V::export())),
277 BTreeMap<K: ToString, V> => ValuesSchema::new(Box::new(V::export())),
278 },
279 features: {
280 "chrono" => {
281 use chrono::{DateTime, FixedOffset, Utc, Local, NaiveTime, NaiveDate, NaiveDateTime, TimeZone};
282
283 exportable! {
284 typeschema: {
285 DateTime<Utc> => Timestamp,
286 DateTime<Local> => Timestamp,
287 DateTime<FixedOffset> => Timestamp,
288 NaiveDate => Timestamp,
289 NaiveTime => Timestamp,
290 NaiveDateTime => Timestamp,
291 chrono::Duration => Int64,
292 },
293 generic: {
294 DateTime<Tz: TimeZone> => TypeSchema::new(Types::Timestamp),
295 }
296 }
297 },
298 "time" => {
299 exportable! {
300 typeschema: {
301 time::OffsetDateTime => Timestamp,
302 time::PrimitiveDateTime => Timestamp,
303 time::Date => Timestamp,
304 time::Time => Timestamp,
305 time::Duration => Int64,
306 }
307 }
308 },
309 "uuid" => {
310 exportable! {
311 typeschema: {
312 uuid::Uuid => String,
313 }
314 }
315 },
316 "bigdecimal" => {
317 exportable! {
318 typeschema: {
319 bigdecimal::BigDecimal => String,
320 }
321 }
322 },
323 "num-bigint" => {
324 exportable! {
325 typeschema: {
326 num_bigint::BigInt => String,
327 }
328 }
329 },
330 "num-bigfloat" => {
331 exportable! {
332 typeschema: {
333 num_bigfloat::BigFloat => String,
334 }
335 }
336 },
337 "rust_decimal" => {
338 exportable! {
339 typeschema: {
340 rust_decimal::Decimal => String,
341 }
342 }
343 },
344 "decimal" => {
345 exportable! {
346 typeschema: {
347 decimal::d128 => String,
348 }
349 }
350 },
351 "url" => {
352 exportable! {
353 typeschema: {
354 url::Url => String,
355 }
356 }
357 },
358 "bytes" => {
359 exportable! {
360 typeschema: {
361 bytes::Bytes => String,
362 bytes::BytesMut => String,
363 }
364 }
365 },
366 "dashmap" => {
367 use dashmap::{DashMap, DashSet};
368
369 exportable! {
370 generic: {
371 DashMap<K: ToString, V> => ValuesSchema::new(Box::new(V::export())),
372 DashSet<T> => ElementsSchema::new(Box::new(T::export())),
373 }
374 }
375 },
376 "indexmap" => {
377 use indexmap::{IndexMap, IndexSet};
378
379 exportable! {
380 generic: {
381 IndexMap<K: ToString, V> => ValuesSchema::new(Box::new(V::export())),
382 IndexSet<T> => ElementsSchema::new(Box::new(T::export())),
383 }
384 }
385 },
386 "smallvec" => {
387 use smallvec::SmallVec;
388
389 exportable! {
390 generic: {
391 SmallVec<T: smallvec::Array> => ElementsSchema::new(Box::new(T::export())),
392 }
393 }
394 }
395 }
396}