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