1use std::collections::{BTreeMap, BTreeSet};
2
3use lookup::{
4 OwnedTargetPath, OwnedValuePath, PathPrefix, lookup_v2::TargetPath, owned_value_path,
5};
6use vrl::value::{Kind, kind::Collection};
7
8use crate::config::{LegacyKey, LogNamespace, log_schema};
9
10#[derive(Clone, Debug, PartialEq, PartialOrd)]
15pub struct Definition {
16 event_kind: Kind,
18
19 metadata_kind: Kind,
21
22 meaning: BTreeMap<String, MeaningPointer>,
27
28 log_namespaces: BTreeSet<LogNamespace>,
32}
33
34#[derive(Clone, Debug, PartialEq, PartialOrd)]
44enum MeaningPointer {
45 Valid(OwnedTargetPath),
46 Invalid(BTreeSet<OwnedTargetPath>),
47}
48
49impl MeaningPointer {
50 fn merge(self, other: Self) -> Self {
51 let set = match (self, other) {
52 (Self::Valid(lhs), Self::Valid(rhs)) if lhs == rhs => return Self::Valid(lhs),
53 (Self::Valid(lhs), Self::Valid(rhs)) => BTreeSet::from([lhs, rhs]),
54 (Self::Valid(lhs), Self::Invalid(mut rhs)) => {
55 rhs.insert(lhs);
56 rhs
57 }
58 (Self::Invalid(mut lhs), Self::Valid(rhs)) => {
59 lhs.insert(rhs);
60 lhs
61 }
62 (Self::Invalid(mut lhs), Self::Invalid(rhs)) => {
63 lhs.extend(rhs);
64 lhs
65 }
66 };
67
68 Self::Invalid(set)
69 }
70}
71
72impl Definition {
73 pub fn any() -> Self {
75 Self {
76 event_kind: Kind::any(),
77 metadata_kind: Kind::any(),
78 meaning: BTreeMap::default(),
79 log_namespaces: [LogNamespace::Legacy, LogNamespace::Vector].into(),
80 }
81 }
82
83 pub fn new_with_default_metadata(
87 event_kind: Kind,
88 log_namespaces: impl Into<BTreeSet<LogNamespace>>,
89 ) -> Self {
90 Self {
91 event_kind,
92 metadata_kind: Kind::object(Collection::any()),
93 meaning: BTreeMap::default(),
94 log_namespaces: log_namespaces.into(),
95 }
96 }
97
98 pub fn new(
102 event_kind: Kind,
103 metadata_kind: Kind,
104 log_namespaces: impl Into<BTreeSet<LogNamespace>>,
105 ) -> Self {
106 Self {
107 event_kind,
108 metadata_kind,
109 meaning: BTreeMap::default(),
110 log_namespaces: log_namespaces.into(),
111 }
112 }
113
114 pub fn default_legacy_namespace() -> Self {
117 Self::new_with_default_metadata(Kind::any_object(), [LogNamespace::Legacy])
118 }
119
120 pub fn empty_legacy_namespace() -> Self {
123 Self::new_with_default_metadata(Kind::object(Collection::empty()), [LogNamespace::Legacy])
124 }
125
126 pub fn default_for_namespace(log_namespaces: &BTreeSet<LogNamespace>) -> Self {
129 let is_legacy = log_namespaces.contains(&LogNamespace::Legacy);
130 let is_vector = log_namespaces.contains(&LogNamespace::Vector);
131 match (is_legacy, is_vector) {
132 (false, false) => Self::new_with_default_metadata(Kind::any(), []),
133 (true, false) => Self::default_legacy_namespace(),
134 (false, true) => Self::new_with_default_metadata(Kind::any(), [LogNamespace::Vector]),
135 (true, true) => Self::any(),
136 }
137 }
138
139 pub fn log_namespaces(&self) -> &BTreeSet<LogNamespace> {
141 &self.log_namespaces
142 }
143
144 #[must_use]
147 pub fn with_standard_vector_source_metadata(self) -> Self {
148 self.with_vector_metadata(
149 log_schema().source_type_key(),
150 &owned_value_path!("source_type"),
151 Kind::bytes(),
152 None,
153 )
154 .with_vector_metadata(
155 log_schema().timestamp_key(),
156 &owned_value_path!("ingest_timestamp"),
157 Kind::timestamp(),
158 None,
159 )
160 }
161
162 #[must_use]
167 pub fn with_source_metadata(
168 self,
169 source_name: &str,
170 legacy_path: Option<LegacyKey<OwnedValuePath>>,
171 vector_path: &OwnedValuePath,
172 kind: Kind,
173 meaning: Option<&str>,
174 ) -> Self {
175 self.with_namespaced_metadata(source_name, legacy_path, vector_path, kind, meaning)
176 }
177
178 #[must_use]
183 pub fn with_vector_metadata(
184 self,
185 legacy_path: Option<&OwnedValuePath>,
186 vector_path: &OwnedValuePath,
187 kind: Kind,
188 meaning: Option<&str>,
189 ) -> Self {
190 self.with_namespaced_metadata(
191 "vector",
192 legacy_path.cloned().map(LegacyKey::InsertIfEmpty),
193 vector_path,
194 kind,
195 meaning,
196 )
197 }
198
199 fn with_namespaced_metadata(
202 self,
203 prefix: &str,
204 legacy_path: Option<LegacyKey<OwnedValuePath>>,
205 vector_path: &OwnedValuePath,
206 kind: Kind,
207 meaning: Option<&str>,
208 ) -> Self {
209 let legacy_definition = legacy_path.and_then(|legacy_path| {
210 if self.log_namespaces.contains(&LogNamespace::Legacy) {
211 match legacy_path {
212 LegacyKey::InsertIfEmpty(legacy_path) => Some(self.clone().try_with_field(
213 &legacy_path,
214 kind.clone(),
215 meaning,
216 )),
217 LegacyKey::Overwrite(legacy_path) => Some(self.clone().with_event_field(
218 &legacy_path,
219 kind.clone(),
220 meaning,
221 )),
222 }
223 } else {
224 None
225 }
226 });
227
228 let vector_definition = if self.log_namespaces.contains(&LogNamespace::Vector) {
229 Some(self.clone().with_metadata_field(
230 &vector_path.with_field_prefix(prefix),
231 kind,
232 meaning,
233 ))
234 } else {
235 None
236 };
237
238 match (legacy_definition, vector_definition) {
239 (Some(a), Some(b)) => a.merge(b),
240 (Some(x), _) | (_, Some(x)) => x,
241 (None, None) => self,
242 }
243 }
244
245 #[must_use]
252 pub fn with_field(
253 self,
254 target_path: &OwnedTargetPath,
255 kind: Kind,
256 meaning: Option<&str>,
257 ) -> Self {
258 match target_path.prefix {
259 PathPrefix::Event => self.with_event_field(&target_path.path, kind, meaning),
260 PathPrefix::Metadata => self.with_metadata_field(&target_path.path, kind, meaning),
261 }
262 }
263
264 #[must_use]
272 pub fn with_event_field(
273 mut self,
274 path: &OwnedValuePath,
275 kind: Kind,
276 meaning: Option<&str>,
277 ) -> Self {
278 if !path.is_root() {
279 assert!(
280 self.event_kind.as_object().is_some(),
281 "Setting a field on a value that cannot be an object"
282 );
283 }
284
285 self.event_kind.set_at_path(path, kind);
286
287 if let Some(meaning) = meaning {
288 self.meaning.insert(
289 meaning.to_owned(),
290 MeaningPointer::Valid(OwnedTargetPath::event(path.clone())),
291 );
292 }
293
294 self
295 }
296
297 #[must_use]
300 pub fn try_with_field(
301 mut self,
302 path: &OwnedValuePath,
303 kind: Kind,
304 meaning: Option<&str>,
305 ) -> Self {
306 let existing_type = self.event_kind.at_path(path);
307
308 if existing_type.is_undefined() {
309 self.with_event_field(path, kind, meaning)
311 } else if !existing_type.contains_undefined() {
312 self
314 } else {
315 let success_definition = self.clone().with_event_field(path, kind, None);
319 self.event_kind
321 .set_at_path(path, existing_type.without_undefined());
322 self.merge(success_definition)
323 }
324 }
325
326 #[must_use]
334 pub fn with_metadata_field(
335 mut self,
336 path: &OwnedValuePath,
337 kind: Kind,
338 meaning: Option<&str>,
339 ) -> Self {
340 if !path.is_root() {
341 assert!(
342 self.metadata_kind.as_object().is_some(),
343 "Setting a field on a value that cannot be an object"
344 );
345 }
346
347 self.metadata_kind.set_at_path(path, kind);
348
349 if let Some(meaning) = meaning {
350 self.meaning.insert(
351 meaning.to_owned(),
352 MeaningPointer::Valid(OwnedTargetPath::metadata(path.clone())),
353 );
354 }
355
356 self
357 }
358
359 #[must_use]
365 pub fn optional_field(self, path: &OwnedValuePath, kind: Kind, meaning: Option<&str>) -> Self {
366 self.with_event_field(path, kind.or_undefined(), meaning)
367 }
368
369 #[must_use]
375 pub fn with_meaning(mut self, target_path: OwnedTargetPath, meaning: &str) -> Self {
376 self.add_meaning(target_path, meaning);
377 self
378 }
379
380 pub fn add_meaning(&mut self, target_path: OwnedTargetPath, meaning: &str) {
386 self.try_with_meaning(target_path, meaning)
387 .unwrap_or_else(|err| panic!("{}", err));
388 }
389
390 pub fn try_with_meaning(
396 &mut self,
397 target_path: OwnedTargetPath,
398 meaning: &str,
399 ) -> Result<(), &'static str> {
400 match target_path.prefix {
401 PathPrefix::Event
402 if !self
403 .event_kind
404 .at_path(&target_path.path)
405 .contains_any_defined() =>
406 {
407 Err("meaning must point to a valid path")
408 }
409
410 PathPrefix::Metadata
411 if !self
412 .metadata_kind
413 .at_path(&target_path.path)
414 .contains_any_defined() =>
415 {
416 Err("meaning must point to a valid path")
417 }
418
419 _ => {
420 self.meaning
421 .insert(meaning.to_owned(), MeaningPointer::Valid(target_path));
422 Ok(())
423 }
424 }
425 }
426
427 #[must_use]
429 pub fn unknown_fields(mut self, unknown: impl Into<Kind>) -> Self {
430 let unknown = unknown.into();
431 if let Some(object) = self.event_kind.as_object_mut() {
432 object.set_unknown(unknown.clone());
433 }
434 if let Some(array) = self.event_kind.as_array_mut() {
435 array.set_unknown(unknown);
436 }
437 self
438 }
439
440 #[must_use]
444 pub fn merge(mut self, mut other: Self) -> Self {
445 for (other_id, other_meaning) in other.meaning {
446 let meaning = match self.meaning.remove(&other_id) {
447 Some(this_meaning) => this_meaning.merge(other_meaning),
448 None => other_meaning,
449 };
450
451 self.meaning.insert(other_id, meaning);
452 }
453
454 self.event_kind = self.event_kind.union(other.event_kind);
455 self.metadata_kind = self.metadata_kind.union(other.metadata_kind);
456 self.log_namespaces.append(&mut other.log_namespaces);
457 self
458 }
459
460 pub fn combine_log_namespaces(
463 log_namespaces: &BTreeSet<LogNamespace>,
464 legacy: Self,
465 vector: Self,
466 ) -> Self {
467 let mut combined =
468 Definition::new_with_default_metadata(Kind::never(), log_namespaces.clone());
469
470 if log_namespaces.contains(&LogNamespace::Legacy) {
471 combined = combined.merge(legacy);
472 }
473 if log_namespaces.contains(&LogNamespace::Vector) {
474 combined = combined.merge(vector);
475 }
476 combined
477 }
478
479 pub fn meaning_path(&self, meaning: &str) -> Option<&OwnedTargetPath> {
481 match self.meaning.get(meaning) {
482 Some(MeaningPointer::Valid(path)) => Some(path),
483 None | Some(MeaningPointer::Invalid(_)) => None,
484 }
485 }
486
487 pub fn invalid_meaning(&self, meaning: &str) -> Option<&BTreeSet<OwnedTargetPath>> {
488 match &self.meaning.get(meaning) {
489 Some(MeaningPointer::Invalid(paths)) => Some(paths),
490 None | Some(MeaningPointer::Valid(_)) => None,
491 }
492 }
493
494 pub fn meanings(&self) -> impl Iterator<Item = (&String, &OwnedTargetPath)> {
495 self.meaning
496 .iter()
497 .filter_map(|(id, pointer)| match pointer {
498 MeaningPointer::Valid(path) => Some((id, path)),
499 MeaningPointer::Invalid(_) => None,
500 })
501 }
502
503 pub fn add_meanings<'a>(
510 &'a mut self,
511 meanings: impl Iterator<Item = (&'a String, &'a OwnedTargetPath)>,
512 ) {
513 for (meaning, path) in meanings {
514 self.add_meaning(path.clone(), meaning);
515 }
516 }
517
518 pub fn event_kind(&self) -> &Kind {
519 &self.event_kind
520 }
521
522 pub fn event_kind_mut(&mut self) -> &mut Kind {
523 &mut self.event_kind
524 }
525
526 pub fn metadata_kind(&self) -> &Kind {
527 &self.metadata_kind
528 }
529
530 #[allow(clippy::needless_pass_by_value)]
531 pub fn kind_at<'a>(&self, target_path: impl TargetPath<'a>) -> Kind {
532 match target_path.prefix() {
533 PathPrefix::Event => self.event_kind.at_path(target_path.value_path()),
534 PathPrefix::Metadata => self.metadata_kind.at_path(target_path.value_path()),
535 }
536 }
537}
538
539#[cfg(any(test, feature = "test"))]
540mod test_utils {
541 use super::{Definition, Kind};
542 use crate::event::{Event, LogEvent};
543
544 impl Definition {
545 pub fn is_valid_for_event(&self, event: &Event) -> Result<(), String> {
551 if let Some(log) = event.maybe_as_log() {
552 let log: &LogEvent = log;
553
554 let actual_kind = Kind::from(log.value());
555 if let Err(path) = self.event_kind.is_superset(&actual_kind) {
556 return Result::Err(format!(
557 "Event value doesn't match at path: {}\n\nEvent type at path = {:?}\n\nDefinition at path = {:?}",
558 path,
559 actual_kind.at_path(&path).debug_info(),
560 self.event_kind.at_path(&path).debug_info()
561 ));
562 }
563
564 let actual_metadata_kind = Kind::from(log.metadata().value());
565 if let Err(path) = self.metadata_kind.is_superset(&actual_metadata_kind) {
566 return Result::Err(format!(
569 "Event METADATA value doesn't match at path: {}\n\nMetadata type at path = {:?}\n\nDefinition at path = {:?}",
570 path,
571 actual_metadata_kind.at_path(&path).debug_info(),
572 self.metadata_kind.at_path(&path).debug_info()
573 ));
574 }
575 if !self.log_namespaces.contains(&log.namespace()) {
576 return Result::Err(format!(
577 "Event uses the {:?} LogNamespace, but the definition only contains: {:?}",
578 log.namespace(),
579 self.log_namespaces
580 ));
581 }
582
583 Ok(())
584 } else {
585 Ok(())
587 }
588 }
589
590 pub fn assert_valid_for_event(&self, event: &Event) {
596 if let Err(err) = self.is_valid_for_event(event) {
597 panic!("Schema definition assertion failed: {err}");
598 }
599 }
600
601 pub fn assert_invalid_for_event(&self, event: &Event) {
607 assert!(
608 self.is_valid_for_event(event).is_err(),
609 "Schema definition assertion should not be valid"
610 );
611 }
612 }
613}
614
615#[cfg(test)]
616mod tests {
617 use std::collections::{BTreeMap, HashMap};
618
619 use lookup::{lookup_v2::parse_target_path, owned_value_path};
620 use vrl::value::Value;
621
622 use super::*;
623 use crate::event::{Event, EventMetadata, LogEvent};
624
625 #[test]
626 fn test_definition_validity() {
627 struct TestCase {
628 title: &'static str,
629 definition: Definition,
630 event: Event,
631 valid: bool,
632 }
633
634 for TestCase {
635 title,
636 definition,
637 event,
638 valid,
639 } in [
640 TestCase {
641 title: "match",
642 definition: Definition::new(Kind::any(), Kind::any(), [LogNamespace::Legacy]),
643 event: Event::Log(LogEvent::from(BTreeMap::new())),
644 valid: true,
645 },
646 TestCase {
647 title: "event mismatch",
648 definition: Definition::new(
649 Kind::object(Collection::empty()),
650 Kind::any(),
651 [LogNamespace::Legacy],
652 ),
653 event: Event::Log(LogEvent::from(BTreeMap::from([("foo".into(), 4.into())]))),
654 valid: false,
655 },
656 TestCase {
657 title: "metadata mismatch",
658 definition: Definition::new(
659 Kind::any(),
660 Kind::object(Collection::empty()),
661 [LogNamespace::Legacy],
662 ),
663 event: Event::Log(LogEvent::from_parts(
664 Value::Object(BTreeMap::new()),
665 EventMetadata::default_with_value(
666 BTreeMap::from([("foo".into(), 4.into())]).into(),
667 ),
668 )),
669 valid: false,
670 },
671 TestCase {
672 title: "wrong log namespace",
673 definition: Definition::new(Kind::any(), Kind::any(), []),
674 event: Event::Log(LogEvent::from(BTreeMap::new())),
675 valid: false,
676 },
677 TestCase {
678 title: "event mismatch - null vs undefined",
679 definition: Definition::new(
680 Kind::object(Collection::empty()),
681 Kind::any(),
682 [LogNamespace::Legacy],
683 ),
684 event: Event::Log(LogEvent::from(BTreeMap::from([(
685 "foo".into(),
686 Value::Null,
687 )]))),
688 valid: false,
689 },
690 ] {
691 let result = definition.is_valid_for_event(&event);
692 assert_eq!(result.is_ok(), valid, "{title}");
693 }
694 }
695
696 #[test]
697 fn test_empty_legacy_field() {
698 let definition = Definition::default_legacy_namespace().with_vector_metadata(
699 Some(&owned_value_path!()),
700 &owned_value_path!(),
701 Kind::integer(),
702 None,
703 );
704
705 assert_eq!(definition, Definition::default_legacy_namespace());
707 }
708
709 #[test]
710 fn test_required_field() {
711 struct TestCase {
712 path: OwnedValuePath,
713 kind: Kind,
714 meaning: Option<&'static str>,
715 want: Definition,
716 }
717
718 for (
719 title,
720 TestCase {
721 path,
722 kind,
723 meaning,
724 want,
725 },
726 ) in HashMap::from([
727 (
728 "simple",
729 TestCase {
730 path: owned_value_path!("foo"),
731 kind: Kind::boolean(),
732 meaning: Some("foo_meaning"),
733 want: Definition {
734 event_kind: Kind::object(BTreeMap::from([("foo".into(), Kind::boolean())])),
735 metadata_kind: Kind::object(Collection::empty()),
736 meaning: [(
737 "foo_meaning".to_owned(),
738 MeaningPointer::Valid(parse_target_path("foo").unwrap()),
739 )]
740 .into(),
741 log_namespaces: BTreeSet::new(),
742 },
743 },
744 ),
745 (
746 "nested fields",
747 TestCase {
748 path: owned_value_path!("foo", "bar"),
749 kind: Kind::regex().or_null(),
750 meaning: Some("foobar"),
751 want: Definition {
752 event_kind: Kind::object(BTreeMap::from([(
753 "foo".into(),
754 Kind::object(BTreeMap::from([("bar".into(), Kind::regex().or_null())])),
755 )])),
756 metadata_kind: Kind::object(Collection::empty()),
757 meaning: [(
758 "foobar".to_owned(),
759 MeaningPointer::Valid(parse_target_path(".foo.bar").unwrap()),
760 )]
761 .into(),
762 log_namespaces: BTreeSet::new(),
763 },
764 },
765 ),
766 (
767 "no meaning",
768 TestCase {
769 path: owned_value_path!("foo"),
770 kind: Kind::boolean(),
771 meaning: None,
772 want: Definition {
773 event_kind: Kind::object(BTreeMap::from([("foo".into(), Kind::boolean())])),
774 metadata_kind: Kind::object(Collection::empty()),
775 meaning: BTreeMap::default(),
776 log_namespaces: BTreeSet::new(),
777 },
778 },
779 ),
780 ]) {
781 let got = Definition::empty_legacy_namespace().with_event_field(&path, kind, meaning);
782 assert_eq!(got.event_kind(), want.event_kind(), "{title}");
783 }
784 }
785
786 #[test]
787 fn test_optional_field() {
788 struct TestCase {
789 path: OwnedValuePath,
790 kind: Kind,
791 meaning: Option<&'static str>,
792 want: Definition,
793 }
794
795 for (
796 title,
797 TestCase {
798 path,
799 kind,
800 meaning,
801 want,
802 },
803 ) in [
804 (
805 "simple",
806 TestCase {
807 path: owned_value_path!("foo"),
808 kind: Kind::boolean(),
809 meaning: Some("foo_meaning"),
810 want: Definition {
811 event_kind: Kind::object(BTreeMap::from([(
812 "foo".into(),
813 Kind::boolean().or_undefined(),
814 )])),
815 metadata_kind: Kind::object(Collection::any()),
816 meaning: [(
817 "foo_meaning".to_owned(),
818 MeaningPointer::Valid(parse_target_path("foo").unwrap()),
819 )]
820 .into(),
821 log_namespaces: BTreeSet::new(),
822 },
823 },
824 ),
825 (
826 "nested fields",
827 TestCase {
828 path: owned_value_path!("foo", "bar"),
829 kind: Kind::regex().or_null(),
830 meaning: Some("foobar"),
831 want: Definition {
832 event_kind: Kind::object(BTreeMap::from([(
833 "foo".into(),
834 Kind::object(BTreeMap::from([(
835 "bar".into(),
836 Kind::regex().or_null().or_undefined(),
837 )])),
838 )])),
839 metadata_kind: Kind::object(Collection::any()),
840 meaning: [(
841 "foobar".to_owned(),
842 MeaningPointer::Valid(parse_target_path(".foo.bar").unwrap()),
843 )]
844 .into(),
845 log_namespaces: BTreeSet::new(),
846 },
847 },
848 ),
849 (
850 "no meaning",
851 TestCase {
852 path: owned_value_path!("foo"),
853 kind: Kind::boolean(),
854 meaning: None,
855 want: Definition {
856 event_kind: Kind::object(BTreeMap::from([(
857 "foo".into(),
858 Kind::boolean().or_undefined(),
859 )])),
860 metadata_kind: Kind::object(Collection::any()),
861 meaning: BTreeMap::default(),
862 log_namespaces: BTreeSet::new(),
863 },
864 },
865 ),
866 ] {
867 let mut got = Definition::new_with_default_metadata(Kind::object(BTreeMap::new()), []);
868 got = got.optional_field(&path, kind, meaning);
869
870 assert_eq!(got, want, "{title}");
871 }
872 }
873
874 #[test]
875 fn test_unknown_fields() {
876 let want = Definition {
877 event_kind: Kind::object(Collection::from_unknown(Kind::bytes().or_integer())),
878 metadata_kind: Kind::object(Collection::any()),
879 meaning: BTreeMap::default(),
880 log_namespaces: BTreeSet::new(),
881 };
882
883 let mut got = Definition::new_with_default_metadata(Kind::object(Collection::empty()), []);
884 got = got.unknown_fields(Kind::boolean());
885 got = got.unknown_fields(Kind::bytes().or_integer());
886
887 assert_eq!(got, want);
888 }
889
890 #[test]
891 fn test_meaning_path() {
892 let def = Definition::new(
893 Kind::object(Collection::empty()),
894 Kind::object(Collection::empty()),
895 [LogNamespace::Legacy],
896 )
897 .with_event_field(
898 &owned_value_path!("foo"),
899 Kind::boolean(),
900 Some("foo_meaning"),
901 )
902 .with_metadata_field(
903 &owned_value_path!("bar"),
904 Kind::boolean(),
905 Some("bar_meaning"),
906 );
907
908 assert_eq!(
909 def.meaning_path("foo_meaning").unwrap(),
910 &OwnedTargetPath::event(owned_value_path!("foo"))
911 );
912 assert_eq!(
913 def.meaning_path("bar_meaning").unwrap(),
914 &OwnedTargetPath::metadata(owned_value_path!("bar"))
915 );
916 }
917
918 #[test]
919 #[allow(clippy::too_many_lines)]
920 fn test_merge() {
921 struct TestCase {
922 this: Definition,
923 other: Definition,
924 want: Definition,
925 }
926
927 for (title, TestCase { this, other, want }) in HashMap::from([
928 (
929 "equal definitions",
930 TestCase {
931 this: Definition {
932 event_kind: Kind::object(Collection::from(BTreeMap::from([(
933 "foo".into(),
934 Kind::boolean().or_null(),
935 )]))),
936 metadata_kind: Kind::object(Collection::empty()),
937 meaning: BTreeMap::from([(
938 "foo_meaning".to_owned(),
939 MeaningPointer::Valid(parse_target_path("foo").unwrap()),
940 )]),
941 log_namespaces: BTreeSet::new(),
942 },
943 other: Definition {
944 event_kind: Kind::object(Collection::from(BTreeMap::from([(
945 "foo".into(),
946 Kind::boolean().or_null(),
947 )]))),
948 metadata_kind: Kind::object(Collection::empty()),
949 meaning: BTreeMap::from([(
950 "foo_meaning".to_owned(),
951 MeaningPointer::Valid(parse_target_path("foo").unwrap()),
952 )]),
953 log_namespaces: BTreeSet::new(),
954 },
955 want: Definition {
956 event_kind: Kind::object(Collection::from(BTreeMap::from([(
957 "foo".into(),
958 Kind::boolean().or_null(),
959 )]))),
960 metadata_kind: Kind::object(Collection::empty()),
961 meaning: BTreeMap::from([(
962 "foo_meaning".to_owned(),
963 MeaningPointer::Valid(parse_target_path("foo").unwrap()),
964 )]),
965 log_namespaces: BTreeSet::new(),
966 },
967 },
968 ),
969 (
970 "this optional, other required",
971 TestCase {
972 this: Definition {
973 event_kind: Kind::object(Collection::from(BTreeMap::from([(
974 "foo".into(),
975 Kind::boolean().or_null(),
976 )]))),
977 metadata_kind: Kind::object(Collection::empty()),
978 meaning: BTreeMap::default(),
979 log_namespaces: BTreeSet::new(),
980 },
981 other: Definition {
982 event_kind: Kind::object(Collection::from(BTreeMap::from([(
983 "foo".into(),
984 Kind::boolean(),
985 )]))),
986 metadata_kind: Kind::object(Collection::empty()),
987 meaning: BTreeMap::default(),
988 log_namespaces: BTreeSet::new(),
989 },
990 want: Definition {
991 event_kind: Kind::object(Collection::from(BTreeMap::from([(
992 "foo".into(),
993 Kind::boolean().or_null(),
994 )]))),
995 metadata_kind: Kind::object(Collection::empty()),
996 meaning: BTreeMap::default(),
997 log_namespaces: BTreeSet::new(),
998 },
999 },
1000 ),
1001 (
1002 "this required, other optional",
1003 TestCase {
1004 this: Definition {
1005 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1006 "foo".into(),
1007 Kind::boolean(),
1008 )]))),
1009 metadata_kind: Kind::object(Collection::empty()),
1010 meaning: BTreeMap::default(),
1011 log_namespaces: BTreeSet::new(),
1012 },
1013 other: Definition {
1014 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1015 "foo".into(),
1016 Kind::boolean().or_null(),
1017 )]))),
1018 metadata_kind: Kind::object(Collection::empty()),
1019 meaning: BTreeMap::default(),
1020 log_namespaces: BTreeSet::new(),
1021 },
1022 want: Definition {
1023 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1024 "foo".into(),
1025 Kind::boolean().or_null(),
1026 )]))),
1027 metadata_kind: Kind::object(Collection::empty()),
1028 meaning: BTreeMap::default(),
1029 log_namespaces: BTreeSet::new(),
1030 },
1031 },
1032 ),
1033 (
1034 "this required, other required",
1035 TestCase {
1036 this: Definition {
1037 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1038 "foo".into(),
1039 Kind::boolean(),
1040 )]))),
1041 metadata_kind: Kind::object(Collection::empty()),
1042 meaning: BTreeMap::default(),
1043 log_namespaces: BTreeSet::new(),
1044 },
1045 other: Definition {
1046 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1047 "foo".into(),
1048 Kind::boolean(),
1049 )]))),
1050 metadata_kind: Kind::object(Collection::empty()),
1051 meaning: BTreeMap::default(),
1052 log_namespaces: BTreeSet::new(),
1053 },
1054 want: Definition {
1055 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1056 "foo".into(),
1057 Kind::boolean(),
1058 )]))),
1059 metadata_kind: Kind::object(Collection::empty()),
1060 meaning: BTreeMap::default(),
1061 log_namespaces: BTreeSet::new(),
1062 },
1063 },
1064 ),
1065 (
1066 "same meaning, pointing to different paths",
1067 TestCase {
1068 this: Definition {
1069 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1070 "foo".into(),
1071 Kind::boolean(),
1072 )]))),
1073 metadata_kind: Kind::object(Collection::empty()),
1074 meaning: BTreeMap::from([(
1075 "foo".into(),
1076 MeaningPointer::Valid(parse_target_path("foo").unwrap()),
1077 )]),
1078 log_namespaces: BTreeSet::new(),
1079 },
1080 other: Definition {
1081 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1082 "foo".into(),
1083 Kind::boolean(),
1084 )]))),
1085 metadata_kind: Kind::object(Collection::empty()),
1086 meaning: BTreeMap::from([(
1087 "foo".into(),
1088 MeaningPointer::Valid(parse_target_path("bar").unwrap()),
1089 )]),
1090 log_namespaces: BTreeSet::new(),
1091 },
1092 want: Definition {
1093 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1094 "foo".into(),
1095 Kind::boolean(),
1096 )]))),
1097 metadata_kind: Kind::object(Collection::empty()),
1098 meaning: BTreeMap::from([(
1099 "foo".into(),
1100 MeaningPointer::Invalid(BTreeSet::from([
1101 parse_target_path("foo").unwrap(),
1102 parse_target_path("bar").unwrap(),
1103 ])),
1104 )]),
1105 log_namespaces: BTreeSet::new(),
1106 },
1107 },
1108 ),
1109 (
1110 "same meaning, pointing to same path",
1111 TestCase {
1112 this: Definition {
1113 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1114 "foo".into(),
1115 Kind::boolean(),
1116 )]))),
1117 metadata_kind: Kind::object(Collection::empty()),
1118 meaning: BTreeMap::from([(
1119 "foo".into(),
1120 MeaningPointer::Valid(parse_target_path("foo").unwrap()),
1121 )]),
1122 log_namespaces: BTreeSet::new(),
1123 },
1124 other: Definition {
1125 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1126 "foo".into(),
1127 Kind::boolean(),
1128 )]))),
1129 metadata_kind: Kind::object(Collection::empty()),
1130 meaning: BTreeMap::from([(
1131 "foo".into(),
1132 MeaningPointer::Valid(parse_target_path("foo").unwrap()),
1133 )]),
1134 log_namespaces: BTreeSet::new(),
1135 },
1136 want: Definition {
1137 event_kind: Kind::object(Collection::from(BTreeMap::from([(
1138 "foo".into(),
1139 Kind::boolean(),
1140 )]))),
1141 metadata_kind: Kind::object(Collection::empty()),
1142 meaning: BTreeMap::from([(
1143 "foo".into(),
1144 MeaningPointer::Valid(parse_target_path("foo").unwrap()),
1145 )]),
1146 log_namespaces: BTreeSet::new(),
1147 },
1148 },
1149 ),
1150 ]) {
1151 let got = this.merge(other);
1152
1153 assert_eq!(got, want, "{title}");
1154 }
1155 }
1156}