1use std::any::TypeId;
22use std::collections::HashMap;
23
24use indexmap::IndexMap;
25
26use crate::{SchemaDocument, SchemaMetadata, SchemaNode, SchemaNodeContent, SchemaNodeId};
27
28pub trait BuildSchema {
43 fn type_name() -> Option<&'static str> {
48 None
49 }
50
51 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent;
56
57 fn schema_metadata() -> SchemaMetadata {
61 SchemaMetadata::default()
62 }
63}
64
65pub struct SchemaBuilder {
72 doc: SchemaDocument,
74 cache: HashMap<TypeId, SchemaNodeId>,
76}
77
78impl SchemaBuilder {
79 pub fn new() -> Self {
81 Self {
82 doc: SchemaDocument {
83 nodes: Vec::new(),
84 root: SchemaNodeId(0), types: Default::default(),
86 },
87 cache: HashMap::new(),
88 }
89 }
90
91 pub fn build<T: BuildSchema + 'static>(&mut self) -> SchemaNodeId {
99 let type_id = TypeId::of::<T>();
100
101 if let Some(&id) = self.cache.get(&type_id) {
103 return id;
104 }
105
106 let type_name = T::type_name();
108
109 if let Some(name) = type_name {
112 let content_id = self.reserve_node();
114
115 let content = T::build_schema(self);
117 let metadata = T::schema_metadata();
118 self.set_node(content_id, content, metadata);
119
120 if let Ok(ident) = name.parse::<eure_document::identifier::Identifier>() {
122 self.doc.types.insert(ident, content_id);
123 }
124
125 let ref_id = self.create_node(SchemaNodeContent::Reference(crate::TypeReference {
127 namespace: None,
128 name: name.parse().expect("valid type name"),
129 }));
130
131 self.cache.insert(type_id, ref_id);
133 ref_id
134 } else {
135 let id = self.reserve_node();
137 self.cache.insert(type_id, id);
138
139 let content = T::build_schema(self);
140 let metadata = T::schema_metadata();
141 self.set_node(id, content, metadata);
142
143 id
144 }
145 }
146
147 pub fn create_node(&mut self, content: SchemaNodeContent) -> SchemaNodeId {
152 let id = SchemaNodeId(self.doc.nodes.len());
153 self.doc.nodes.push(SchemaNode {
154 content,
155 metadata: SchemaMetadata::default(),
156 ext_types: Default::default(),
157 });
158 id
159 }
160
161 pub fn create_node_with_metadata(
163 &mut self,
164 content: SchemaNodeContent,
165 metadata: SchemaMetadata,
166 ) -> SchemaNodeId {
167 let id = SchemaNodeId(self.doc.nodes.len());
168 self.doc.nodes.push(SchemaNode {
169 content,
170 metadata,
171 ext_types: Default::default(),
172 });
173 id
174 }
175
176 fn reserve_node(&mut self) -> SchemaNodeId {
181 let id = SchemaNodeId(self.doc.nodes.len());
182 self.doc.nodes.push(SchemaNode {
183 content: SchemaNodeContent::Any, metadata: SchemaMetadata::default(),
185 ext_types: Default::default(),
186 });
187 id
188 }
189
190 fn set_node(&mut self, id: SchemaNodeId, content: SchemaNodeContent, metadata: SchemaMetadata) {
192 let node = &mut self.doc.nodes[id.0];
193 node.content = content;
194 node.metadata = metadata;
195 }
196
197 pub fn node_mut(&mut self, id: SchemaNodeId) -> &mut SchemaNode {
199 &mut self.doc.nodes[id.0]
200 }
201
202 pub fn register_type(&mut self, name: &str, id: SchemaNodeId) {
204 if let Ok(ident) = name.parse() {
205 self.doc.types.insert(ident, id);
206 }
207 }
208
209 pub fn finish(mut self, root: SchemaNodeId) -> SchemaDocument {
211 self.doc.root = root;
212 self.doc
213 }
214}
215
216impl Default for SchemaBuilder {
217 fn default() -> Self {
218 Self::new()
219 }
220}
221
222impl SchemaDocument {
223 pub fn of<T: BuildSchema + 'static>() -> SchemaDocument {
235 let mut builder = SchemaBuilder::new();
236 let root = builder.build::<T>();
237 builder.finish(root)
238 }
239}
240
241impl BuildSchema for String {
246 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
247 SchemaNodeContent::Text(crate::TextSchema::default())
248 }
249}
250
251impl BuildSchema for &str {
252 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
253 SchemaNodeContent::Text(crate::TextSchema::default())
254 }
255}
256
257impl BuildSchema for bool {
258 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
259 SchemaNodeContent::Boolean
260 }
261}
262
263impl BuildSchema for i8 {
265 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
266 SchemaNodeContent::Integer(crate::IntegerSchema::default())
267 }
268}
269
270impl BuildSchema for i16 {
271 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
272 SchemaNodeContent::Integer(crate::IntegerSchema::default())
273 }
274}
275
276impl BuildSchema for i32 {
277 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
278 SchemaNodeContent::Integer(crate::IntegerSchema::default())
279 }
280}
281
282impl BuildSchema for i64 {
283 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
284 SchemaNodeContent::Integer(crate::IntegerSchema::default())
285 }
286}
287
288impl BuildSchema for i128 {
289 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
290 SchemaNodeContent::Integer(crate::IntegerSchema::default())
291 }
292}
293
294impl BuildSchema for isize {
295 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
296 SchemaNodeContent::Integer(crate::IntegerSchema::default())
297 }
298}
299
300impl BuildSchema for u8 {
302 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
303 SchemaNodeContent::Integer(crate::IntegerSchema::default())
304 }
305}
306
307impl BuildSchema for u16 {
308 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
309 SchemaNodeContent::Integer(crate::IntegerSchema::default())
310 }
311}
312
313impl BuildSchema for u32 {
314 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
315 SchemaNodeContent::Integer(crate::IntegerSchema::default())
316 }
317}
318
319impl BuildSchema for u64 {
320 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
321 SchemaNodeContent::Integer(crate::IntegerSchema::default())
322 }
323}
324
325impl BuildSchema for u128 {
326 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
327 SchemaNodeContent::Integer(crate::IntegerSchema::default())
328 }
329}
330
331impl BuildSchema for usize {
332 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
333 SchemaNodeContent::Integer(crate::IntegerSchema::default())
334 }
335}
336
337impl BuildSchema for f32 {
339 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
340 SchemaNodeContent::Float(crate::FloatSchema {
341 precision: crate::FloatPrecision::F32,
342 ..Default::default()
343 })
344 }
345}
346
347impl BuildSchema for f64 {
348 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
349 SchemaNodeContent::Float(crate::FloatSchema {
350 precision: crate::FloatPrecision::F64,
351 ..Default::default()
352 })
353 }
354}
355
356impl BuildSchema for () {
358 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
359 SchemaNodeContent::Null
360 }
361}
362
363impl<T: BuildSchema + 'static> BuildSchema for Option<T> {
369 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
370 let some_schema = ctx.build::<T>();
371 let none_schema = ctx.create_node(SchemaNodeContent::Null);
372
373 SchemaNodeContent::Union(crate::UnionSchema {
374 variants: IndexMap::from([
375 ("some".to_string(), some_schema),
376 ("none".to_string(), none_schema),
377 ]),
378 unambiguous: Default::default(),
379 repr: eure_document::data_model::VariantRepr::default(),
380 deny_untagged: Default::default(),
381 })
382 }
383}
384
385impl<T: BuildSchema + 'static, E: BuildSchema + 'static> BuildSchema for Result<T, E> {
387 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
388 let ok_schema = ctx.build::<T>();
389 let err_schema = ctx.build::<E>();
390
391 SchemaNodeContent::Union(crate::UnionSchema {
392 variants: IndexMap::from([
393 ("ok".to_string(), ok_schema),
394 ("err".to_string(), err_schema),
395 ]),
396 unambiguous: Default::default(),
397 repr: eure_document::data_model::VariantRepr::default(),
398 deny_untagged: Default::default(),
399 })
400 }
401}
402
403impl<T: BuildSchema + 'static> BuildSchema for Vec<T> {
405 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
406 let item = ctx.build::<T>();
407 SchemaNodeContent::Array(crate::ArraySchema {
408 item,
409 min_length: None,
410 max_length: None,
411 unique: false,
412 contains: None,
413 binding_style: None,
414 })
415 }
416}
417
418impl<K: BuildSchema + 'static, V: BuildSchema + 'static> BuildSchema
420 for std::collections::HashMap<K, V>
421{
422 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
423 let key = ctx.build::<K>();
424 let value = ctx.build::<V>();
425 SchemaNodeContent::Map(crate::MapSchema {
426 key,
427 value,
428 min_size: None,
429 max_size: None,
430 })
431 }
432}
433
434impl<K: BuildSchema + 'static, V: BuildSchema + 'static> BuildSchema
436 for std::collections::BTreeMap<K, V>
437{
438 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
439 let key = ctx.build::<K>();
440 let value = ctx.build::<V>();
441 SchemaNodeContent::Map(crate::MapSchema {
442 key,
443 value,
444 min_size: None,
445 max_size: None,
446 })
447 }
448}
449
450impl<T: BuildSchema + 'static> BuildSchema for Box<T> {
452 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
453 T::build_schema(ctx)
454 }
455}
456
457impl<T: BuildSchema + 'static> BuildSchema for std::rc::Rc<T> {
459 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
460 T::build_schema(ctx)
461 }
462}
463
464impl<T: BuildSchema + 'static> BuildSchema for std::sync::Arc<T> {
466 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
467 T::build_schema(ctx)
468 }
469}
470
471impl<A: BuildSchema + 'static> BuildSchema for (A,) {
473 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
474 let elements = vec![ctx.build::<A>()];
475 SchemaNodeContent::Tuple(crate::TupleSchema {
476 elements,
477 binding_style: None,
478 })
479 }
480}
481
482impl<A: BuildSchema + 'static, B: BuildSchema + 'static> BuildSchema for (A, B) {
483 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
484 let elements = vec![ctx.build::<A>(), ctx.build::<B>()];
485 SchemaNodeContent::Tuple(crate::TupleSchema {
486 elements,
487 binding_style: None,
488 })
489 }
490}
491
492impl<A: BuildSchema + 'static, B: BuildSchema + 'static, C: BuildSchema + 'static> BuildSchema
493 for (A, B, C)
494{
495 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
496 let elements = vec![ctx.build::<A>(), ctx.build::<B>(), ctx.build::<C>()];
497 SchemaNodeContent::Tuple(crate::TupleSchema {
498 elements,
499 binding_style: None,
500 })
501 }
502}
503
504impl<
505 A: BuildSchema + 'static,
506 B: BuildSchema + 'static,
507 C: BuildSchema + 'static,
508 D: BuildSchema + 'static,
509> BuildSchema for (A, B, C, D)
510{
511 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
512 let elements = vec![
513 ctx.build::<A>(),
514 ctx.build::<B>(),
515 ctx.build::<C>(),
516 ctx.build::<D>(),
517 ];
518 SchemaNodeContent::Tuple(crate::TupleSchema {
519 elements,
520 binding_style: None,
521 })
522 }
523}
524
525impl<
526 A: BuildSchema + 'static,
527 B: BuildSchema + 'static,
528 C: BuildSchema + 'static,
529 D: BuildSchema + 'static,
530 E: BuildSchema + 'static,
531> BuildSchema for (A, B, C, D, E)
532{
533 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
534 let elements = vec![
535 ctx.build::<A>(),
536 ctx.build::<B>(),
537 ctx.build::<C>(),
538 ctx.build::<D>(),
539 ctx.build::<E>(),
540 ];
541 SchemaNodeContent::Tuple(crate::TupleSchema {
542 elements,
543 binding_style: None,
544 })
545 }
546}
547
548impl<
549 A: BuildSchema + 'static,
550 B: BuildSchema + 'static,
551 C: BuildSchema + 'static,
552 D: BuildSchema + 'static,
553 E: BuildSchema + 'static,
554 F: BuildSchema + 'static,
555> BuildSchema for (A, B, C, D, E, F)
556{
557 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
558 let elements = vec![
559 ctx.build::<A>(),
560 ctx.build::<B>(),
561 ctx.build::<C>(),
562 ctx.build::<D>(),
563 ctx.build::<E>(),
564 ctx.build::<F>(),
565 ];
566 SchemaNodeContent::Tuple(crate::TupleSchema {
567 elements,
568 binding_style: None,
569 })
570 }
571}