1use std::any::TypeId;
22use std::collections::HashMap;
23
24use eure_document::Text;
25use eure_document::identifier::Identifier;
26use indexmap::IndexMap;
27
28use crate::{
29 CodegenDefaults, ExtTypeSchema, RootCodegen, SchemaDocument, SchemaMetadata, SchemaNode,
30 SchemaNodeContent, SchemaNodeId, TextSchema, TypeCodegen,
31};
32
33pub struct SchemaNodeSpec {
53 pub content: SchemaNodeContent,
54 pub metadata: SchemaMetadata,
55 pub ext_types: IndexMap<Identifier, ExtTypeSchema>,
56 pub type_codegen: TypeCodegen,
57}
58
59pub trait BuildSchema {
60 fn type_name() -> Option<&'static str> {
65 None
66 }
67
68 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent;
73
74 fn schema_metadata() -> SchemaMetadata {
78 SchemaMetadata::default()
79 }
80
81 fn build_schema_node(ctx: &mut SchemaBuilder) -> SchemaNodeSpec {
86 SchemaNodeSpec {
87 content: Self::build_schema(ctx),
88 metadata: Self::schema_metadata(),
89 ext_types: IndexMap::new(),
90 type_codegen: TypeCodegen::None,
91 }
92 }
93}
94
95pub struct SchemaBuilder {
102 doc: SchemaDocument,
104 cache: HashMap<TypeId, SchemaNodeId>,
106}
107
108impl SchemaBuilder {
109 pub fn new() -> Self {
111 Self {
112 doc: SchemaDocument {
113 nodes: Vec::new(),
114 root: SchemaNodeId(0), types: Default::default(),
116 exports: Default::default(),
117 imports: Default::default(),
118 root_codegen: RootCodegen::default(),
119 codegen_defaults: CodegenDefaults::default(),
120 },
121 cache: HashMap::new(),
122 }
123 }
124
125 pub fn build<T: BuildSchema + 'static>(&mut self) -> SchemaNodeId {
133 let type_id = TypeId::of::<T>();
134
135 if let Some(&id) = self.cache.get(&type_id) {
137 return id;
138 }
139
140 let type_name = T::type_name();
142
143 if let Some(name) = type_name {
146 let content_id = self.reserve_node();
148
149 let spec = T::build_schema_node(self);
151 self.set_node_spec(content_id, spec);
152
153 if let Ok(ident) = name.parse::<eure_document::identifier::Identifier>() {
155 self.doc.types.insert(ident, content_id);
156 }
157
158 let ref_id = self.create_node(SchemaNodeContent::Reference(
160 crate::TypeReference::Resolved(content_id),
161 ));
162
163 self.cache.insert(type_id, ref_id);
165 ref_id
166 } else {
167 let id = self.reserve_node();
169 self.cache.insert(type_id, id);
170
171 let spec = T::build_schema_node(self);
172 self.set_node_spec(id, spec);
173
174 id
175 }
176 }
177
178 pub fn create_node(&mut self, content: SchemaNodeContent) -> SchemaNodeId {
183 let id = SchemaNodeId(self.doc.nodes.len());
184 self.doc.nodes.push(SchemaNode {
185 content,
186 metadata: SchemaMetadata::default(),
187 ext_types: Default::default(),
188 type_codegen: TypeCodegen::None,
189 });
190 id
191 }
192
193 pub fn create_node_with_metadata(
195 &mut self,
196 content: SchemaNodeContent,
197 metadata: SchemaMetadata,
198 ) -> SchemaNodeId {
199 let id = SchemaNodeId(self.doc.nodes.len());
200 self.doc.nodes.push(SchemaNode {
201 content,
202 metadata,
203 ext_types: Default::default(),
204 type_codegen: TypeCodegen::None,
205 });
206 id
207 }
208
209 fn reserve_node(&mut self) -> SchemaNodeId {
214 let id = SchemaNodeId(self.doc.nodes.len());
215 self.doc.nodes.push(SchemaNode {
216 content: SchemaNodeContent::Any, metadata: SchemaMetadata::default(),
218 ext_types: Default::default(),
219 type_codegen: TypeCodegen::None,
220 });
221 id
222 }
223
224 fn set_node_spec(&mut self, id: SchemaNodeId, spec: SchemaNodeSpec) {
226 let node = &mut self.doc.nodes[id.0];
227 node.content = spec.content;
228 node.metadata = spec.metadata;
229 node.ext_types = spec.ext_types;
230 node.type_codegen = spec.type_codegen;
231 }
232
233 pub fn node_mut(&mut self, id: SchemaNodeId) -> &mut SchemaNode {
235 &mut self.doc.nodes[id.0]
236 }
237
238 pub fn register_type(&mut self, name: &str, id: SchemaNodeId) {
240 if let Ok(ident) = name.parse::<eure_document::identifier::Identifier>() {
241 self.doc.types.insert(ident, id);
242 }
243 }
244
245 pub fn finish(mut self, root: SchemaNodeId) -> SchemaDocument {
247 self.doc.root = root;
248 self.doc.exports = self.doc.types.keys().cloned().collect();
251 self.doc
252 }
253}
254
255impl Default for SchemaBuilder {
256 fn default() -> Self {
257 Self::new()
258 }
259}
260
261impl SchemaDocument {
262 pub fn of<T: BuildSchema + 'static>() -> SchemaDocument {
274 let mut builder = SchemaBuilder::new();
275 let root = builder.build::<T>();
276 builder.finish(root)
277 }
278}
279
280impl BuildSchema for String {
285 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
286 SchemaNodeContent::Text(crate::TextSchema::default())
287 }
288}
289
290impl BuildSchema for &str {
291 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
292 SchemaNodeContent::Text(crate::TextSchema::default())
293 }
294}
295
296impl BuildSchema for bool {
297 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
298 SchemaNodeContent::Boolean
299 }
300}
301
302macro_rules! impl_build_schema_int {
303 ($($ty:ty),*) => {
304 $(
305 impl BuildSchema for $ty {
306 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
307 SchemaNodeContent::Integer(crate::IntegerSchema::default())
308 }
309 }
310 )*
311 };
312}
313
314impl_build_schema_int!(
315 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
316);
317
318impl BuildSchema for f32 {
320 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
321 SchemaNodeContent::Float(crate::FloatSchema {
322 precision: crate::FloatPrecision::F32,
323 ..Default::default()
324 })
325 }
326}
327
328impl BuildSchema for f64 {
329 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
330 SchemaNodeContent::Float(crate::FloatSchema {
331 precision: crate::FloatPrecision::F64,
332 ..Default::default()
333 })
334 }
335}
336
337impl BuildSchema for () {
339 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
340 SchemaNodeContent::Null
341 }
342}
343
344impl BuildSchema for Text {
345 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
346 SchemaNodeContent::Text(TextSchema {
347 language: None,
348 min_length: None,
349 max_length: None,
350 pattern: None,
351 unknown_fields: IndexMap::new(),
352 })
353 }
354}
355
356impl<T: BuildSchema + 'static> BuildSchema for Option<T> {
362 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
363 let some_schema = ctx.build::<T>();
364 let none_schema = ctx.create_node(SchemaNodeContent::Null);
365
366 SchemaNodeContent::Union(crate::UnionSchema {
367 variants: IndexMap::from([
368 ("some".to_string(), some_schema),
369 ("none".to_string(), none_schema),
370 ]),
371 unambiguous: Default::default(),
372 interop: crate::interop::UnionInterop::default(),
373 deny_untagged: Default::default(),
374 })
375 }
376}
377
378impl<T: BuildSchema + 'static, E: BuildSchema + 'static> BuildSchema for Result<T, E> {
380 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
381 let ok_schema = ctx.build::<T>();
382 let err_schema = ctx.build::<E>();
383
384 SchemaNodeContent::Union(crate::UnionSchema {
385 variants: IndexMap::from([
386 ("ok".to_string(), ok_schema),
387 ("err".to_string(), err_schema),
388 ]),
389 unambiguous: Default::default(),
390 interop: crate::interop::UnionInterop::default(),
391 deny_untagged: Default::default(),
392 })
393 }
394}
395
396impl<T: BuildSchema + 'static> BuildSchema for Vec<T> {
398 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
399 let item = ctx.build::<T>();
400 SchemaNodeContent::Array(crate::ArraySchema {
401 item,
402 min_length: None,
403 max_length: None,
404 unique: false,
405 contains: None,
406 binding_style: None,
407 })
408 }
409}
410
411impl<K: BuildSchema + 'static, V: BuildSchema + 'static> BuildSchema
413 for std::collections::HashMap<K, V>
414{
415 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
416 let key = ctx.build::<K>();
417 let value = ctx.build::<V>();
418 SchemaNodeContent::Map(crate::MapSchema {
419 key,
420 value,
421 min_size: None,
422 max_size: None,
423 })
424 }
425}
426
427impl<K: BuildSchema + 'static, V: BuildSchema + 'static> BuildSchema
429 for std::collections::BTreeMap<K, V>
430{
431 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
432 let key = ctx.build::<K>();
433 let value = ctx.build::<V>();
434 SchemaNodeContent::Map(crate::MapSchema {
435 key,
436 value,
437 min_size: None,
438 max_size: None,
439 })
440 }
441}
442
443impl<K: BuildSchema + 'static, V: BuildSchema + 'static> BuildSchema for IndexMap<K, V> {
445 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
446 let key = ctx.build::<K>();
447 let value = ctx.build::<V>();
448 SchemaNodeContent::Map(crate::MapSchema {
449 key,
450 value,
451 min_size: None,
452 max_size: None,
453 })
454 }
455}
456
457impl<T: BuildSchema + 'static> BuildSchema for Box<T> {
459 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
460 T::build_schema(ctx)
461 }
462}
463
464impl<T: BuildSchema + 'static> BuildSchema for std::rc::Rc<T> {
466 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
467 T::build_schema(ctx)
468 }
469}
470
471impl<T: BuildSchema + 'static> BuildSchema for std::sync::Arc<T> {
473 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
474 T::build_schema(ctx)
475 }
476}
477
478impl<A: BuildSchema + 'static> BuildSchema for (A,) {
480 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
481 let elements = vec![ctx.build::<A>()];
482 SchemaNodeContent::Tuple(crate::TupleSchema {
483 elements,
484 binding_style: None,
485 })
486 }
487}
488
489impl<A: BuildSchema + 'static, B: BuildSchema + 'static> BuildSchema for (A, B) {
490 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
491 let elements = vec![ctx.build::<A>(), ctx.build::<B>()];
492 SchemaNodeContent::Tuple(crate::TupleSchema {
493 elements,
494 binding_style: None,
495 })
496 }
497}
498
499impl<A: BuildSchema + 'static, B: BuildSchema + 'static, C: BuildSchema + 'static> BuildSchema
500 for (A, B, C)
501{
502 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
503 let elements = vec![ctx.build::<A>(), ctx.build::<B>(), ctx.build::<C>()];
504 SchemaNodeContent::Tuple(crate::TupleSchema {
505 elements,
506 binding_style: None,
507 })
508 }
509}
510
511impl<
512 A: BuildSchema + 'static,
513 B: BuildSchema + 'static,
514 C: BuildSchema + 'static,
515 D: BuildSchema + 'static,
516> BuildSchema for (A, B, C, D)
517{
518 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
519 let elements = vec![
520 ctx.build::<A>(),
521 ctx.build::<B>(),
522 ctx.build::<C>(),
523 ctx.build::<D>(),
524 ];
525 SchemaNodeContent::Tuple(crate::TupleSchema {
526 elements,
527 binding_style: None,
528 })
529 }
530}
531
532impl<
533 A: BuildSchema + 'static,
534 B: BuildSchema + 'static,
535 C: BuildSchema + 'static,
536 D: BuildSchema + 'static,
537 E: BuildSchema + 'static,
538> BuildSchema for (A, B, C, D, E)
539{
540 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
541 let elements = vec![
542 ctx.build::<A>(),
543 ctx.build::<B>(),
544 ctx.build::<C>(),
545 ctx.build::<D>(),
546 ctx.build::<E>(),
547 ];
548 SchemaNodeContent::Tuple(crate::TupleSchema {
549 elements,
550 binding_style: None,
551 })
552 }
553}
554
555impl<
556 A: BuildSchema + 'static,
557 B: BuildSchema + 'static,
558 C: BuildSchema + 'static,
559 D: BuildSchema + 'static,
560 E: BuildSchema + 'static,
561 F: BuildSchema + 'static,
562> BuildSchema for (A, B, C, D, E, F)
563{
564 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
565 let elements = vec![
566 ctx.build::<A>(),
567 ctx.build::<B>(),
568 ctx.build::<C>(),
569 ctx.build::<D>(),
570 ctx.build::<E>(),
571 ctx.build::<F>(),
572 ];
573 SchemaNodeContent::Tuple(crate::TupleSchema {
574 elements,
575 binding_style: None,
576 })
577 }
578}