1use crate::error::Details;
19use crate::schema::{
20 DecimalSchema, EnumSchema, FixedSchema, InnerDecimalSchema, NamesRef, NamespaceRef,
21 RecordSchema, UnionSchema, UuidSchema,
22};
23use crate::{AvroResult, Error, Schema};
24use std::collections::HashMap;
25
26#[derive(Debug, Clone)]
27pub struct ResolvedSchema<'s> {
28 pub(super) names_ref: NamesRef<'s>,
29 schemata: Vec<&'s Schema>,
30}
31
32impl<'s> ResolvedSchema<'s> {
33 pub fn get_schemata(&self) -> &[&'s Schema] {
34 &self.schemata
35 }
36
37 pub fn get_names(&self) -> &NamesRef<'s> {
38 &self.names_ref
39 }
40
41 pub fn new(schema: &'s Schema) -> AvroResult<Self> {
45 Self::new_with_schemata(vec![schema])
46 }
47
48 pub fn new_with_schemata(schemata: Vec<&'s Schema>) -> AvroResult<Self> {
54 Self::new_with_known_schemata(schemata, None, &HashMap::new())
55 }
56
57 pub fn new_with_known_schemata<'n>(
61 schemata_to_resolve: Vec<&'s Schema>,
62 enclosing_namespace: NamespaceRef,
63 known_schemata: &'n NamesRef<'n>,
64 ) -> AvroResult<Self> {
65 let mut names = HashMap::new();
66 resolve_names_with_schemata(
67 schemata_to_resolve.iter().copied(),
68 &mut names,
69 enclosing_namespace,
70 known_schemata,
71 )?;
72 Ok(ResolvedSchema {
73 names_ref: names,
74 schemata: schemata_to_resolve,
75 })
76 }
77}
78
79impl<'s> TryFrom<&'s Schema> for ResolvedSchema<'s> {
80 type Error = Error;
81
82 fn try_from(schema: &'s Schema) -> AvroResult<Self> {
83 Self::new(schema)
84 }
85}
86
87impl<'s> TryFrom<Vec<&'s Schema>> for ResolvedSchema<'s> {
88 type Error = Error;
89
90 fn try_from(schemata: Vec<&'s Schema>) -> AvroResult<Self> {
91 Self::new_with_schemata(schemata)
92 }
93}
94
95#[ouroboros::self_referencing]
100struct InnerResolvedOwnedSchema {
101 root_schema: Schema,
102 #[borrows(root_schema)]
103 #[covariant]
104 names: NamesRef<'this>,
105}
106
107pub struct ResolvedOwnedSchema {
109 inner: InnerResolvedOwnedSchema,
110}
111
112impl ResolvedOwnedSchema {
113 pub fn new(root_schema: Schema) -> AvroResult<Self> {
114 Ok(Self {
115 inner: InnerResolvedOwnedSchemaTryBuilder {
116 root_schema,
117 names_builder: |schema: &Schema| {
118 let mut names = HashMap::new();
119 resolve_names(schema, &mut names, None, &HashMap::new())?;
120 Ok::<_, Error>(names)
121 },
122 }
123 .try_build()?,
124 })
125 }
126
127 pub fn get_root_schema(&self) -> &Schema {
128 self.inner.borrow_root_schema()
129 }
130 pub fn get_names(&self) -> &NamesRef<'_> {
131 self.inner.borrow_names()
132 }
133}
134
135impl TryFrom<Schema> for ResolvedOwnedSchema {
136 type Error = Error;
137
138 fn try_from(schema: Schema) -> AvroResult<Self> {
139 Self::new(schema)
140 }
141}
142
143pub fn resolve_names<'s, 'n>(
147 schema: &'s Schema,
148 names: &mut NamesRef<'s>,
149 enclosing_namespace: NamespaceRef,
150 known_schemata: &NamesRef<'n>,
151) -> AvroResult<()> {
152 match schema {
153 Schema::Array(schema) => {
154 resolve_names(&schema.items, names, enclosing_namespace, known_schemata)
155 }
156 Schema::Map(schema) => {
157 resolve_names(&schema.types, names, enclosing_namespace, known_schemata)
158 }
159 Schema::Union(UnionSchema { schemas, .. }) => {
160 for schema in schemas {
161 resolve_names(schema, names, enclosing_namespace, known_schemata)?
162 }
163 Ok(())
164 }
165 Schema::Enum(EnumSchema { name, .. })
166 | Schema::Fixed(FixedSchema { name, .. })
167 | Schema::Uuid(UuidSchema::Fixed(FixedSchema { name, .. }))
168 | Schema::Decimal(DecimalSchema {
169 inner: InnerDecimalSchema::Fixed(FixedSchema { name, .. }),
170 ..
171 })
172 | Schema::Duration(FixedSchema { name, .. }) => {
173 let fully_qualified_name = name.fully_qualified_name(enclosing_namespace).into_owned();
174 if names.contains_key(&fully_qualified_name)
175 || known_schemata.contains_key(&fully_qualified_name)
176 {
177 Err(Details::AmbiguousSchemaDefinition(fully_qualified_name).into())
178 } else {
179 names.insert(fully_qualified_name, schema);
180 Ok(())
181 }
182 }
183 Schema::Record(RecordSchema { name, fields, .. }) => {
184 let fully_qualified_name = name.fully_qualified_name(enclosing_namespace).into_owned();
185 if names.contains_key(&fully_qualified_name)
186 || known_schemata.contains_key(&fully_qualified_name)
187 {
188 Err(Details::AmbiguousSchemaDefinition(fully_qualified_name).into())
189 } else {
190 let record_namespace = fully_qualified_name.namespace().map(ToString::to_string);
191 names.insert(fully_qualified_name, schema);
192 for field in fields {
193 resolve_names(
194 &field.schema,
195 names,
196 record_namespace.as_deref(),
197 known_schemata,
198 )?
199 }
200 Ok(())
201 }
202 }
203 Schema::Ref { name } => {
204 let fully_qualified_name = name.fully_qualified_name(enclosing_namespace);
205 if names.contains_key(&fully_qualified_name)
206 || known_schemata.contains_key(&fully_qualified_name)
207 {
208 Ok(())
209 } else {
210 Err(Details::SchemaResolutionError(fully_qualified_name.into_owned()).into())
211 }
212 }
213 _ => Ok(()),
214 }
215}
216
217pub fn resolve_names_with_schemata<'s, 'n>(
218 schemata: impl IntoIterator<Item = &'s Schema>,
219 names: &mut NamesRef<'s>,
220 enclosing_namespace: NamespaceRef,
221 known_schemata: &NamesRef<'n>,
222) -> AvroResult<()> {
223 for schema in schemata {
224 resolve_names(schema, names, enclosing_namespace, known_schemata)?;
225 }
226 Ok(())
227}
228
229#[cfg(test)]
230mod tests {
231 use super::{ResolvedOwnedSchema, ResolvedSchema};
232 use crate::{
233 Schema,
234 schema::{Name, NamesRef},
235 };
236 use apache_avro_test_helper::TestResult;
237 use std::collections::HashMap;
238
239 #[test]
240 fn avro_3448_test_proper_resolution_inner_record_inherited_namespace() -> TestResult {
241 let schema = r#"
242 {
243 "name": "record_name",
244 "namespace": "space",
245 "type": "record",
246 "fields": [
247 {
248 "name": "outer_field_1",
249 "type": [
250 "null",
251 {
252 "type":"record",
253 "name":"inner_record_name",
254 "fields":[
255 {
256 "name":"inner_field_1",
257 "type":"double"
258 }
259 ]
260 }
261 ]
262 },
263 {
264 "name": "outer_field_2",
265 "type" : "inner_record_name"
266 }
267 ]
268 }
269 "#;
270 let schema = Schema::parse_str(schema)?;
271 let rs = ResolvedSchema::new(&schema)?;
272 assert_eq!(rs.get_names().len(), 2);
273 for s in ["space.record_name", "space.inner_record_name"] {
274 assert!(rs.get_names().contains_key(&Name::new(s)?));
275 }
276
277 Ok(())
278 }
279
280 #[test]
281 fn avro_3448_test_proper_resolution_inner_record_qualified_namespace() -> TestResult {
282 let schema = r#"
283 {
284 "name": "record_name",
285 "namespace": "space",
286 "type": "record",
287 "fields": [
288 {
289 "name": "outer_field_1",
290 "type": [
291 "null",
292 {
293 "type":"record",
294 "name":"inner_record_name",
295 "fields":[
296 {
297 "name":"inner_field_1",
298 "type":"double"
299 }
300 ]
301 }
302 ]
303 },
304 {
305 "name": "outer_field_2",
306 "type" : "space.inner_record_name"
307 }
308 ]
309 }
310 "#;
311 let schema = Schema::parse_str(schema)?;
312 let rs = ResolvedSchema::new(&schema)?;
313 assert_eq!(rs.get_names().len(), 2);
314 for s in ["space.record_name", "space.inner_record_name"] {
315 assert!(rs.get_names().contains_key(&Name::new(s)?));
316 }
317
318 Ok(())
319 }
320
321 #[test]
322 fn avro_3448_test_proper_resolution_inner_enum_inherited_namespace() -> TestResult {
323 let schema = r#"
324 {
325 "name": "record_name",
326 "namespace": "space",
327 "type": "record",
328 "fields": [
329 {
330 "name": "outer_field_1",
331 "type": [
332 "null",
333 {
334 "type":"enum",
335 "name":"inner_enum_name",
336 "symbols":["Extensive","Testing"]
337 }
338 ]
339 },
340 {
341 "name": "outer_field_2",
342 "type" : "inner_enum_name"
343 }
344 ]
345 }
346 "#;
347 let schema = Schema::parse_str(schema)?;
348 let rs = ResolvedSchema::new(&schema)?;
349 assert_eq!(rs.get_names().len(), 2);
350 for s in ["space.record_name", "space.inner_enum_name"] {
351 assert!(rs.get_names().contains_key(&Name::new(s)?));
352 }
353
354 Ok(())
355 }
356
357 #[test]
358 fn avro_3448_test_proper_resolution_inner_enum_qualified_namespace() -> TestResult {
359 let schema = r#"
360 {
361 "name": "record_name",
362 "namespace": "space",
363 "type": "record",
364 "fields": [
365 {
366 "name": "outer_field_1",
367 "type": [
368 "null",
369 {
370 "type":"enum",
371 "name":"inner_enum_name",
372 "symbols":["Extensive","Testing"]
373 }
374 ]
375 },
376 {
377 "name": "outer_field_2",
378 "type" : "space.inner_enum_name"
379 }
380 ]
381 }
382 "#;
383 let schema = Schema::parse_str(schema)?;
384 let rs = ResolvedSchema::new(&schema)?;
385 assert_eq!(rs.get_names().len(), 2);
386 for s in ["space.record_name", "space.inner_enum_name"] {
387 assert!(rs.get_names().contains_key(&Name::new(s)?));
388 }
389
390 Ok(())
391 }
392
393 #[test]
394 fn avro_3448_test_proper_resolution_inner_fixed_inherited_namespace() -> TestResult {
395 let schema = r#"
396 {
397 "name": "record_name",
398 "namespace": "space",
399 "type": "record",
400 "fields": [
401 {
402 "name": "outer_field_1",
403 "type": [
404 "null",
405 {
406 "type":"fixed",
407 "name":"inner_fixed_name",
408 "size": 16
409 }
410 ]
411 },
412 {
413 "name": "outer_field_2",
414 "type" : "inner_fixed_name"
415 }
416 ]
417 }
418 "#;
419 let schema = Schema::parse_str(schema)?;
420 let rs = ResolvedSchema::new(&schema)?;
421 assert_eq!(rs.get_names().len(), 2);
422 for s in ["space.record_name", "space.inner_fixed_name"] {
423 assert!(rs.get_names().contains_key(&Name::new(s)?));
424 }
425
426 Ok(())
427 }
428
429 #[test]
430 fn avro_3448_test_proper_resolution_inner_fixed_qualified_namespace() -> TestResult {
431 let schema = r#"
432 {
433 "name": "record_name",
434 "namespace": "space",
435 "type": "record",
436 "fields": [
437 {
438 "name": "outer_field_1",
439 "type": [
440 "null",
441 {
442 "type":"fixed",
443 "name":"inner_fixed_name",
444 "size": 16
445 }
446 ]
447 },
448 {
449 "name": "outer_field_2",
450 "type" : "space.inner_fixed_name"
451 }
452 ]
453 }
454 "#;
455 let schema = Schema::parse_str(schema)?;
456 let rs = ResolvedSchema::new(&schema)?;
457 assert_eq!(rs.get_names().len(), 2);
458 for s in ["space.record_name", "space.inner_fixed_name"] {
459 assert!(rs.get_names().contains_key(&Name::new(s)?));
460 }
461
462 Ok(())
463 }
464
465 #[test]
466 fn avro_3448_test_proper_resolution_inner_record_inner_namespace() -> TestResult {
467 let schema = r#"
468 {
469 "name": "record_name",
470 "namespace": "space",
471 "type": "record",
472 "fields": [
473 {
474 "name": "outer_field_1",
475 "type": [
476 "null",
477 {
478 "type":"record",
479 "name":"inner_record_name",
480 "namespace":"inner_space",
481 "fields":[
482 {
483 "name":"inner_field_1",
484 "type":"double"
485 }
486 ]
487 }
488 ]
489 },
490 {
491 "name": "outer_field_2",
492 "type" : "inner_space.inner_record_name"
493 }
494 ]
495 }
496 "#;
497 let schema = Schema::parse_str(schema)?;
498 let rs = ResolvedSchema::new(&schema)?;
499 assert_eq!(rs.get_names().len(), 2);
500 for s in ["space.record_name", "inner_space.inner_record_name"] {
501 assert!(rs.get_names().contains_key(&Name::new(s)?));
502 }
503
504 Ok(())
505 }
506
507 #[test]
508 fn avro_3448_test_proper_resolution_inner_enum_inner_namespace() -> TestResult {
509 let schema = r#"
510 {
511 "name": "record_name",
512 "namespace": "space",
513 "type": "record",
514 "fields": [
515 {
516 "name": "outer_field_1",
517 "type": [
518 "null",
519 {
520 "type":"enum",
521 "name":"inner_enum_name",
522 "namespace": "inner_space",
523 "symbols":["Extensive","Testing"]
524 }
525 ]
526 },
527 {
528 "name": "outer_field_2",
529 "type" : "inner_space.inner_enum_name"
530 }
531 ]
532 }
533 "#;
534 let schema = Schema::parse_str(schema)?;
535 let rs = ResolvedSchema::new(&schema)?;
536 assert_eq!(rs.get_names().len(), 2);
537 for s in ["space.record_name", "inner_space.inner_enum_name"] {
538 assert!(rs.get_names().contains_key(&Name::new(s)?));
539 }
540
541 Ok(())
542 }
543
544 #[test]
545 fn avro_3448_test_proper_resolution_inner_fixed_inner_namespace() -> TestResult {
546 let schema = r#"
547 {
548 "name": "record_name",
549 "namespace": "space",
550 "type": "record",
551 "fields": [
552 {
553 "name": "outer_field_1",
554 "type": [
555 "null",
556 {
557 "type":"fixed",
558 "name":"inner_fixed_name",
559 "namespace": "inner_space",
560 "size": 16
561 }
562 ]
563 },
564 {
565 "name": "outer_field_2",
566 "type" : "inner_space.inner_fixed_name"
567 }
568 ]
569 }
570 "#;
571 let schema = Schema::parse_str(schema)?;
572 let rs = ResolvedSchema::new(&schema)?;
573 assert_eq!(rs.get_names().len(), 2);
574 for s in ["space.record_name", "inner_space.inner_fixed_name"] {
575 assert!(rs.get_names().contains_key(&Name::new(s)?));
576 }
577
578 Ok(())
579 }
580
581 #[test]
582 fn avro_3448_test_proper_multi_level_resolution_inner_record_outer_namespace() -> TestResult {
583 let schema = r#"
584 {
585 "name": "record_name",
586 "namespace": "space",
587 "type": "record",
588 "fields": [
589 {
590 "name": "outer_field_1",
591 "type": [
592 "null",
593 {
594 "type":"record",
595 "name":"middle_record_name",
596 "fields":[
597 {
598 "name":"middle_field_1",
599 "type":[
600 "null",
601 {
602 "type":"record",
603 "name":"inner_record_name",
604 "fields":[
605 {
606 "name":"inner_field_1",
607 "type":"double"
608 }
609 ]
610 }
611 ]
612 }
613 ]
614 }
615 ]
616 },
617 {
618 "name": "outer_field_2",
619 "type" : "space.inner_record_name"
620 }
621 ]
622 }
623 "#;
624 let schema = Schema::parse_str(schema)?;
625 let rs = ResolvedSchema::new(&schema)?;
626 assert_eq!(rs.get_names().len(), 3);
627 for s in [
628 "space.record_name",
629 "space.middle_record_name",
630 "space.inner_record_name",
631 ] {
632 assert!(rs.get_names().contains_key(&Name::new(s)?));
633 }
634
635 Ok(())
636 }
637
638 #[test]
639 fn avro_3448_test_proper_multi_level_resolution_inner_record_middle_namespace() -> TestResult {
640 let schema = r#"
641 {
642 "name": "record_name",
643 "namespace": "space",
644 "type": "record",
645 "fields": [
646 {
647 "name": "outer_field_1",
648 "type": [
649 "null",
650 {
651 "type":"record",
652 "name":"middle_record_name",
653 "namespace":"middle_namespace",
654 "fields":[
655 {
656 "name":"middle_field_1",
657 "type":[
658 "null",
659 {
660 "type":"record",
661 "name":"inner_record_name",
662 "fields":[
663 {
664 "name":"inner_field_1",
665 "type":"double"
666 }
667 ]
668 }
669 ]
670 }
671 ]
672 }
673 ]
674 },
675 {
676 "name": "outer_field_2",
677 "type" : "middle_namespace.inner_record_name"
678 }
679 ]
680 }
681 "#;
682 let schema = Schema::parse_str(schema)?;
683 let rs = ResolvedSchema::new(&schema)?;
684 assert_eq!(rs.get_names().len(), 3);
685 for s in [
686 "space.record_name",
687 "middle_namespace.middle_record_name",
688 "middle_namespace.inner_record_name",
689 ] {
690 assert!(rs.get_names().contains_key(&Name::new(s)?));
691 }
692
693 Ok(())
694 }
695
696 #[test]
697 fn avro_3448_test_proper_multi_level_resolution_inner_record_inner_namespace() -> TestResult {
698 let schema = r#"
699 {
700 "name": "record_name",
701 "namespace": "space",
702 "type": "record",
703 "fields": [
704 {
705 "name": "outer_field_1",
706 "type": [
707 "null",
708 {
709 "type":"record",
710 "name":"middle_record_name",
711 "namespace":"middle_namespace",
712 "fields":[
713 {
714 "name":"middle_field_1",
715 "type":[
716 "null",
717 {
718 "type":"record",
719 "name":"inner_record_name",
720 "namespace":"inner_namespace",
721 "fields":[
722 {
723 "name":"inner_field_1",
724 "type":"double"
725 }
726 ]
727 }
728 ]
729 }
730 ]
731 }
732 ]
733 },
734 {
735 "name": "outer_field_2",
736 "type" : "inner_namespace.inner_record_name"
737 }
738 ]
739 }
740 "#;
741 let schema = Schema::parse_str(schema)?;
742 let rs = ResolvedSchema::new(&schema)?;
743 assert_eq!(rs.get_names().len(), 3);
744 for s in [
745 "space.record_name",
746 "middle_namespace.middle_record_name",
747 "inner_namespace.inner_record_name",
748 ] {
749 assert!(rs.get_names().contains_key(&Name::new(s)?));
750 }
751
752 Ok(())
753 }
754
755 #[test]
756 fn avro_3448_test_proper_in_array_resolution_inherited_namespace() -> TestResult {
757 let schema = r#"
758 {
759 "name": "record_name",
760 "namespace": "space",
761 "type": "record",
762 "fields": [
763 {
764 "name": "outer_field_1",
765 "type": {
766 "type":"array",
767 "items":{
768 "type":"record",
769 "name":"in_array_record",
770 "fields": [
771 {
772 "name":"array_record_field",
773 "type":"string"
774 }
775 ]
776 }
777 }
778 },
779 {
780 "name":"outer_field_2",
781 "type":"in_array_record"
782 }
783 ]
784 }
785 "#;
786 let schema = Schema::parse_str(schema)?;
787 let rs = ResolvedSchema::new(&schema)?;
788 assert_eq!(rs.get_names().len(), 2);
789 for s in ["space.record_name", "space.in_array_record"] {
790 assert!(rs.get_names().contains_key(&Name::new(s)?));
791 }
792
793 Ok(())
794 }
795
796 #[test]
797 fn avro_3448_test_proper_in_map_resolution_inherited_namespace() -> TestResult {
798 let schema = r#"
799 {
800 "name": "record_name",
801 "namespace": "space",
802 "type": "record",
803 "fields": [
804 {
805 "name": "outer_field_1",
806 "type": {
807 "type":"map",
808 "values":{
809 "type":"record",
810 "name":"in_map_record",
811 "fields": [
812 {
813 "name":"map_record_field",
814 "type":"string"
815 }
816 ]
817 }
818 }
819 },
820 {
821 "name":"outer_field_2",
822 "type":"in_map_record"
823 }
824 ]
825 }
826 "#;
827 let schema = Schema::parse_str(schema)?;
828 let rs = ResolvedSchema::new(&schema)?;
829 assert_eq!(rs.get_names().len(), 2);
830 for s in ["space.record_name", "space.in_map_record"] {
831 assert!(rs.get_names().contains_key(&Name::new(s)?));
832 }
833
834 Ok(())
835 }
836
837 #[test]
838 fn avro_3466_test_to_json_inner_enum_inner_namespace() -> TestResult {
839 let schema = r#"
840 {
841 "name": "record_name",
842 "namespace": "space",
843 "type": "record",
844 "fields": [
845 {
846 "name": "outer_field_1",
847 "type": [
848 "null",
849 {
850 "type":"enum",
851 "name":"inner_enum_name",
852 "namespace": "inner_space",
853 "symbols":["Extensive","Testing"]
854 }
855 ]
856 },
857 {
858 "name": "outer_field_2",
859 "type" : "inner_space.inner_enum_name"
860 }
861 ]
862 }
863 "#;
864 let schema = Schema::parse_str(schema)?;
865 let rs = ResolvedSchema::new(&schema)?;
866
867 assert_eq!(rs.get_names().len(), 2);
869 for s in ["space.record_name", "inner_space.inner_enum_name"] {
870 assert!(rs.get_names().contains_key(&Name::new(s)?));
871 }
872
873 let schema_str = serde_json::to_string(&schema)?;
875 let _schema = Schema::parse_str(&schema_str)?;
876 assert_eq!(schema, _schema);
877
878 Ok(())
879 }
880
881 #[test]
882 fn avro_3466_test_to_json_inner_fixed_inner_namespace() -> TestResult {
883 let schema = r#"
884 {
885 "name": "record_name",
886 "namespace": "space",
887 "type": "record",
888 "fields": [
889 {
890 "name": "outer_field_1",
891 "type": [
892 "null",
893 {
894 "type":"fixed",
895 "name":"inner_fixed_name",
896 "namespace": "inner_space",
897 "size":54
898 }
899 ]
900 },
901 {
902 "name": "outer_field_2",
903 "type" : "inner_space.inner_fixed_name"
904 }
905 ]
906 }
907 "#;
908 let schema = Schema::parse_str(schema)?;
909 let rs = ResolvedSchema::new(&schema)?;
910
911 assert_eq!(rs.get_names().len(), 2);
913 for s in ["space.record_name", "inner_space.inner_fixed_name"] {
914 assert!(rs.get_names().contains_key(&Name::new(s)?));
915 }
916
917 let schema_str = serde_json::to_string(&schema)?;
919 let _schema = Schema::parse_str(&schema_str)?;
920 assert_eq!(schema, _schema);
921
922 Ok(())
923 }
924
925 #[test]
926 fn avro_rs_339_schema_ref_uuid() -> TestResult {
927 let schema = Schema::parse_str(
928 r#"{
929 "name": "foo",
930 "type": "record",
931 "fields": [
932 {
933 "name": "a",
934 "type": {
935 "type": "fixed",
936 "size": 16,
937 "logicalType": "uuid",
938 "name": "bar"
939 }
940 },
941 {
942 "name": "b",
943 "type": "bar"
944 }
945 ]
946 }"#,
947 )?;
948 let _resolved = ResolvedSchema::new(&schema)?;
949 let _resolved_owned = ResolvedOwnedSchema::try_from(schema)?;
950
951 Ok(())
952 }
953
954 #[test]
955 fn avro_rs_339_schema_ref_decimal() -> TestResult {
956 let schema = Schema::parse_str(
957 r#"{
958 "name": "foo",
959 "type": "record",
960 "fields": [
961 {
962 "name": "a",
963 "type": {
964 "type": "fixed",
965 "size": 16,
966 "logicalType": "decimal",
967 "precision": 4,
968 "scale": 2,
969 "name": "bar"
970 }
971 },
972 {
973 "name": "b",
974 "type": "bar"
975 }
976 ]
977 }"#,
978 )?;
979 let _resolved = ResolvedSchema::new(&schema)?;
980 let _resolved_owned = ResolvedOwnedSchema::try_from(schema)?;
981
982 Ok(())
983 }
984
985 #[test]
986 fn avro_rs_444_do_not_allow_duplicate_names_in_known_schemata() -> TestResult {
987 let schema = Schema::parse_str(
988 r#"{
989 "name": "foo",
990 "type": "record",
991 "fields": [
992 {
993 "name": "a",
994 "type": {
995 "type": "fixed",
996 "size": 16,
997 "logicalType": "decimal",
998 "precision": 4,
999 "scale": 2,
1000 "name": "bar"
1001 }
1002 },
1003 {
1004 "name": "b",
1005 "type": "bar"
1006 },
1007 {
1008 "name": "c",
1009 "type": {
1010 "type": "fixed",
1011 "size": 16,
1012 "logicalType": "uuid",
1013 "name": "duplicated_name"
1014 }
1015 }
1016 ]
1017 }"#,
1018 )?;
1019
1020 let mut known_schemata: NamesRef = HashMap::default();
1021 known_schemata.insert("duplicated_name".try_into()?, &Schema::Boolean);
1022
1023 let result = ResolvedSchema::new_with_known_schemata(vec![&schema], None, &known_schemata)
1024 .unwrap_err();
1025
1026 assert_eq!(
1027 result.to_string(),
1028 "Two named schema defined for same fullname: duplicated_name."
1029 );
1030
1031 Ok(())
1032 }
1033}