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 root_codegen: RootCodegen::default(),
117 codegen_defaults: CodegenDefaults::default(),
118 },
119 cache: HashMap::new(),
120 }
121 }
122
123 pub fn build<T: BuildSchema + 'static>(&mut self) -> SchemaNodeId {
131 let type_id = TypeId::of::<T>();
132
133 if let Some(&id) = self.cache.get(&type_id) {
135 return id;
136 }
137
138 let type_name = T::type_name();
140
141 if let Some(name) = type_name {
144 let content_id = self.reserve_node();
146
147 let spec = T::build_schema_node(self);
149 self.set_node_spec(content_id, spec);
150
151 if let Ok(ident) = name.parse::<eure_document::identifier::Identifier>() {
153 self.doc.types.insert(ident, content_id);
154 }
155
156 let ref_id = self.create_node(SchemaNodeContent::Reference(crate::TypeReference {
158 namespace: None,
159 name: name.parse().expect("valid type name"),
160 }));
161
162 self.cache.insert(type_id, ref_id);
164 ref_id
165 } else {
166 let id = self.reserve_node();
168 self.cache.insert(type_id, id);
169
170 let spec = T::build_schema_node(self);
171 self.set_node_spec(id, spec);
172
173 id
174 }
175 }
176
177 pub fn create_node(&mut self, content: SchemaNodeContent) -> SchemaNodeId {
182 let id = SchemaNodeId(self.doc.nodes.len());
183 self.doc.nodes.push(SchemaNode {
184 content,
185 metadata: SchemaMetadata::default(),
186 ext_types: Default::default(),
187 type_codegen: TypeCodegen::None,
188 });
189 id
190 }
191
192 pub fn create_node_with_metadata(
194 &mut self,
195 content: SchemaNodeContent,
196 metadata: SchemaMetadata,
197 ) -> SchemaNodeId {
198 let id = SchemaNodeId(self.doc.nodes.len());
199 self.doc.nodes.push(SchemaNode {
200 content,
201 metadata,
202 ext_types: Default::default(),
203 type_codegen: TypeCodegen::None,
204 });
205 id
206 }
207
208 fn reserve_node(&mut self) -> SchemaNodeId {
213 let id = SchemaNodeId(self.doc.nodes.len());
214 self.doc.nodes.push(SchemaNode {
215 content: SchemaNodeContent::Any, metadata: SchemaMetadata::default(),
217 ext_types: Default::default(),
218 type_codegen: TypeCodegen::None,
219 });
220 id
221 }
222
223 fn set_node_spec(&mut self, id: SchemaNodeId, spec: SchemaNodeSpec) {
225 let node = &mut self.doc.nodes[id.0];
226 node.content = spec.content;
227 node.metadata = spec.metadata;
228 node.ext_types = spec.ext_types;
229 node.type_codegen = spec.type_codegen;
230 }
231
232 pub fn node_mut(&mut self, id: SchemaNodeId) -> &mut SchemaNode {
234 &mut self.doc.nodes[id.0]
235 }
236
237 pub fn register_type(&mut self, name: &str, id: SchemaNodeId) {
239 if let Ok(ident) = name.parse() {
240 self.doc.types.insert(ident, id);
241 }
242 }
243
244 pub fn finish(mut self, root: SchemaNodeId) -> SchemaDocument {
246 self.doc.root = root;
247 self.doc
248 }
249}
250
251impl Default for SchemaBuilder {
252 fn default() -> Self {
253 Self::new()
254 }
255}
256
257impl SchemaDocument {
258 pub fn of<T: BuildSchema + 'static>() -> SchemaDocument {
270 let mut builder = SchemaBuilder::new();
271 let root = builder.build::<T>();
272 builder.finish(root)
273 }
274}
275
276impl BuildSchema for String {
281 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
282 SchemaNodeContent::Text(crate::TextSchema::default())
283 }
284}
285
286impl BuildSchema for &str {
287 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
288 SchemaNodeContent::Text(crate::TextSchema::default())
289 }
290}
291
292impl BuildSchema for bool {
293 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
294 SchemaNodeContent::Boolean
295 }
296}
297
298macro_rules! impl_build_schema_int {
299 ($($ty:ty),*) => {
300 $(
301 impl BuildSchema for $ty {
302 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
303 SchemaNodeContent::Integer(crate::IntegerSchema::default())
304 }
305 }
306 )*
307 };
308}
309
310impl_build_schema_int!(
311 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
312);
313
314impl BuildSchema for f32 {
316 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
317 SchemaNodeContent::Float(crate::FloatSchema {
318 precision: crate::FloatPrecision::F32,
319 ..Default::default()
320 })
321 }
322}
323
324impl BuildSchema for f64 {
325 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
326 SchemaNodeContent::Float(crate::FloatSchema {
327 precision: crate::FloatPrecision::F64,
328 ..Default::default()
329 })
330 }
331}
332
333impl BuildSchema for () {
335 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
336 SchemaNodeContent::Null
337 }
338}
339
340impl BuildSchema for Text {
341 fn build_schema(_ctx: &mut SchemaBuilder) -> SchemaNodeContent {
342 SchemaNodeContent::Text(TextSchema {
343 language: None,
344 min_length: None,
345 max_length: None,
346 pattern: None,
347 unknown_fields: IndexMap::new(),
348 })
349 }
350}
351
352impl<T: BuildSchema + 'static> BuildSchema for Option<T> {
358 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
359 let some_schema = ctx.build::<T>();
360 let none_schema = ctx.create_node(SchemaNodeContent::Null);
361
362 SchemaNodeContent::Union(crate::UnionSchema {
363 variants: IndexMap::from([
364 ("some".to_string(), some_schema),
365 ("none".to_string(), none_schema),
366 ]),
367 unambiguous: Default::default(),
368 interop: crate::interop::UnionInterop::default(),
369 deny_untagged: Default::default(),
370 })
371 }
372}
373
374impl<T: BuildSchema + 'static, E: BuildSchema + 'static> BuildSchema for Result<T, E> {
376 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
377 let ok_schema = ctx.build::<T>();
378 let err_schema = ctx.build::<E>();
379
380 SchemaNodeContent::Union(crate::UnionSchema {
381 variants: IndexMap::from([
382 ("ok".to_string(), ok_schema),
383 ("err".to_string(), err_schema),
384 ]),
385 unambiguous: Default::default(),
386 interop: crate::interop::UnionInterop::default(),
387 deny_untagged: Default::default(),
388 })
389 }
390}
391
392impl<T: BuildSchema + 'static> BuildSchema for Vec<T> {
394 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
395 let item = ctx.build::<T>();
396 SchemaNodeContent::Array(crate::ArraySchema {
397 item,
398 min_length: None,
399 max_length: None,
400 unique: false,
401 contains: None,
402 binding_style: None,
403 })
404 }
405}
406
407impl<K: BuildSchema + 'static, V: BuildSchema + 'static> BuildSchema
409 for std::collections::HashMap<K, V>
410{
411 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
412 let key = ctx.build::<K>();
413 let value = ctx.build::<V>();
414 SchemaNodeContent::Map(crate::MapSchema {
415 key,
416 value,
417 min_size: None,
418 max_size: None,
419 })
420 }
421}
422
423impl<K: BuildSchema + 'static, V: BuildSchema + 'static> BuildSchema
425 for std::collections::BTreeMap<K, V>
426{
427 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
428 let key = ctx.build::<K>();
429 let value = ctx.build::<V>();
430 SchemaNodeContent::Map(crate::MapSchema {
431 key,
432 value,
433 min_size: None,
434 max_size: None,
435 })
436 }
437}
438
439impl<K: BuildSchema + 'static, V: BuildSchema + 'static> BuildSchema for IndexMap<K, V> {
441 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
442 let key = ctx.build::<K>();
443 let value = ctx.build::<V>();
444 SchemaNodeContent::Map(crate::MapSchema {
445 key,
446 value,
447 min_size: None,
448 max_size: None,
449 })
450 }
451}
452
453impl<T: BuildSchema + 'static> BuildSchema for Box<T> {
455 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
456 T::build_schema(ctx)
457 }
458}
459
460impl<T: BuildSchema + 'static> BuildSchema for std::rc::Rc<T> {
462 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
463 T::build_schema(ctx)
464 }
465}
466
467impl<T: BuildSchema + 'static> BuildSchema for std::sync::Arc<T> {
469 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
470 T::build_schema(ctx)
471 }
472}
473
474impl<A: BuildSchema + 'static> BuildSchema for (A,) {
476 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
477 let elements = vec![ctx.build::<A>()];
478 SchemaNodeContent::Tuple(crate::TupleSchema {
479 elements,
480 binding_style: None,
481 })
482 }
483}
484
485impl<A: BuildSchema + 'static, B: BuildSchema + 'static> BuildSchema for (A, B) {
486 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
487 let elements = vec![ctx.build::<A>(), ctx.build::<B>()];
488 SchemaNodeContent::Tuple(crate::TupleSchema {
489 elements,
490 binding_style: None,
491 })
492 }
493}
494
495impl<A: BuildSchema + 'static, B: BuildSchema + 'static, C: BuildSchema + 'static> BuildSchema
496 for (A, B, C)
497{
498 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
499 let elements = vec![ctx.build::<A>(), ctx.build::<B>(), ctx.build::<C>()];
500 SchemaNodeContent::Tuple(crate::TupleSchema {
501 elements,
502 binding_style: None,
503 })
504 }
505}
506
507impl<
508 A: BuildSchema + 'static,
509 B: BuildSchema + 'static,
510 C: BuildSchema + 'static,
511 D: BuildSchema + 'static,
512> BuildSchema for (A, B, C, D)
513{
514 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
515 let elements = vec![
516 ctx.build::<A>(),
517 ctx.build::<B>(),
518 ctx.build::<C>(),
519 ctx.build::<D>(),
520 ];
521 SchemaNodeContent::Tuple(crate::TupleSchema {
522 elements,
523 binding_style: None,
524 })
525 }
526}
527
528impl<
529 A: BuildSchema + 'static,
530 B: BuildSchema + 'static,
531 C: BuildSchema + 'static,
532 D: BuildSchema + 'static,
533 E: BuildSchema + 'static,
534> BuildSchema for (A, B, C, D, E)
535{
536 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
537 let elements = vec![
538 ctx.build::<A>(),
539 ctx.build::<B>(),
540 ctx.build::<C>(),
541 ctx.build::<D>(),
542 ctx.build::<E>(),
543 ];
544 SchemaNodeContent::Tuple(crate::TupleSchema {
545 elements,
546 binding_style: None,
547 })
548 }
549}
550
551impl<
552 A: BuildSchema + 'static,
553 B: BuildSchema + 'static,
554 C: BuildSchema + 'static,
555 D: BuildSchema + 'static,
556 E: BuildSchema + 'static,
557 F: BuildSchema + 'static,
558> BuildSchema for (A, B, C, D, E, F)
559{
560 fn build_schema(ctx: &mut SchemaBuilder) -> SchemaNodeContent {
561 let elements = vec![
562 ctx.build::<A>(),
563 ctx.build::<B>(),
564 ctx.build::<C>(),
565 ctx.build::<D>(),
566 ctx.build::<E>(),
567 ctx.build::<F>(),
568 ];
569 SchemaNodeContent::Tuple(crate::TupleSchema {
570 elements,
571 binding_style: None,
572 })
573 }
574}