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