1use crate::parse::{
46 ParsedArraySchema, ParsedExtTypeSchema, ParsedFloatSchema, ParsedIntegerSchema,
47 ParsedMapSchema, ParsedRecordSchema, ParsedSchemaMetadata, ParsedSchemaNode,
48 ParsedSchemaNodeContent, ParsedTupleSchema, ParsedUnionSchema, ParsedUnknownFieldsPolicy,
49};
50use crate::{
51 ArraySchema, Bound, ExtTypeSchema, FloatPrecision, FloatSchema, IntegerSchema, MapSchema,
52 RecordFieldSchema, RecordSchema, SchemaDocument, SchemaMetadata, SchemaNodeContent,
53 SchemaNodeId, TupleSchema, UnionSchema, UnknownFieldsPolicy,
54};
55use eure_document::document::node::{Node, NodeValue};
56use eure_document::document::{EureDocument, NodeId};
57use eure_document::identifier::Identifier;
58use eure_document::parse::ParseError;
59use eure_document::value::ObjectKey;
60use indexmap::IndexMap;
61use num_bigint::BigInt;
62use thiserror::Error;
63
64#[derive(Debug, Error, Clone, PartialEq)]
66pub enum ConversionError {
67 #[error("Invalid type name: {0}")]
68 InvalidTypeName(ObjectKey),
69
70 #[error("Unsupported schema construct at path: {0}")]
71 UnsupportedConstruct(String),
72
73 #[error("Invalid extension value: {extension} at path {path}")]
74 InvalidExtensionValue { extension: String, path: String },
75
76 #[error("Invalid range string: {0}")]
77 InvalidRangeString(String),
78
79 #[error("Invalid precision: {0} (expected \"f32\" or \"f64\")")]
80 InvalidPrecision(String),
81
82 #[error("Undefined type reference: {0}")]
83 UndefinedTypeReference(String),
84
85 #[error("Parse error: {0}")]
86 ParseError(#[from] ParseError),
87}
88
89pub type SchemaSourceMap = IndexMap<SchemaNodeId, NodeId>;
92
93struct Converter<'a> {
95 doc: &'a EureDocument,
96 schema: SchemaDocument,
97 source_map: SchemaSourceMap,
99}
100
101impl<'a> Converter<'a> {
102 fn new(doc: &'a EureDocument) -> Self {
103 Self {
104 doc,
105 schema: SchemaDocument::new(),
106 source_map: IndexMap::new(),
107 }
108 }
109
110 fn convert(mut self) -> Result<(SchemaDocument, SchemaSourceMap), ConversionError> {
112 let root_id = self.doc.get_root_id();
113 let root_node = self.doc.node(root_id);
114
115 self.convert_types(root_node)?;
117
118 self.schema.root = self.convert_node(root_id)?;
120
121 self.validate_type_references()?;
123
124 Ok((self.schema, self.source_map))
125 }
126
127 fn convert_types(&mut self, node: &Node) -> Result<(), ConversionError> {
129 let types_ident: Identifier = "types".parse().unwrap();
130 if let Some(types_node_id) = node.extensions.get(&types_ident) {
131 let types_node = self.doc.node(*types_node_id);
132 if let NodeValue::Map(map) = &types_node.content {
133 for (key, &node_id) in map.iter() {
134 if let ObjectKey::String(name) = key {
135 let type_name: Identifier = name
136 .parse()
137 .map_err(|_| ConversionError::InvalidTypeName(key.clone()))?;
138 let schema_id = self.convert_node(node_id)?;
139 self.schema.types.insert(type_name, schema_id);
140 } else {
141 return Err(ConversionError::InvalidTypeName(key.clone()));
142 }
143 }
144 } else {
145 return Err(ConversionError::InvalidExtensionValue {
146 extension: "types".to_string(),
147 path: "$types must be a map".to_string(),
148 });
149 }
150 }
151 Ok(())
152 }
153
154 fn validate_type_references(&self) -> Result<(), ConversionError> {
156 for node in &self.schema.nodes {
157 if let SchemaNodeContent::Reference(type_ref) = &node.content
158 && type_ref.namespace.is_none()
159 && !self.schema.types.contains_key(&type_ref.name)
160 {
161 return Err(ConversionError::UndefinedTypeReference(
162 type_ref.name.to_string(),
163 ));
164 }
165 }
166 Ok(())
167 }
168
169 fn convert_node(&mut self, node_id: NodeId) -> Result<SchemaNodeId, ConversionError> {
171 let parsed: ParsedSchemaNode = self.doc.parse(node_id)?;
173
174 let content = self.convert_content(parsed.content)?;
176 let metadata = self.convert_metadata(parsed.metadata)?;
177 let ext_types = self.convert_ext_types(parsed.ext_types)?;
178
179 let schema_id = self.schema.create_node(content);
181 self.schema.node_mut(schema_id).metadata = metadata;
182 self.schema.node_mut(schema_id).ext_types = ext_types;
183
184 self.source_map.insert(schema_id, node_id);
186 Ok(schema_id)
187 }
188
189 fn convert_content(
191 &mut self,
192 content: ParsedSchemaNodeContent,
193 ) -> Result<SchemaNodeContent, ConversionError> {
194 match content {
195 ParsedSchemaNodeContent::Any => Ok(SchemaNodeContent::Any),
196 ParsedSchemaNodeContent::Boolean => Ok(SchemaNodeContent::Boolean),
197 ParsedSchemaNodeContent::Null => Ok(SchemaNodeContent::Null),
198 ParsedSchemaNodeContent::Text(schema) => Ok(SchemaNodeContent::Text(schema)),
199 ParsedSchemaNodeContent::Reference(type_ref) => {
200 Ok(SchemaNodeContent::Reference(type_ref))
201 }
202
203 ParsedSchemaNodeContent::Integer(parsed) => Ok(SchemaNodeContent::Integer(
204 self.convert_integer_schema(parsed)?,
205 )),
206 ParsedSchemaNodeContent::Float(parsed) => {
207 Ok(SchemaNodeContent::Float(self.convert_float_schema(parsed)?))
208 }
209 ParsedSchemaNodeContent::Literal(node_id) => {
210 Ok(SchemaNodeContent::Literal(self.node_to_document(node_id)?))
211 }
212 ParsedSchemaNodeContent::Array(parsed) => {
213 Ok(SchemaNodeContent::Array(self.convert_array_schema(parsed)?))
214 }
215 ParsedSchemaNodeContent::Map(parsed) => {
216 Ok(SchemaNodeContent::Map(self.convert_map_schema(parsed)?))
217 }
218 ParsedSchemaNodeContent::Record(parsed) => Ok(SchemaNodeContent::Record(
219 self.convert_record_schema(parsed)?,
220 )),
221 ParsedSchemaNodeContent::Tuple(parsed) => {
222 Ok(SchemaNodeContent::Tuple(self.convert_tuple_schema(parsed)?))
223 }
224 ParsedSchemaNodeContent::Union(parsed) => {
225 Ok(SchemaNodeContent::Union(self.convert_union_schema(parsed)?))
226 }
227 }
228 }
229
230 fn convert_integer_schema(
232 &self,
233 parsed: ParsedIntegerSchema,
234 ) -> Result<IntegerSchema, ConversionError> {
235 let (min, max) = if let Some(range_str) = &parsed.range {
236 parse_integer_range(range_str)?
237 } else {
238 (Bound::Unbounded, Bound::Unbounded)
239 };
240
241 Ok(IntegerSchema {
242 min,
243 max,
244 multiple_of: parsed.multiple_of,
245 })
246 }
247
248 fn convert_float_schema(
250 &self,
251 parsed: ParsedFloatSchema,
252 ) -> Result<FloatSchema, ConversionError> {
253 let (min, max) = if let Some(range_str) = &parsed.range {
254 parse_float_range(range_str)?
255 } else {
256 (Bound::Unbounded, Bound::Unbounded)
257 };
258
259 let precision = match parsed.precision.as_deref() {
260 Some("f32") => FloatPrecision::F32,
261 Some("f64") | None => FloatPrecision::F64,
262 Some(other) => {
263 return Err(ConversionError::InvalidPrecision(other.to_string()));
264 }
265 };
266
267 Ok(FloatSchema {
268 min,
269 max,
270 multiple_of: parsed.multiple_of,
271 precision,
272 })
273 }
274
275 fn convert_array_schema(
277 &mut self,
278 parsed: ParsedArraySchema,
279 ) -> Result<ArraySchema, ConversionError> {
280 let item = self.convert_node(parsed.item)?;
281 let contains = parsed
282 .contains
283 .map(|id| self.convert_node(id))
284 .transpose()?;
285
286 Ok(ArraySchema {
287 item,
288 min_length: parsed.min_length,
289 max_length: parsed.max_length,
290 unique: parsed.unique,
291 contains,
292 binding_style: parsed.binding_style,
293 })
294 }
295
296 fn convert_map_schema(
298 &mut self,
299 parsed: ParsedMapSchema,
300 ) -> Result<MapSchema, ConversionError> {
301 let key = self.convert_node(parsed.key)?;
302 let value = self.convert_node(parsed.value)?;
303
304 Ok(MapSchema {
305 key,
306 value,
307 min_size: parsed.min_size,
308 max_size: parsed.max_size,
309 })
310 }
311
312 fn convert_tuple_schema(
314 &mut self,
315 parsed: ParsedTupleSchema,
316 ) -> Result<TupleSchema, ConversionError> {
317 let elements: Vec<SchemaNodeId> = parsed
318 .elements
319 .iter()
320 .map(|&id| self.convert_node(id))
321 .collect::<Result<_, _>>()?;
322
323 Ok(TupleSchema {
324 elements,
325 binding_style: parsed.binding_style,
326 })
327 }
328
329 fn convert_record_schema(
331 &mut self,
332 parsed: ParsedRecordSchema,
333 ) -> Result<RecordSchema, ConversionError> {
334 let mut properties = IndexMap::new();
335
336 for (field_name, field_parsed) in parsed.properties {
337 let schema = self.convert_node(field_parsed.schema)?;
338 properties.insert(
339 field_name,
340 RecordFieldSchema {
341 schema,
342 optional: field_parsed.optional,
343 binding_style: field_parsed.binding_style,
344 },
345 );
346 }
347
348 let flatten = parsed
350 .flatten
351 .into_iter()
352 .map(|id| self.convert_node(id))
353 .collect::<Result<Vec<_>, _>>()?;
354
355 let unknown_fields = self.convert_unknown_fields_policy(parsed.unknown_fields)?;
356
357 Ok(RecordSchema {
358 properties,
359 flatten,
360 unknown_fields,
361 })
362 }
363
364 fn convert_union_schema(
366 &mut self,
367 parsed: ParsedUnionSchema,
368 ) -> Result<UnionSchema, ConversionError> {
369 let mut variants = IndexMap::new();
370
371 for (variant_name, variant_node_id) in parsed.variants {
372 let schema = self.convert_node(variant_node_id)?;
373 variants.insert(variant_name, schema);
374 }
375
376 Ok(UnionSchema {
377 variants,
378 unambiguous: parsed.unambiguous,
379 repr: parsed.repr,
380 deny_untagged: parsed.deny_untagged,
381 })
382 }
383
384 fn convert_unknown_fields_policy(
386 &mut self,
387 parsed: ParsedUnknownFieldsPolicy,
388 ) -> Result<UnknownFieldsPolicy, ConversionError> {
389 match parsed {
390 ParsedUnknownFieldsPolicy::Deny => Ok(UnknownFieldsPolicy::Deny),
391 ParsedUnknownFieldsPolicy::Allow => Ok(UnknownFieldsPolicy::Allow),
392 ParsedUnknownFieldsPolicy::Schema(node_id) => {
393 let schema = self.convert_node(node_id)?;
394 Ok(UnknownFieldsPolicy::Schema(schema))
395 }
396 }
397 }
398
399 fn convert_metadata(
401 &mut self,
402 parsed: ParsedSchemaMetadata,
403 ) -> Result<SchemaMetadata, ConversionError> {
404 let default = parsed
405 .default
406 .map(|id| self.node_to_document(id))
407 .transpose()?;
408
409 let examples = parsed
410 .examples
411 .map(|ids| {
412 ids.into_iter()
413 .map(|id| self.node_to_document(id))
414 .collect::<Result<Vec<_>, _>>()
415 })
416 .transpose()?;
417
418 Ok(SchemaMetadata {
419 description: parsed.description,
420 deprecated: parsed.deprecated,
421 default,
422 examples,
423 })
424 }
425
426 fn convert_ext_types(
428 &mut self,
429 parsed: IndexMap<Identifier, ParsedExtTypeSchema>,
430 ) -> Result<IndexMap<Identifier, ExtTypeSchema>, ConversionError> {
431 let mut result = IndexMap::new();
432
433 for (name, parsed_schema) in parsed {
434 let schema = self.convert_node(parsed_schema.schema)?;
435 result.insert(
436 name,
437 ExtTypeSchema {
438 schema,
439 optional: parsed_schema.optional,
440 },
441 );
442 }
443
444 Ok(result)
445 }
446
447 fn node_to_document(&self, node_id: NodeId) -> Result<EureDocument, ConversionError> {
449 let mut new_doc = EureDocument::new();
450 let root_id = new_doc.get_root_id();
451 self.copy_node_to(&mut new_doc, root_id, node_id)?;
452 Ok(new_doc)
453 }
454
455 fn copy_node_to(
457 &self,
458 dest: &mut EureDocument,
459 dest_node_id: NodeId,
460 src_node_id: NodeId,
461 ) -> Result<(), ConversionError> {
462 let src_node = self.doc.node(src_node_id);
463
464 let children_to_copy: Vec<_> = match &src_node.content {
466 NodeValue::Primitive(prim) => {
467 dest.set_content(dest_node_id, NodeValue::Primitive(prim.clone()));
468 vec![]
469 }
470 NodeValue::Array(arr) => {
471 dest.set_content(dest_node_id, NodeValue::empty_array());
472 arr.to_vec()
473 }
474 NodeValue::Tuple(tup) => {
475 dest.set_content(dest_node_id, NodeValue::empty_tuple());
476 tup.to_vec()
477 }
478 NodeValue::Map(map) => {
479 dest.set_content(dest_node_id, NodeValue::empty_map());
480 map.iter()
481 .map(|(k, &v)| (k.clone(), v))
482 .collect::<Vec<_>>()
483 .into_iter()
484 .map(|(_, v)| v)
485 .collect()
486 }
487 NodeValue::Hole(_) => {
488 return Err(ConversionError::UnsupportedConstruct(
489 "Hole node".to_string(),
490 ));
491 }
492 };
493
494 let src_node = self.doc.node(src_node_id);
501 match &src_node.content {
502 NodeValue::Array(_) => {
503 for child_id in children_to_copy {
504 let new_child_id = dest
505 .add_array_element(None, dest_node_id)
506 .map_err(|e| ConversionError::UnsupportedConstruct(e.to_string()))?
507 .node_id;
508 self.copy_node_to(dest, new_child_id, child_id)?;
509 }
510 }
511 NodeValue::Tuple(_) => {
512 for (index, child_id) in children_to_copy.into_iter().enumerate() {
513 let new_child_id = dest
514 .add_tuple_element(index as u8, dest_node_id)
515 .map_err(|e| ConversionError::UnsupportedConstruct(e.to_string()))?
516 .node_id;
517 self.copy_node_to(dest, new_child_id, child_id)?;
518 }
519 }
520 NodeValue::Map(map) => {
521 for (key, &child_id) in map.iter() {
522 let new_child_id = dest
523 .add_map_child(key.clone(), dest_node_id)
524 .map_err(|e| ConversionError::UnsupportedConstruct(e.to_string()))?
525 .node_id;
526 self.copy_node_to(dest, new_child_id, child_id)?;
527 }
528 }
529 _ => {}
530 }
531
532 Ok(())
533 }
534}
535
536fn parse_integer_range(s: &str) -> Result<(Bound<BigInt>, Bound<BigInt>), ConversionError> {
538 let s = s.trim();
539
540 if s.starts_with('[') || s.starts_with('(') {
542 return parse_interval_integer(s);
543 }
544
545 if let Some(eq_pos) = s.find("..=") {
547 let left = &s[..eq_pos];
548 let right = &s[eq_pos + 3..];
549 let min = if left.is_empty() {
550 Bound::Unbounded
551 } else {
552 Bound::Inclusive(parse_bigint(left)?)
553 };
554 let max = if right.is_empty() {
555 Bound::Unbounded
556 } else {
557 Bound::Inclusive(parse_bigint(right)?)
558 };
559 Ok((min, max))
560 } else if let Some(dot_pos) = s.find("..") {
561 let left = &s[..dot_pos];
562 let right = &s[dot_pos + 2..];
563 let min = if left.is_empty() {
564 Bound::Unbounded
565 } else {
566 Bound::Inclusive(parse_bigint(left)?)
567 };
568 let max = if right.is_empty() {
569 Bound::Unbounded
570 } else {
571 Bound::Exclusive(parse_bigint(right)?)
572 };
573 Ok((min, max))
574 } else {
575 Err(ConversionError::InvalidRangeString(s.to_string()))
576 }
577}
578
579fn parse_interval_integer(s: &str) -> Result<(Bound<BigInt>, Bound<BigInt>), ConversionError> {
581 let left_inclusive = s.starts_with('[');
582 let right_inclusive = s.ends_with(']');
583
584 let inner = &s[1..s.len() - 1];
585 let parts: Vec<&str> = inner.split(',').map(|p| p.trim()).collect();
586 if parts.len() != 2 {
587 return Err(ConversionError::InvalidRangeString(s.to_string()));
588 }
589
590 let min = if parts[0].is_empty() {
591 Bound::Unbounded
592 } else if left_inclusive {
593 Bound::Inclusive(parse_bigint(parts[0])?)
594 } else {
595 Bound::Exclusive(parse_bigint(parts[0])?)
596 };
597
598 let max = if parts[1].is_empty() {
599 Bound::Unbounded
600 } else if right_inclusive {
601 Bound::Inclusive(parse_bigint(parts[1])?)
602 } else {
603 Bound::Exclusive(parse_bigint(parts[1])?)
604 };
605
606 Ok((min, max))
607}
608
609fn parse_float_range(s: &str) -> Result<(Bound<f64>, Bound<f64>), ConversionError> {
611 let s = s.trim();
612
613 if s.starts_with('[') || s.starts_with('(') {
615 return parse_interval_float(s);
616 }
617
618 if let Some(eq_pos) = s.find("..=") {
620 let left = &s[..eq_pos];
621 let right = &s[eq_pos + 3..];
622 let min = if left.is_empty() {
623 Bound::Unbounded
624 } else {
625 Bound::Inclusive(parse_f64(left)?)
626 };
627 let max = if right.is_empty() {
628 Bound::Unbounded
629 } else {
630 Bound::Inclusive(parse_f64(right)?)
631 };
632 Ok((min, max))
633 } else if let Some(dot_pos) = s.find("..") {
634 let left = &s[..dot_pos];
635 let right = &s[dot_pos + 2..];
636 let min = if left.is_empty() {
637 Bound::Unbounded
638 } else {
639 Bound::Inclusive(parse_f64(left)?)
640 };
641 let max = if right.is_empty() {
642 Bound::Unbounded
643 } else {
644 Bound::Exclusive(parse_f64(right)?)
645 };
646 Ok((min, max))
647 } else {
648 Err(ConversionError::InvalidRangeString(s.to_string()))
649 }
650}
651
652fn parse_interval_float(s: &str) -> Result<(Bound<f64>, Bound<f64>), ConversionError> {
654 let left_inclusive = s.starts_with('[');
655 let right_inclusive = s.ends_with(']');
656
657 let inner = &s[1..s.len() - 1];
658 let parts: Vec<&str> = inner.split(',').map(|p| p.trim()).collect();
659 if parts.len() != 2 {
660 return Err(ConversionError::InvalidRangeString(s.to_string()));
661 }
662
663 let min = if parts[0].is_empty() {
664 Bound::Unbounded
665 } else if left_inclusive {
666 Bound::Inclusive(parse_f64(parts[0])?)
667 } else {
668 Bound::Exclusive(parse_f64(parts[0])?)
669 };
670
671 let max = if parts[1].is_empty() {
672 Bound::Unbounded
673 } else if right_inclusive {
674 Bound::Inclusive(parse_f64(parts[1])?)
675 } else {
676 Bound::Exclusive(parse_f64(parts[1])?)
677 };
678
679 Ok((min, max))
680}
681
682fn parse_bigint(s: &str) -> Result<BigInt, ConversionError> {
683 s.parse()
684 .map_err(|_| ConversionError::InvalidRangeString(format!("Invalid integer: {}", s)))
685}
686
687fn parse_f64(s: &str) -> Result<f64, ConversionError> {
688 s.parse()
689 .map_err(|_| ConversionError::InvalidRangeString(format!("Invalid float: {}", s)))
690}
691
692pub fn document_to_schema(
726 doc: &EureDocument,
727) -> Result<(SchemaDocument, SchemaSourceMap), ConversionError> {
728 Converter::new(doc).convert()
729}
730
731#[cfg(test)]
732mod tests {
733 use super::*;
734 use crate::identifiers::{EXT_TYPE, OPTIONAL};
735 use eure_document::document::node::NodeMap;
736 use eure_document::eure;
737 use eure_document::text::Text;
738 use eure_document::value::PrimitiveValue;
739
740 fn create_schema_with_field_ext_type(ext_type_content: NodeValue) -> EureDocument {
742 let mut doc = EureDocument::new();
743 let root_id = doc.get_root_id();
744
745 let field_value_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
747 Text::inline_implicit("text"),
748 )));
749
750 let ext_type_id = doc.create_node(ext_type_content);
752 doc.node_mut(field_value_id)
753 .extensions
754 .insert(EXT_TYPE.clone(), ext_type_id);
755
756 let mut root_map = NodeMap::default();
758 root_map.insert(ObjectKey::String("name".to_string()), field_value_id);
759 doc.node_mut(root_id).content = NodeValue::Map(root_map);
760
761 doc
762 }
763
764 #[test]
765 fn extract_ext_types_not_map() {
766 let doc = create_schema_with_field_ext_type(NodeValue::Primitive(PrimitiveValue::Integer(
769 1.into(),
770 )));
771
772 let err = document_to_schema(&doc).unwrap_err();
773 use eure_document::parse::ParseErrorKind;
774 use eure_document::value::ValueKind;
775 assert_eq!(
776 err,
777 ConversionError::ParseError(ParseError {
778 node_id: NodeId(2),
779 kind: ParseErrorKind::TypeMismatch {
780 expected: ValueKind::Map,
781 actual: ValueKind::Integer,
782 }
783 })
784 );
785 }
786
787 #[test]
788 fn extract_ext_types_invalid_key() {
789 let mut doc = EureDocument::new();
792 let root_id = doc.get_root_id();
793
794 let field_value_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
796 Text::inline_implicit("text"),
797 )));
798
799 let ext_type_value_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
802 Text::inline_implicit("text"),
803 )));
804 let mut ext_type_map = NodeMap::default();
805 ext_type_map.insert(ObjectKey::Number(0.into()), ext_type_value_id);
806
807 let ext_type_id = doc.create_node(NodeValue::Map(ext_type_map));
808 doc.node_mut(field_value_id)
809 .extensions
810 .insert(EXT_TYPE.clone(), ext_type_id);
811
812 let mut root_map = NodeMap::default();
814 root_map.insert(ObjectKey::String("name".to_string()), field_value_id);
815 doc.node_mut(root_id).content = NodeValue::Map(root_map);
816
817 let err = document_to_schema(&doc).unwrap_err();
818 use eure_document::parse::ParseErrorKind;
819 assert_eq!(
820 err,
821 ConversionError::ParseError(ParseError {
822 node_id: ext_type_value_id,
824 kind: ParseErrorKind::InvalidKeyType(ObjectKey::Number(0.into()))
825 })
826 );
827 }
828
829 #[test]
830 fn extract_ext_types_invalid_optional() {
831 let mut doc = EureDocument::new();
834 let root_id = doc.get_root_id();
835
836 let field_value_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
838 Text::inline_implicit("text"),
839 )));
840
841 let ext_type_value_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
843 Text::inline_implicit("text"),
844 )));
845 let optional_node_id =
846 doc.create_node(NodeValue::Primitive(PrimitiveValue::Integer(1.into())));
847 doc.node_mut(ext_type_value_id)
848 .extensions
849 .insert(OPTIONAL.clone(), optional_node_id);
850
851 let mut ext_type_map = NodeMap::default();
853 ext_type_map.insert(ObjectKey::String("desc".to_string()), ext_type_value_id);
854
855 let ext_type_id = doc.create_node(NodeValue::Map(ext_type_map));
856 doc.node_mut(field_value_id)
857 .extensions
858 .insert(EXT_TYPE.clone(), ext_type_id);
859
860 let mut root_map = NodeMap::default();
862 root_map.insert(ObjectKey::String("name".to_string()), field_value_id);
863 doc.node_mut(root_id).content = NodeValue::Map(root_map);
864
865 let err = document_to_schema(&doc).unwrap_err();
866 use eure_document::parse::ParseErrorKind;
867 use eure_document::value::ValueKind;
868 assert_eq!(
869 err,
870 ConversionError::ParseError(ParseError {
871 node_id: NodeId(3),
872 kind: ParseErrorKind::TypeMismatch {
873 expected: ValueKind::Bool,
874 actual: ValueKind::Integer,
875 }
876 })
877 );
878 }
879
880 #[test]
881 fn literal_variant_with_inline_code() {
882 let mut doc = EureDocument::new();
886 let root_id = doc.get_root_id();
887
888 let variant_value_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
890 Text::plaintext("literal"),
891 )));
892
893 doc.node_mut(root_id).content =
896 NodeValue::Primitive(PrimitiveValue::Text(Text::inline_implicit("any")));
897
898 doc.node_mut(root_id)
900 .extensions
901 .insert("variant".parse().unwrap(), variant_value_id);
902
903 let (schema, _source_map) =
904 document_to_schema(&doc).expect("Schema conversion should succeed");
905
906 let root_content = &schema.node(schema.root).content;
908 match root_content {
909 SchemaNodeContent::Literal(doc) => {
910 match &doc.root().content {
912 NodeValue::Primitive(PrimitiveValue::Text(t)) => {
913 assert_eq!(t.as_str(), "any", "Literal should contain 'any'");
914 }
915 _ => panic!("Expected Literal with Text primitive, got {:?}", doc),
916 }
917 }
918 SchemaNodeContent::Any => {
919 panic!("BUG: Got Any instead of Literal - $variant extension not detected!");
920 }
921 other => panic!("Expected Literal, got {:?}", other),
922 }
923 }
924
925 #[test]
926 fn literal_variant_parsed_from_eure() {
927 let doc = eure!({
928 = @code("any")
929 %variant = "literal"
930 });
931
932 let (schema, _source_map) =
933 document_to_schema(&doc).expect("Schema conversion should succeed");
934
935 let root_content = &schema.node(schema.root).content;
936 match root_content {
937 SchemaNodeContent::Literal(doc) => match &doc.root().content {
938 NodeValue::Primitive(PrimitiveValue::Text(t)) => {
939 assert_eq!(t.as_str(), "any", "Literal should contain 'any'");
940 }
941 _ => panic!("Expected Literal with Text primitive, got {:?}", doc),
942 },
943 SchemaNodeContent::Any => {
944 panic!(
945 "BUG: Got Any instead of Literal - $variant extension not respected for primitive"
946 );
947 }
948 other => panic!("Expected Literal, got {:?}", other),
949 }
950 }
951
952 #[test]
953 fn union_with_literal_any_variant() {
954 let mut doc = EureDocument::new();
959 let root_id = doc.get_root_id();
960
961 let any_variant_node = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
964 Text::inline_implicit("any"),
965 )));
966 let literal_ext = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
968 Text::plaintext("literal"),
969 )));
970 doc.node_mut(any_variant_node)
971 .extensions
972 .insert("variant".parse().unwrap(), literal_ext);
973
974 let literal_variant_node = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
976 Text::inline_implicit("any"),
977 )));
978
979 let mut variants_map = NodeMap::default();
981 variants_map.insert(ObjectKey::String("any".to_string()), any_variant_node);
982 variants_map.insert(
983 ObjectKey::String("literal".to_string()),
984 literal_variant_node,
985 );
986 let variants_node = doc.create_node(NodeValue::Map(variants_map));
987
988 let union_ext = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
990 Text::plaintext("union"),
991 )));
992 let untagged_ext = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(
993 Text::plaintext("untagged"),
994 )));
995
996 let mut root_map = NodeMap::default();
998 root_map.insert(ObjectKey::String("variants".to_string()), variants_node);
999
1000 doc.node_mut(root_id).content = NodeValue::Map(root_map);
1001 doc.node_mut(root_id)
1002 .extensions
1003 .insert("variant".parse().unwrap(), union_ext);
1004 doc.node_mut(root_id)
1005 .extensions
1006 .insert("variant-repr".parse().unwrap(), untagged_ext);
1007
1008 let (schema, _source_map) =
1009 document_to_schema(&doc).expect("Schema conversion should succeed");
1010
1011 let root_content = &schema.node(schema.root).content;
1013 match root_content {
1014 SchemaNodeContent::Union(union_schema) => {
1015 let any_variant_id = union_schema
1017 .variants
1018 .get("any")
1019 .expect("'any' variant missing");
1020 let any_content = &schema.node(*any_variant_id).content;
1021 match any_content {
1022 SchemaNodeContent::Literal(doc) => match &doc.root().content {
1023 NodeValue::Primitive(PrimitiveValue::Text(t)) => {
1024 assert_eq!(
1025 t.as_str(),
1026 "any",
1027 "'any' variant should be Literal(\"any\")"
1028 );
1029 }
1030 _ => panic!("'any' variant: expected Text, got {:?}", doc),
1031 },
1032 SchemaNodeContent::Any => {
1033 panic!(
1034 "BUG: 'any' variant is Any instead of Literal(\"any\") - $variant extension not detected!"
1035 );
1036 }
1037 other => panic!("'any' variant: expected Literal, got {:?}", other),
1038 }
1039
1040 let literal_variant_id = union_schema
1042 .variants
1043 .get("literal")
1044 .expect("'literal' variant missing");
1045 let literal_content = &schema.node(*literal_variant_id).content;
1046 match literal_content {
1047 SchemaNodeContent::Any => {
1048 }
1050 other => panic!("'literal' variant: expected Any, got {:?}", other),
1051 }
1052 }
1053 other => panic!("Expected Union, got {:?}", other),
1054 }
1055 }
1056}