1use indexmap::IndexMap;
6use itertools::Itertools;
7
8use crate::types::capability::ContextCapability;
9use crate::types::child::ContextChild;
10use crate::types::collection::ContextCollection;
11use crate::types::common::*;
12use crate::types::environment::ContextEnvironment;
13use crate::types::expose::ContextExpose;
14use crate::types::offer::ContextOffer;
15use crate::types::program::ContextProgram;
16use crate::types::r#use::ContextUse;
17use crate::{
18 Canonicalize, CanonicalizeContext, Capability, CapabilityClause, CapabilityFromRef, Child,
19 Collection, ConfigKey, ConfigValueType, Environment, Error, Expose, Location, Offer, Program,
20 Use, merge_spanned_vec,
21};
22
23pub use cm_types::{
24 Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
25 OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
26};
27use reference_doc::ReferenceDoc;
28use serde::{Deserialize, Serialize};
29use serde_json::{Map, Value};
30
31use std::collections::{BTreeMap, HashMap, HashSet};
32use std::path::PathBuf;
33use std::sync::Arc;
34use std::{cmp, path};
35
36#[derive(ReferenceDoc, Deserialize, Debug, Default, PartialEq, Serialize)]
84#[serde(deny_unknown_fields)]
85pub struct Document {
86 #[serde(skip_serializing_if = "Option::is_none")]
220 pub include: Option<Vec<String>>,
221
222 #[reference_doc(json_type = "object")]
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub program: Option<Program>,
259
260 #[reference_doc(recurse)]
265 #[serde(skip_serializing_if = "Option::is_none")]
266 pub children: Option<Vec<Child>>,
267
268 #[reference_doc(recurse)]
271 #[serde(skip_serializing_if = "Option::is_none")]
272 pub collections: Option<Vec<Collection>>,
273
274 #[reference_doc(recurse)]
279 #[serde(skip_serializing_if = "Option::is_none")]
280 pub environments: Option<Vec<Environment>>,
281
282 #[reference_doc(recurse)]
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub capabilities: Option<Vec<Capability>>,
307
308 #[reference_doc(recurse)]
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub r#use: Option<Vec<Use>>,
334
335 #[reference_doc(recurse)]
354 #[serde(skip_serializing_if = "Option::is_none")]
355 pub expose: Option<Vec<Expose>>,
356
357 #[reference_doc(recurse)]
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub offer: Option<Vec<Offer>>,
380
381 #[serde(skip_serializing_if = "Option::is_none")]
385 pub facets: Option<IndexMap<String, Value>>,
386
387 #[reference_doc(json_type = "object")]
454 #[serde(skip_serializing_if = "Option::is_none")]
455 pub config: Option<BTreeMap<ConfigKey, ConfigValueType>>,
460}
461
462impl Document {
463 pub fn merge_from(
464 &mut self,
465 other: &mut Document,
466 include_path: &path::Path,
467 ) -> Result<(), Error> {
468 merge_from_capability_field(&mut self.r#use, &mut other.r#use)?;
471 merge_from_capability_field(&mut self.expose, &mut other.expose)?;
472 merge_from_capability_field(&mut self.offer, &mut other.offer)?;
473 merge_from_capability_field(&mut self.capabilities, &mut other.capabilities)?;
474 merge_from_other_field(&mut self.include, &mut other.include);
475 merge_from_other_field(&mut self.children, &mut other.children);
476 merge_from_other_field(&mut self.collections, &mut other.collections);
477 self.merge_environment(other, include_path)?;
478 self.merge_program(other, include_path)?;
479 self.merge_facets(other, include_path)?;
480 self.merge_config(other, include_path)?;
481
482 Ok(())
483 }
484
485 pub fn canonicalize(&mut self) {
486 if let Some(children) = &mut self.children {
488 children.sort_by(|a, b| a.name.cmp(&b.name));
489 }
490 if let Some(collections) = &mut self.collections {
491 collections.sort_by(|a, b| a.name.cmp(&b.name));
492 }
493 if let Some(environments) = &mut self.environments {
494 environments.sort_by(|a, b| a.name.cmp(&b.name));
495 }
496 if let Some(capabilities) = &mut self.capabilities {
497 capabilities.canonicalize();
498 }
499 if let Some(offers) = &mut self.offer {
500 offers.canonicalize();
501 }
502 if let Some(expose) = &mut self.expose {
503 expose.canonicalize();
504 }
505 if let Some(r#use) = &mut self.r#use {
506 r#use.canonicalize();
507 }
508 }
509
510 fn merge_program(
511 &mut self,
512 other: &mut Document,
513 include_path: &path::Path,
514 ) -> Result<(), Error> {
515 if let None = other.program {
516 return Ok(());
517 }
518 if let None = self.program {
519 self.program = Some(Program::default());
520 }
521 let my_program = self.program.as_mut().unwrap();
522 let other_program = other.program.as_mut().unwrap();
523 if let Some(other_runner) = other_program.runner.take() {
524 my_program.runner = match &my_program.runner {
525 Some(runner) if *runner != other_runner => {
526 return Err(Error::validate(format!(
527 "manifest include had a conflicting `program.runner`: {}",
528 include_path.display()
529 )));
530 }
531 _ => Some(other_runner),
532 }
533 }
534
535 Self::merge_maps_with_options(
536 &mut my_program.info,
537 &other_program.info,
538 "program",
539 include_path,
540 Some(vec!["environ", "features"]),
541 )
542 }
543
544 fn merge_environment(
545 &mut self,
546 other: &mut Document,
547 _include_path: &path::Path,
548 ) -> Result<(), Error> {
549 if let None = other.environments {
550 return Ok(());
551 }
552 if let None = self.environments {
553 self.environments = Some(vec![]);
554 }
555
556 let my_environments = self.environments.as_mut().unwrap();
557 let other_environments = other.environments.as_mut().unwrap();
558 my_environments.sort_by(|x, y| x.name.cmp(&y.name));
559 other_environments.sort_by(|x, y| x.name.cmp(&y.name));
560
561 let all_environments =
562 my_environments.into_iter().merge_by(other_environments, |x, y| x.name <= y.name);
563 let groups = all_environments.chunk_by(|e| e.name.clone());
564
565 let mut merged_environments = vec![];
566 for (name, group) in groups.into_iter() {
567 let mut merged_environment = Environment {
568 name: name.clone(),
569 extends: None,
570 runners: None,
571 resolvers: None,
572 debug: None,
573 stop_timeout_ms: None,
574 };
575 for e in group {
576 merged_environment.merge_from(e)?;
577 }
578 merged_environments.push(merged_environment);
579 }
580
581 self.environments = Some(merged_environments);
582 Ok(())
583 }
584
585 fn merge_maps<'s, Source, Dest>(
586 self_map: &mut Dest,
587 include_map: Source,
588 outer_key: &str,
589 include_path: &path::Path,
590 ) -> Result<(), Error>
591 where
592 Source: IntoIterator<Item = (&'s String, &'s Value)>,
593 Dest: ValueMap,
594 {
595 Self::merge_maps_with_options(self_map, include_map, outer_key, include_path, None)
596 }
597
598 fn merge_maps_with_options<'s, Source, Dest>(
603 self_map: &mut Dest,
604 include_map: Source,
605 outer_key: &str,
606 include_path: &path::Path,
607 allow_array_concatenation_keys: Option<Vec<&str>>,
608 ) -> Result<(), Error>
609 where
610 Source: IntoIterator<Item = (&'s String, &'s Value)>,
611 Dest: ValueMap,
612 {
613 for (key, value) in include_map {
614 match self_map.get_mut(key) {
615 None => {
616 self_map.insert(key.clone(), value.clone());
618 }
619 Some(Value::Object(self_nested_map)) => match value {
621 Value::Object(include_nested_map) => {
623 let combined_key = format!("{}.{}", outer_key, key);
624
625 Self::merge_maps(
627 self_nested_map,
628 include_nested_map,
629 &combined_key,
630 include_path,
631 )?;
632 }
633 _ => {
634 return Err(Error::validate(format!(
636 "manifest include had a conflicting `{}.{}`: {}",
637 outer_key,
638 key,
639 include_path.display()
640 )));
641 }
642 },
643 Some(Value::Array(self_nested_vec)) => match value {
644 Value::Array(include_nested_vec) => {
647 if let Some(allowed_keys) = &allow_array_concatenation_keys {
648 if !allowed_keys.contains(&key.as_str()) {
649 return Err(Error::validate(format!(
652 "manifest include had a conflicting `{}.{}`: {}",
653 outer_key,
654 key,
655 include_path.display()
656 )));
657 }
658 }
659 let mut new_values = include_nested_vec.clone();
660 self_nested_vec.append(&mut new_values);
661 }
662 _ => {
663 return Err(Error::validate(format!(
665 "manifest include had a conflicting `{}.{}`: {}",
666 outer_key,
667 key,
668 include_path.display()
669 )));
670 }
671 },
672 _ => {
673 return Err(Error::validate(format!(
675 "manifest include had a conflicting `{}.{}`: {}",
676 outer_key,
677 key,
678 include_path.display()
679 )));
680 }
681 }
682 }
683 Ok(())
684 }
685
686 fn merge_facets(
687 &mut self,
688 other: &mut Document,
689 include_path: &path::Path,
690 ) -> Result<(), Error> {
691 if let None = other.facets {
692 return Ok(());
693 }
694 if let None = self.facets {
695 self.facets = Some(Default::default());
696 }
697 let my_facets = self.facets.as_mut().unwrap();
698 let other_facets = other.facets.as_ref().unwrap();
699
700 Self::merge_maps(my_facets, other_facets, "facets", include_path)
701 }
702
703 fn merge_config(
704 &mut self,
705 other: &mut Document,
706 include_path: &path::Path,
707 ) -> Result<(), Error> {
708 if let Some(other_config) = other.config.as_mut() {
709 if let Some(self_config) = self.config.as_mut() {
710 for (key, field) in other_config {
711 match self_config.entry(key.clone()) {
712 std::collections::btree_map::Entry::Vacant(v) => {
713 v.insert(field.clone());
714 }
715 std::collections::btree_map::Entry::Occupied(o) => {
716 if o.get() != field {
717 let msg = format!(
718 "Found conflicting entry for config key `{key}` in `{}`.",
719 include_path.display()
720 );
721 return Err(Error::validate(&msg));
722 }
723 }
724 }
725 }
726 } else {
727 self.config.replace(std::mem::take(other_config));
728 }
729 }
730 Ok(())
731 }
732
733 pub fn includes(&self) -> Vec<String> {
734 self.include.clone().unwrap_or_default()
735 }
736
737 pub fn all_children_names(&self) -> Vec<&BorrowedName> {
738 if let Some(children) = self.children.as_ref() {
739 children.iter().map(|c| c.name.as_ref()).collect()
740 } else {
741 vec![]
742 }
743 }
744
745 pub fn all_collection_names(&self) -> Vec<&BorrowedName> {
746 if let Some(collections) = self.collections.as_ref() {
747 collections.iter().map(|c| c.name.as_ref()).collect()
748 } else {
749 vec![]
750 }
751 }
752
753 pub fn all_storage_names(&self) -> Vec<&BorrowedName> {
754 if let Some(capabilities) = self.capabilities.as_ref() {
755 capabilities.iter().filter_map(|c| c.storage.as_ref().map(|n| n.as_ref())).collect()
756 } else {
757 vec![]
758 }
759 }
760
761 pub fn all_storage_with_sources<'a>(
762 &'a self,
763 ) -> HashMap<&'a BorrowedName, &'a CapabilityFromRef> {
764 if let Some(capabilities) = self.capabilities.as_ref() {
765 capabilities
766 .iter()
767 .filter_map(|c| match (c.storage.as_ref().map(Name::as_ref), c.from.as_ref()) {
768 (Some(s), Some(f)) => Some((s, f)),
769 _ => None,
770 })
771 .collect()
772 } else {
773 HashMap::new()
774 }
775 }
776
777 pub fn all_service_names(&self) -> Vec<&BorrowedName> {
778 self.capabilities
779 .as_ref()
780 .map(|c| {
781 c.iter()
782 .filter_map(|c| c.service.as_ref().map(|o| o.as_ref()))
783 .map(|p| p.into_iter())
784 .flatten()
785 .collect()
786 })
787 .unwrap_or_else(|| vec![])
788 }
789
790 pub fn all_protocol_names(&self) -> Vec<&BorrowedName> {
791 self.capabilities
792 .as_ref()
793 .map(|c| {
794 c.iter()
795 .filter_map(|c| c.protocol.as_ref().map(|o| o.as_ref()))
796 .map(|p| p.into_iter())
797 .flatten()
798 .collect()
799 })
800 .unwrap_or_else(|| vec![])
801 }
802
803 pub fn all_directory_names(&self) -> Vec<&BorrowedName> {
804 self.capabilities
805 .as_ref()
806 .map(|c| c.iter().filter_map(|c| c.directory.as_ref().map(Name::as_ref)).collect())
807 .unwrap_or_else(|| vec![])
808 }
809
810 pub fn all_runner_names(&self) -> Vec<&BorrowedName> {
811 self.capabilities
812 .as_ref()
813 .map(|c| c.iter().filter_map(|c| c.runner.as_ref().map(Name::as_ref)).collect())
814 .unwrap_or_else(|| vec![])
815 }
816
817 pub fn all_resolver_names(&self) -> Vec<&BorrowedName> {
818 self.capabilities
819 .as_ref()
820 .map(|c| c.iter().filter_map(|c| c.resolver.as_ref().map(Name::as_ref)).collect())
821 .unwrap_or_else(|| vec![])
822 }
823
824 pub fn all_dictionary_names(&self) -> Vec<&BorrowedName> {
825 if let Some(capabilities) = self.capabilities.as_ref() {
826 capabilities.iter().filter_map(|c| c.dictionary.as_ref().map(Name::as_ref)).collect()
827 } else {
828 vec![]
829 }
830 }
831
832 pub fn all_dictionaries<'a>(&'a self) -> HashMap<&'a BorrowedName, &'a Capability> {
833 if let Some(capabilities) = self.capabilities.as_ref() {
834 capabilities
835 .iter()
836 .filter_map(|c| match c.dictionary.as_ref().map(Name::as_ref) {
837 Some(s) => Some((s, c)),
838 _ => None,
839 })
840 .collect()
841 } else {
842 HashMap::new()
843 }
844 }
845
846 pub fn all_config_names(&self) -> Vec<&BorrowedName> {
847 self.capabilities
848 .as_ref()
849 .map(|c| c.iter().filter_map(|c| c.config.as_ref().map(Name::as_ref)).collect())
850 .unwrap_or_else(|| vec![])
851 }
852
853 pub fn all_environment_names(&self) -> Vec<&BorrowedName> {
854 self.environments
855 .as_ref()
856 .map(|c| c.iter().map(|s| s.name.as_ref()).collect())
857 .unwrap_or_else(|| vec![])
858 }
859
860 pub fn all_capability_names(&self) -> HashSet<&BorrowedName> {
861 self.capabilities
862 .as_ref()
863 .map(|c| {
864 c.iter().fold(HashSet::new(), |mut acc, capability| {
865 acc.extend(capability.names());
866 acc
867 })
868 })
869 .unwrap_or_default()
870 }
871}
872
873fn merge_from_capability_field<T: CapabilityClause>(
876 us: &mut Option<Vec<T>>,
877 other: &mut Option<Vec<T>>,
878) -> Result<(), Error> {
879 for entry in us.iter().flatten().chain(other.iter().flatten()) {
882 if entry.names().is_empty() {
883 return Err(Error::Validate {
884 err: format!("{}: Missing type name: {:#?}", entry.decl_type(), entry),
885 filename: None,
886 });
887 }
888 }
889
890 if let Some(all_ours) = us.as_mut() {
891 if let Some(all_theirs) = other.take() {
892 for mut theirs in all_theirs {
893 for ours in &mut *all_ours {
894 compute_diff(ours, &mut theirs);
895 }
896 all_ours.push(theirs);
897 }
898 }
899 all_ours.retain(|ours| !ours.names().is_empty())
901 } else if let Some(theirs) = other.take() {
902 us.replace(theirs);
903 }
904 Ok(())
905}
906
907fn merge_from_context_capability_field<T: ContextCapabilityClause>(
908 us: &mut Option<Vec<T>>,
909 other: &mut Option<Vec<T>>,
910) -> Result<(), Error> {
911 for entry in us.iter().flatten().chain(other.iter().flatten()) {
914 if entry.names().is_empty() {
915 return Err(Error::Validate {
916 err: format!("{}: Missing type name: {:#?}", entry.decl_type(), entry),
918 filename: None,
919 });
920 }
921 }
922
923 if let Some(all_ours) = us.as_mut() {
924 if let Some(all_theirs) = other.take() {
925 for mut theirs in all_theirs {
926 for ours in &mut *all_ours {
927 compute_diff_context(ours, &mut theirs);
928 }
929 all_ours.push(theirs);
930 }
931 }
932 all_ours.retain(|ours| !ours.names().is_empty())
934 } else if let Some(theirs) = other.take() {
935 us.replace(theirs);
936 }
937 Ok(())
938}
939
940fn merge_from_other_field<T: std::cmp::PartialEq>(
943 us: &mut Option<Vec<T>>,
944 other: &mut Option<Vec<T>>,
945) {
946 if let Some(ours) = us {
947 if let Some(theirs) = other.take() {
948 for t in theirs {
950 if !ours.contains(&t) {
951 ours.push(t);
952 }
953 }
954 }
955 } else if let Some(theirs) = other.take() {
956 us.replace(theirs);
957 }
958}
959
960fn compute_diff<T: CapabilityClause>(ours: &mut T, theirs: &mut T) {
967 if ours.names().is_empty() || theirs.names().is_empty() {
969 return;
970 }
971
972 if ours.capability_type().unwrap() != theirs.capability_type().unwrap() {
974 return;
975 }
976
977 let mut ours_partial = ours.clone();
979 let mut theirs_partial = theirs.clone();
980 for e in [&mut ours_partial, &mut theirs_partial] {
981 e.set_names(Vec::new());
982 e.set_availability(None);
984 }
985 if ours_partial != theirs_partial {
986 return;
988 }
989
990 let Some(avail_cmp) = ours
992 .availability()
993 .unwrap_or_default()
994 .partial_cmp(&theirs.availability().unwrap_or_default())
995 else {
996 return;
998 };
999
1000 let mut our_names: Vec<Name> = ours.names().into_iter().map(Into::into).collect();
1001 let mut their_names: Vec<Name> = theirs.names().into_iter().map(Into::into).collect();
1002
1003 let mut our_entries_to_remove = HashSet::new();
1004 let mut their_entries_to_remove = HashSet::new();
1005 for e in &their_names {
1006 if !our_names.contains(e) {
1007 continue;
1009 }
1010 match avail_cmp {
1011 cmp::Ordering::Less => {
1012 our_entries_to_remove.insert(e.clone());
1015 }
1016 cmp::Ordering::Greater => {
1017 their_entries_to_remove.insert(e.clone());
1020 }
1021 cmp::Ordering::Equal => {
1022 their_entries_to_remove.insert(e.clone());
1024 }
1025 }
1026 }
1027 our_names.retain(|e| !our_entries_to_remove.contains(e));
1028 their_names.retain(|e| !their_entries_to_remove.contains(e));
1029
1030 ours.set_names(our_names);
1031 theirs.set_names(their_names);
1032}
1033
1034fn compute_diff_context<T: ContextCapabilityClause>(ours: &mut T, theirs: &mut T) {
1041 let our_spanned = ours.names();
1042 let their_spanned = theirs.names();
1043
1044 if our_spanned.is_empty() || their_spanned.is_empty() {
1045 return;
1046 }
1047
1048 if ours.capability_type(None).unwrap() != theirs.capability_type(None).unwrap() {
1049 return;
1050 }
1051
1052 let mut ours_check = ours.clone();
1053 let mut theirs_check = theirs.clone();
1054
1055 ours_check.set_names(Vec::new());
1056 theirs_check.set_names(Vec::new());
1057 ours_check.set_availability(None);
1058 theirs_check.set_availability(None);
1059
1060 if ours_check != theirs_check {
1061 return;
1062 }
1063
1064 let our_avail = ours.availability().map(|a| a.value).unwrap_or_default();
1065 let their_avail = theirs.availability().map(|a| a.value).unwrap_or_default();
1066
1067 let Some(avail_cmp) = our_avail.partial_cmp(&their_avail) else {
1068 return;
1069 };
1070
1071 let our_raw_set: HashSet<&Name> = our_spanned.iter().map(|s| &s.value).collect();
1072
1073 let mut remove_from_ours_raw = HashSet::new();
1074 let mut remove_from_theirs_raw = HashSet::new();
1075
1076 for item in &their_spanned {
1077 let name = &item.value;
1078 if !our_raw_set.contains(name) {
1079 continue;
1080 }
1081
1082 match avail_cmp {
1083 cmp::Ordering::Less => {
1084 remove_from_ours_raw.insert(name.clone());
1085 }
1086 cmp::Ordering::Greater => {
1087 remove_from_theirs_raw.insert(name.clone());
1088 }
1089 cmp::Ordering::Equal => {
1090 remove_from_theirs_raw.insert(name.clone());
1091 }
1092 }
1093 }
1094
1095 if !remove_from_ours_raw.is_empty() {
1096 let new_ours =
1097 our_spanned.into_iter().filter(|s| !remove_from_ours_raw.contains(&s.value)).collect();
1098 ours.set_names(new_ours);
1099 }
1100
1101 if !remove_from_theirs_raw.is_empty() {
1102 let new_theirs = their_spanned
1103 .into_iter()
1104 .filter(|s| !remove_from_theirs_raw.contains(&s.value))
1105 .collect();
1106 theirs.set_names(new_theirs);
1107 }
1108}
1109
1110trait ValueMap {
1112 fn get_mut(&mut self, key: &str) -> Option<&mut Value>;
1113 fn insert(&mut self, key: String, val: Value);
1114}
1115
1116impl ValueMap for Map<String, Value> {
1117 fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
1118 self.get_mut(key)
1119 }
1120
1121 fn insert(&mut self, key: String, val: Value) {
1122 self.insert(key, val);
1123 }
1124}
1125
1126impl ValueMap for IndexMap<String, Value> {
1127 fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
1128 self.get_mut(key)
1129 }
1130
1131 fn insert(&mut self, key: String, val: Value) {
1132 self.insert(key, val);
1133 }
1134}
1135
1136#[derive(Debug, Default, Serialize, PartialEq)]
1137pub struct DocumentContext {
1138 #[serde(skip_serializing_if = "Option::is_none")]
1139 pub include: Option<Vec<ContextSpanned<String>>>,
1140 #[serde(skip_serializing_if = "Option::is_none")]
1141 pub program: Option<ContextSpanned<ContextProgram>>,
1142 #[serde(skip_serializing_if = "Option::is_none")]
1143 pub children: Option<Vec<ContextSpanned<ContextChild>>>,
1144 #[serde(skip_serializing_if = "Option::is_none")]
1145 pub collections: Option<Vec<ContextSpanned<ContextCollection>>>,
1146 #[serde(skip_serializing_if = "Option::is_none")]
1147 pub environments: Option<Vec<ContextSpanned<ContextEnvironment>>>,
1148 #[serde(skip_serializing_if = "Option::is_none")]
1149 pub capabilities: Option<Vec<ContextSpanned<ContextCapability>>>,
1150 #[serde(skip_serializing_if = "Option::is_none")]
1151 pub r#use: Option<Vec<ContextSpanned<ContextUse>>>,
1152 #[serde(skip_serializing_if = "Option::is_none")]
1153 pub expose: Option<Vec<ContextSpanned<ContextExpose>>>,
1154 #[serde(skip_serializing_if = "Option::is_none")]
1155 pub offer: Option<Vec<ContextSpanned<ContextOffer>>>,
1156 #[serde(skip_serializing_if = "Option::is_none")]
1157 pub facets: Option<IndexMap<String, ContextSpanned<Value>>>,
1158 #[serde(skip_serializing_if = "Option::is_none")]
1159 pub config: Option<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>,
1160}
1161
1162impl DocumentContext {
1163 pub fn merge_from(
1164 &mut self,
1165 mut other: DocumentContext,
1166 include_path: &path::Path,
1167 ) -> Result<(), Error> {
1168 merge_spanned_vec!(self, other, include);
1169 self.merge_program(&mut other, include_path)?;
1170 merge_spanned_vec!(self, other, children);
1171 merge_spanned_vec!(self, other, collections);
1172 self.merge_environment(&mut other)?;
1173 merge_from_context_capability_field(&mut self.capabilities, &mut other.capabilities)?;
1174 merge_from_context_capability_field(&mut self.r#use, &mut other.r#use)?;
1175 merge_from_context_capability_field(&mut self.expose, &mut other.expose)?;
1176 merge_from_context_capability_field(&mut self.offer, &mut other.offer)?;
1177 self.merge_facets(&mut other, include_path)?;
1178 self.merge_config(&mut other)?;
1179 Ok(())
1180 }
1181
1182 pub fn canonicalize(&mut self) {
1183 if let Some(children) = &mut self.children {
1184 children.sort_by(|a, b| a.value.name.cmp(&b.value.name));
1185 }
1186 if let Some(collections) = &mut self.collections {
1187 collections.sort_by(|a, b| a.value.name.cmp(&b.value.name));
1188 }
1189 if let Some(environments) = &mut self.environments {
1190 environments.sort_by(|a, b| a.value.name.cmp(&b.value.name));
1191 }
1192 if let Some(capabilities) = &mut self.capabilities {
1193 capabilities.canonicalize_context();
1194 }
1195 if let Some(offers) = &mut self.offer {
1196 offers.canonicalize_context();
1197 }
1198 if let Some(expose) = &mut self.expose {
1199 expose.canonicalize_context();
1200 }
1201 if let Some(r#use) = &mut self.r#use {
1202 r#use.canonicalize_context();
1203 }
1204 }
1205
1206 pub fn all_storage_with_sources<'a>(&'a self) -> HashMap<Name, &'a CapabilityFromRef> {
1207 if let Some(capabilities) = self.capabilities.as_ref() {
1208 capabilities
1209 .iter()
1210 .filter_map(|cap_wrapper| {
1211 let c = &cap_wrapper.value;
1212
1213 let storage_span_opt = c.storage.as_ref();
1214 let source_span_opt = c.from.as_ref();
1215
1216 match (storage_span_opt, source_span_opt) {
1217 (Some(s_span), Some(f_span)) => {
1218 let name_ref: Name = s_span.value.clone();
1219 let source_ref: &CapabilityFromRef = &f_span.value;
1220
1221 Some((name_ref, source_ref))
1222 }
1223 _ => None,
1224 }
1225 })
1226 .collect()
1227 } else {
1228 HashMap::new()
1229 }
1230 }
1231
1232 pub fn all_capability_names(&self) -> HashSet<Name> {
1233 self.capabilities
1234 .as_ref()
1235 .map(|c| {
1236 c.iter()
1237 .flat_map(|capability_wrapper| capability_wrapper.value.names())
1238 .map(|spanned_name| spanned_name.value)
1239 .collect()
1240 })
1241 .unwrap_or_default()
1242 }
1243
1244 pub fn all_collection_names(&self) -> HashSet<Name> {
1245 if let Some(collections) = self.collections.as_ref() {
1246 collections.iter().map(|c| c.value.name.value.clone()).collect()
1247 } else {
1248 HashSet::new()
1249 }
1250 }
1251
1252 pub fn all_config_names(&self) -> HashSet<Name> {
1253 self.capabilities
1254 .as_ref()
1255 .map(|caps| {
1256 caps.iter()
1257 .filter_map(|cap_wrapper| {
1258 let cap = &cap_wrapper.value;
1259
1260 cap.config.as_ref().map(|spanned_key| spanned_key.value.clone())
1261 })
1262 .collect()
1263 })
1264 .unwrap_or_default()
1265 }
1266
1267 pub fn all_children_names(&self) -> HashSet<Name> {
1268 if let Some(children) = self.children.as_ref() {
1269 children.iter().map(|c| c.value.name.value.clone()).collect()
1270 } else {
1271 HashSet::new()
1272 }
1273 }
1274
1275 pub fn all_dictionaries<'a>(&'a self) -> HashMap<Name, &'a ContextCapability> {
1276 if let Some(capabilities) = self.capabilities.as_ref() {
1277 capabilities
1278 .iter()
1279 .filter_map(|cap_wrapper| {
1280 let cap = &cap_wrapper.value;
1281 let dict_span_opt = cap.dictionary.as_ref();
1282
1283 dict_span_opt.and_then(|dict_span| {
1284 let name_value = &dict_span.value;
1285 let name: Name = name_value.clone();
1286 Some((name, cap))
1287 })
1288 })
1289 .collect()
1290 } else {
1291 HashMap::new()
1292 }
1293 }
1294
1295 fn merge_program(
1296 &mut self,
1297 other: &mut DocumentContext,
1298 include_path: &path::Path,
1299 ) -> Result<(), Error> {
1300 if other.program.is_none() {
1301 return Ok(());
1302 }
1303 if self.program.is_none() {
1304 self.program = other.program.clone();
1305 return Ok(());
1306 }
1307
1308 let my_program = &mut self.program.as_mut().unwrap().value;
1309 let other_wrapper = other.program.as_mut().unwrap();
1310
1311 let other_origin = other_wrapper.origin.clone();
1312 let other_program_val = &mut other_wrapper.value;
1313
1314 if let Some(other_runner) = other_program_val.runner.take() {
1315 if let Some(my_runner) = my_program.runner.as_ref() {
1316 if my_runner.value != other_runner.value {
1317 return Err(Error::merge(
1318 format!(
1319 "Manifest include had a conflicting `program.runner`: parent='{}', include='{}'",
1320 my_runner.value, other_runner.value
1321 ),
1322 Some(other_runner.origin),
1323 ));
1324 }
1325 } else {
1326 my_program.runner = Some(other_runner);
1327 }
1328 }
1329
1330 Self::merge_maps_unified(
1331 &mut my_program.info,
1332 &other_program_val.info,
1333 "program",
1334 include_path,
1335 Some(&other_origin),
1336 Some(&vec!["environ", "features"]),
1337 )
1338 }
1339
1340 fn merge_environment(&mut self, other: &mut DocumentContext) -> Result<(), Error> {
1341 if other.environments.is_none() {
1342 return Ok(());
1343 }
1344 if self.environments.is_none() {
1345 self.environments = Some(vec![]);
1346 }
1347
1348 let merged_results = {
1349 let my_environments = self.environments.as_mut().unwrap();
1350 let other_environments = other.environments.as_mut().unwrap();
1351
1352 my_environments.sort_by(|x, y| x.value.name.value.cmp(&y.value.name.value));
1353 other_environments.sort_by(|x, y| x.value.name.value.cmp(&y.value.name.value));
1354
1355 let all_environments =
1356 my_environments.drain(..).merge_by(other_environments.drain(..), |x, y| {
1357 x.value.name.value <= y.value.name.value
1358 });
1359
1360 let groups = all_environments.chunk_by(|e| e.value.name.value.clone());
1361
1362 let mut results = vec![];
1363 for (_name_value, group) in &groups {
1364 let mut group_iter = group.into_iter();
1365 let first_wrapper = group_iter.next().expect("chunk cannot be empty");
1366 let first_origin = first_wrapper.origin.clone();
1367 let mut merged_inner = first_wrapper.value;
1368
1369 for subsequent in group_iter {
1370 merged_inner.merge_from(subsequent.value)?;
1371 }
1372
1373 results.push(ContextSpanned { value: merged_inner, origin: first_origin });
1374 }
1375 results
1376 };
1377
1378 self.environments = Some(merged_results);
1379 Ok(())
1380 }
1381
1382 fn merge_facets(
1383 &mut self,
1384 other: &mut DocumentContext,
1385 include_path: &path::Path,
1386 ) -> Result<(), Error> {
1387 if let None = other.facets {
1388 return Ok(());
1389 }
1390 if let None = self.facets {
1391 self.facets = Some(Default::default());
1392 }
1393 let other_facets = other.facets.as_ref().unwrap();
1394
1395 for (key, include_spanned) in other_facets {
1396 let entry_origin = Some(&include_spanned.origin);
1397 let my_facets = self.facets.as_mut().unwrap();
1398
1399 if !my_facets.contains_key(key) {
1400 my_facets.insert(key.clone(), include_spanned.clone());
1401 } else {
1402 let self_spanned = my_facets.get_mut(key).unwrap();
1403 match (&mut self_spanned.value, &include_spanned.value) {
1404 (
1405 serde_json::Value::Object(self_obj),
1406 serde_json::Value::Object(include_obj),
1407 ) => {
1408 Self::merge_maps_unified(
1409 self_obj,
1410 include_obj,
1411 &format!("facets.{}", key),
1412 include_path,
1413 entry_origin,
1414 None,
1415 )?;
1416 }
1417 (v1, v2) => {
1418 if v1 != v2 {
1419 return Err(Error::merge(
1420 format!(
1421 "Manifest include '{}' had a conflicting value for field \"facets.{}\"",
1422 include_path.display(),
1423 key
1424 ),
1425 entry_origin.cloned(),
1426 ));
1427 }
1428 }
1429 }
1430 }
1431 }
1432 Ok(())
1433 }
1434
1435 fn merge_config(&mut self, other: &mut DocumentContext) -> Result<(), Error> {
1436 if other.config.is_none() {
1437 return Ok(());
1438 }
1439 if self.config.is_none() {
1440 self.config = Some(BTreeMap::new());
1441 }
1442
1443 let my_config = self.config.as_mut().unwrap();
1444 let other_config = other.config.as_ref().unwrap();
1445
1446 for (key, other_spanned) in other_config {
1447 if let Some(my_spanned) = my_config.get(key) {
1448 if my_spanned.value != other_spanned.value {
1449 return Err(Error::merge(
1450 format!("Conflicting configuration key found: '{}'", key),
1451 Some(other_spanned.origin.clone()),
1452 ));
1453 }
1454 } else {
1455 my_config.insert(key.clone(), other_spanned.clone());
1456 }
1457 }
1458 Ok(())
1459 }
1460
1461 fn merge_maps_unified<'s, Source, Dest>(
1462 self_map: &mut Dest,
1463 include_map: Source,
1464 outer_key: &str,
1465 include_path: &path::Path,
1466 origin: Option<&Arc<PathBuf>>,
1467 allow_array_concatenation_keys: Option<&Vec<&str>>,
1468 ) -> Result<(), Error>
1469 where
1470 Source: IntoIterator<Item = (&'s String, &'s serde_json::Value)>,
1471 Dest: ValueMap,
1472 {
1473 for (key, include_val) in include_map {
1474 match self_map.get_mut(key) {
1475 None => {
1476 self_map.insert(key.clone(), include_val.clone());
1477 }
1478 Some(self_val) => match (self_val, include_val) {
1479 (serde_json::Value::Object(s_inner), serde_json::Value::Object(i_inner)) => {
1480 let combined_key = format!("{}.{}", outer_key, key);
1481 Self::merge_maps_unified(
1482 s_inner,
1483 i_inner,
1484 &combined_key,
1485 include_path,
1486 origin,
1487 allow_array_concatenation_keys,
1488 )?;
1489 }
1490 (serde_json::Value::Array(s_arr), serde_json::Value::Array(i_arr)) => {
1491 let is_allowed = allow_array_concatenation_keys
1492 .map_or(true, |keys| keys.contains(&key.as_str()));
1493
1494 if is_allowed {
1495 s_arr.extend(i_arr.clone());
1496 } else if s_arr != i_arr {
1497 return Err(Error::merge(
1498 format!(
1499 "Conflicting array values for field \"{}.{}\"",
1500 outer_key, key
1501 ),
1502 origin.cloned(),
1503 ));
1504 }
1505 }
1506 (v1, v2) if v1 == v2 => {}
1507 _ => {
1508 return Err(Error::merge(
1509 format!(
1510 "Manifest include '{}' had a conflicting value for field \"{}.{}\"",
1511 include_path.display(),
1512 outer_key,
1513 key
1514 ),
1515 origin.cloned(),
1516 ));
1517 }
1518 },
1519 }
1520 }
1521 Ok(())
1522 }
1523
1524 pub fn includes(&self) -> Vec<String> {
1525 self.include
1526 .as_ref()
1527 .map(|includes| includes.iter().map(|s| s.value.clone()).collect())
1528 .unwrap_or_default()
1529 }
1530}
1531
1532pub fn parse_and_hydrate(
1533 file_arc: Arc<PathBuf>,
1534 buffer: &String,
1535) -> Result<DocumentContext, Error> {
1536 let parsed_doc: Document = serde_json5::from_str(buffer).map_err(|e| {
1537 let serde_json5::Error::Message { location, msg } = e;
1538 let location = location.map(|l| Location { line: l.line, column: l.column });
1539 Error::parse(msg, location, Some(&(*file_arc).clone()))
1540 })?;
1541
1542 let include = parsed_doc.include.map(|raw_includes| {
1543 raw_includes
1544 .into_iter()
1545 .map(|path| hydrate_simple(path, &file_arc))
1546 .collect::<Vec<ContextSpanned<String>>>()
1547 });
1548
1549 let facets = parsed_doc.facets.map(|raw_facets| {
1550 raw_facets
1551 .into_iter()
1552 .map(|(key, val)| (key, hydrate_simple(val, &file_arc)))
1553 .collect::<IndexMap<String, ContextSpanned<serde_json::Value>>>()
1554 });
1555
1556 let config = parsed_doc.config.map(|raw_config| {
1557 raw_config
1558 .into_iter()
1559 .map(|(key, val)| (key, hydrate_simple(val, &file_arc)))
1560 .collect::<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>()
1561 });
1562
1563 Ok(DocumentContext {
1564 include,
1565 program: hydrate_opt(parsed_doc.program, &file_arc)?,
1566 children: hydrate_list(parsed_doc.children, &file_arc)?,
1567 collections: hydrate_list(parsed_doc.collections, &file_arc)?,
1568 environments: hydrate_list(parsed_doc.environments, &file_arc)?,
1569 capabilities: hydrate_list(parsed_doc.capabilities, &file_arc)?,
1570 r#use: hydrate_list(parsed_doc.r#use, &file_arc)?,
1571 expose: hydrate_list(parsed_doc.expose, &file_arc)?,
1572 offer: hydrate_list(parsed_doc.offer, &file_arc)?,
1573 facets,
1574 config,
1575 })
1576}
1577
1578#[cfg(test)]
1579mod tests {
1580 use super::*;
1581 use crate::OneOrMany;
1582 use crate::types::document::Document;
1583 use crate::types::offer::OfferFromRef;
1584 use crate::types::right::Right;
1585 use difference::Changeset;
1586 use serde_json::{json, to_string_pretty, to_value};
1587 use std::path;
1588 use std::path::Path;
1589 use test_case::test_case;
1590
1591 fn document(contents: serde_json::Value) -> Document {
1592 serde_json5::from_str::<Document>(&contents.to_string()).unwrap()
1593 }
1594
1595 fn document_context(contents: &str) -> DocumentContext {
1596 let file_arc = Arc::new("test.cml".into());
1597 parse_and_hydrate(file_arc, &contents.to_string()).unwrap()
1598 }
1599
1600 macro_rules! assert_json_eq {
1601 ($a:expr, $e:expr) => {{
1602 if $a != $e {
1603 let expected = to_string_pretty(&$e).unwrap();
1604 let actual = to_string_pretty(&$a).unwrap();
1605 assert_eq!(
1606 $a,
1607 $e,
1608 "JSON actual != expected. Diffs:\n\n{}",
1609 Changeset::new(&actual, &expected, "\n")
1610 );
1611 }
1612 }};
1613 }
1614
1615 #[test]
1616 fn test_includes_v1() {
1617 assert_eq!(document(json!({})).includes(), Vec::<String>::new());
1618 assert_eq!(document(json!({ "include": []})).includes(), Vec::<String>::new());
1619 assert_eq!(
1620 document(json!({ "include": [ "foo.cml", "bar.cml" ]})).includes(),
1621 vec!["foo.cml", "bar.cml"]
1622 );
1623 }
1624
1625 #[test]
1626 fn test_includes() {
1627 let buffer = r##"{}"##;
1628 let empty_document = document_context(buffer);
1629 assert_eq!(empty_document.includes(), Vec::<String>::new());
1630
1631 let buffer = r##"{"include": []}"##;
1632 let empty_include = document_context(buffer);
1633 assert_eq!(empty_include.includes(), Vec::<String>::new());
1634
1635 let buffer = r##"{ "include": [ "foo.cml", "bar.cml" ]}"##;
1636 let include_doc = document_context(buffer);
1637
1638 assert_eq!(include_doc.includes(), vec!["foo.cml", "bar.cml"]);
1639 }
1640
1641 #[test]
1642 fn test_merge_same_section_v1() {
1643 let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1644 let mut other = document(json!({ "use": [{ "protocol": "bar" }] }));
1645 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1646 let uses = some.r#use.as_ref().unwrap();
1647 assert_eq!(uses.len(), 2);
1648 assert_eq!(
1649 uses[0].protocol.as_ref().unwrap(),
1650 &OneOrMany::One("foo".parse::<Name>().unwrap())
1651 );
1652 assert_eq!(
1653 uses[1].protocol.as_ref().unwrap(),
1654 &OneOrMany::One("bar".parse::<Name>().unwrap())
1655 );
1656 }
1657
1658 #[test]
1659 fn test_merge_same_section() {
1660 let mut some = document_context(r##"{ "use": [{ "protocol": "foo" }] }"##);
1661 let other = document_context(r##"{ "use": [{ "protocol": "bar" }] }"##);
1662 some.merge_from(other, &Path::new("some/path")).unwrap();
1663 let uses = some.r#use.as_ref().unwrap();
1664 assert_eq!(uses.len(), 2);
1665 let get_protocol = |u: &ContextSpanned<ContextUse>| -> String {
1666 let proto_wrapper = u.value.protocol.as_ref().expect("Missing protocol");
1667
1668 match &proto_wrapper.value {
1669 OneOrMany::One(name) => name.to_string(),
1670 OneOrMany::Many(_) => panic!("Expected single protocol, found list"),
1671 }
1672 };
1673
1674 assert_eq!(get_protocol(&uses[0]), "foo");
1675 assert_eq!(get_protocol(&uses[1]), "bar");
1676 }
1677
1678 #[test]
1679 fn test_merge_upgraded_availability_v1() {
1680 let mut some =
1681 document(json!({ "use": [{ "protocol": "foo", "availability": "optional" }] }));
1682 let mut other1 = document(json!({ "use": [{ "protocol": "foo" }] }));
1683 let mut other2 =
1684 document(json!({ "use": [{ "protocol": "foo", "availability": "transitional" }] }));
1685 let mut other3 =
1686 document(json!({ "use": [{ "protocol": "foo", "availability": "same_as_target" }] }));
1687 some.merge_from(&mut other1, &Path::new("some/path")).unwrap();
1688 some.merge_from(&mut other2, &Path::new("some/path")).unwrap();
1689 some.merge_from(&mut other3, &Path::new("some/path")).unwrap();
1690 let uses = some.r#use.as_ref().unwrap();
1691 assert_eq!(uses.len(), 2);
1692 assert_eq!(
1693 uses[0].protocol.as_ref().unwrap(),
1694 &OneOrMany::One("foo".parse::<Name>().unwrap())
1695 );
1696 assert!(uses[0].availability.is_none());
1697 assert_eq!(
1698 uses[1].protocol.as_ref().unwrap(),
1699 &OneOrMany::One("foo".parse::<Name>().unwrap())
1700 );
1701 assert_eq!(uses[1].availability.as_ref().unwrap(), &Availability::SameAsTarget,);
1702 }
1703
1704 #[test]
1705 fn test_merge_upgraded_availability() {
1706 let mut some =
1707 document_context(r##"{ "use": [{ "protocol": "foo", "availability": "optional" }] }"##);
1708 let other1 = document_context(r##"{ "use": [{ "protocol": "foo" }] }"##);
1709 let other2 = document_context(
1710 r##"{ "use": [{ "protocol": "foo", "availability": "transitional" }] }"##,
1711 );
1712 let other3 = document_context(
1713 r##"{ "use": [{ "protocol": "foo", "availability": "same_as_target" }] }"##,
1714 );
1715 some.merge_from(other1, &Path::new("some/path")).unwrap();
1716 some.merge_from(other2, &Path::new("some/path")).unwrap();
1717 some.merge_from(other3, &Path::new("some/path")).unwrap();
1718
1719 let uses = some.r#use.as_ref().unwrap();
1720 assert_eq!(uses.len(), 2);
1721 assert_eq!(
1722 uses[0].protocol().as_ref().unwrap().value,
1723 OneOrMany::One("foo".parse::<Name>().unwrap().as_ref())
1724 );
1725 assert!(uses[0].availability().is_none());
1726 assert_eq!(
1727 uses[1].protocol().as_ref().unwrap().value,
1728 OneOrMany::One("foo".parse::<Name>().unwrap().as_ref())
1729 );
1730 assert_eq!(uses[1].availability().as_ref().unwrap().value, Availability::SameAsTarget,);
1731 }
1732
1733 #[test]
1734 fn test_merge_different_sections_v1() {
1735 let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1736 let mut other = document(json!({ "expose": [{ "protocol": "bar", "from": "self" }] }));
1737 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1738 let uses = some.r#use.as_ref().unwrap();
1739 let exposes = some.expose.as_ref().unwrap();
1740 assert_eq!(uses.len(), 1);
1741 assert_eq!(exposes.len(), 1);
1742 assert_eq!(
1743 uses[0].protocol.as_ref().unwrap(),
1744 &OneOrMany::One("foo".parse::<Name>().unwrap())
1745 );
1746 assert_eq!(
1747 exposes[0].protocol.as_ref().unwrap(),
1748 &OneOrMany::One("bar".parse::<Name>().unwrap())
1749 );
1750 }
1751
1752 #[test]
1753 fn test_merge_different_sections() {
1754 let mut some = document_context(r##"{ "use": [{ "protocol": "foo" }] }"##);
1755 let other = document_context(r##"{ "expose": [{ "protocol": "bar", "from": "self" }] }"##);
1756 some.merge_from(other, &Path::new("some/path")).unwrap();
1757 let uses = some.r#use.as_ref().unwrap();
1758 let exposes = some.expose.as_ref().unwrap();
1759 assert_eq!(uses.len(), 1);
1760 assert_eq!(exposes.len(), 1);
1761 assert_eq!(
1762 uses[0].protocol().as_ref().unwrap().value,
1763 OneOrMany::One("foo".parse::<Name>().unwrap().as_ref())
1764 );
1765 assert_eq!(
1766 exposes[0].protocol().as_ref().unwrap().value,
1767 OneOrMany::One("bar".parse::<Name>().unwrap().as_ref())
1768 );
1769 }
1770
1771 #[test]
1772 fn test_merge_environments_v1() {
1773 let mut some = document(json!({ "environments": [
1774 {
1775 "name": "one",
1776 "extends": "realm",
1777 },
1778 {
1779 "name": "two",
1780 "extends": "none",
1781 "runners": [
1782 {
1783 "runner": "r1",
1784 "from": "#c1",
1785 },
1786 {
1787 "runner": "r2",
1788 "from": "#c2",
1789 },
1790 ],
1791 "resolvers": [
1792 {
1793 "resolver": "res1",
1794 "from": "#c1",
1795 "scheme": "foo",
1796 },
1797 ],
1798 "debug": [
1799 {
1800 "protocol": "baz",
1801 "from": "#c2"
1802 }
1803 ]
1804 },
1805 ]}));
1806 let mut other = document(json!({ "environments": [
1807 {
1808 "name": "two",
1809 "__stop_timeout_ms": 100,
1810 "runners": [
1811 {
1812 "runner": "r3",
1813 "from": "#c3",
1814 },
1815 ],
1816 "resolvers": [
1817 {
1818 "resolver": "res2",
1819 "from": "#c1",
1820 "scheme": "bar",
1821 },
1822 ],
1823 "debug": [
1824 {
1825 "protocol": "faz",
1826 "from": "#c2"
1827 }
1828 ]
1829 },
1830 {
1831 "name": "three",
1832 "__stop_timeout_ms": 1000,
1833 },
1834 ]}));
1835 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1836 assert_eq!(
1837 to_value(some).unwrap(),
1838 json!({"environments": [
1839 {
1840 "name": "one",
1841 "extends": "realm",
1842 },
1843 {
1844 "name": "three",
1845 "__stop_timeout_ms": 1000,
1846 },
1847 {
1848 "name": "two",
1849 "extends": "none",
1850 "__stop_timeout_ms": 100,
1851 "runners": [
1852 {
1853 "runner": "r1",
1854 "from": "#c1",
1855 },
1856 {
1857 "runner": "r2",
1858 "from": "#c2",
1859 },
1860 {
1861 "runner": "r3",
1862 "from": "#c3",
1863 },
1864 ],
1865 "resolvers": [
1866 {
1867 "resolver": "res1",
1868 "from": "#c1",
1869 "scheme": "foo",
1870 },
1871 {
1872 "resolver": "res2",
1873 "from": "#c1",
1874 "scheme": "bar",
1875 },
1876 ],
1877 "debug": [
1878 {
1879 "protocol": "baz",
1880 "from": "#c2"
1881 },
1882 {
1883 "protocol": "faz",
1884 "from": "#c2"
1885 }
1886 ]
1887 },
1888 ]})
1889 );
1890 }
1891
1892 #[test]
1893 fn test_merge_environments() {
1894 let mut some = document_context(
1895 r##"
1896 { "environments": [
1897 {
1898 "name": "one",
1899 "extends": "realm"
1900 },
1901 {
1902 "name": "two",
1903 "extends": "none",
1904 "runners": [
1905 {
1906 "runner": "r1",
1907 "from": "#c1"
1908 },
1909 {
1910 "runner": "r2",
1911 "from": "#c2"
1912 }
1913 ],
1914 "resolvers": [
1915 {
1916 "resolver": "res1",
1917 "from": "#c1",
1918 "scheme": "foo"
1919 }
1920 ],
1921 "debug": [
1922 {
1923 "protocol": "baz",
1924 "from": "#c2"
1925 }
1926 ]
1927 }
1928 ]}"##,
1929 );
1930 let other = document_context(
1931 r##"
1932 { "environments": [
1933 {
1934 "name": "two",
1935 "__stop_timeout_ms": 100,
1936 "runners": [
1937 {
1938 "runner": "r3",
1939 "from": "#c3"
1940 }
1941 ],
1942 "resolvers": [
1943 {
1944 "resolver": "res2",
1945 "from": "#c1",
1946 "scheme": "bar"
1947 }
1948 ],
1949 "debug": [
1950 {
1951 "protocol": "faz",
1952 "from": "#c2"
1953 }
1954 ]
1955 },
1956 {
1957 "name": "three",
1958 "__stop_timeout_ms": 1000
1959 }
1960 ]}"##,
1961 );
1962 some.merge_from(other, &Path::new("some/path")).unwrap();
1963 assert_eq!(
1964 to_value(some).unwrap(),
1965 json!({"environments": [
1966 {
1967 "name": "one",
1968 "extends": "realm",
1969 },
1970 {
1971 "name": "three",
1972 "__stop_timeout_ms": 1000,
1973 },
1974 {
1975 "name": "two",
1976 "extends": "none",
1977 "__stop_timeout_ms": 100,
1978 "runners": [
1979 {
1980 "runner": "r1",
1981 "from": "#c1",
1982 },
1983 {
1984 "runner": "r2",
1985 "from": "#c2",
1986 },
1987 {
1988 "runner": "r3",
1989 "from": "#c3",
1990 },
1991 ],
1992 "resolvers": [
1993 {
1994 "resolver": "res1",
1995 "from": "#c1",
1996 "scheme": "foo",
1997 },
1998 {
1999 "resolver": "res2",
2000 "from": "#c1",
2001 "scheme": "bar",
2002 },
2003 ],
2004 "debug": [
2005 {
2006 "protocol": "baz",
2007 "from": "#c2"
2008 },
2009 {
2010 "protocol": "faz",
2011 "from": "#c2"
2012 }
2013 ]
2014 },
2015 ]})
2016 );
2017 }
2018
2019 #[test]
2020 fn test_merge_environments_errors_v1() {
2021 {
2022 let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
2023 let mut other = document(json!({"environments": [{"name": "one", "extends": "none"}]}));
2024 assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
2025 }
2026 {
2027 let mut some =
2028 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
2029 let mut other =
2030 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 20}]}));
2031 assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
2032 }
2033
2034 {
2036 let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
2037 let mut other =
2038 document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
2039 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2040 assert_eq!(
2041 to_value(some).unwrap(),
2042 json!({"environments": [{"name": "one", "extends": "realm"}]})
2043 );
2044 }
2045 {
2046 let mut some =
2047 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
2048 let mut other =
2049 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
2050 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2051 assert_eq!(
2052 to_value(some).unwrap(),
2053 json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]})
2054 );
2055 }
2056 }
2057
2058 #[test]
2059 fn test_merge_environments_errors() {
2060 {
2061 let mut some =
2062 document_context(r##"{"environments": [{"name": "one", "extends": "realm"}]}"##);
2063 let other =
2064 document_context(r##"{"environments": [{"name": "one", "extends": "none"}]}"##);
2065 assert!(some.merge_from(other, &Path::new("some/path")).is_err());
2066 }
2067 {
2068 let mut some = document_context(
2069 r##"{"environments": [{"name": "one", "__stop_timeout_ms": 10}]}"##,
2070 );
2071 let other = document_context(
2072 r##"{"environments": [{"name": "one", "__stop_timeout_ms": 20}]}"##,
2073 );
2074 assert!(some.merge_from(other, &Path::new("some/path")).is_err());
2075 }
2076
2077 {
2079 let mut some =
2080 document_context(r##"{"environments": [{"name": "one", "extends": "realm"}]}"##);
2081 let other =
2082 document_context(r##"{"environments": [{"name": "one", "extends": "realm"}]}"##);
2083 some.merge_from(other, &Path::new("some/path")).unwrap();
2084 assert_eq!(
2085 to_value(some).unwrap(),
2086 json!({"environments": [{"name": "one", "extends": "realm"}]})
2087 );
2088 }
2089 {
2090 let mut some = document_context(
2091 r##"{"environments": [{"name": "one", "__stop_timeout_ms": 10}]}"##,
2092 );
2093 let other = document_context(
2094 r##"{"environments": [{"name": "one", "__stop_timeout_ms": 10}]}"##,
2095 );
2096 some.merge_from(other, &Path::new("some/path")).unwrap();
2097 assert_eq!(
2098 to_value(some).unwrap(),
2099 json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]})
2100 );
2101 }
2102 }
2103
2104 #[test]
2105 fn test_merge_from_other_config_v1() {
2106 let mut some = document(json!({}));
2107 let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
2108
2109 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2110 let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
2111 assert_eq!(some.config, expected.config);
2112 }
2113
2114 #[test]
2115 fn test_merge_from_other_config() {
2116 let mut some = document_context(r##"{}"##);
2117 let other = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2118
2119 some.merge_from(other, &path::Path::new("some/path")).unwrap();
2120 let expected = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2121 assert_eq!(some.config, expected.config);
2122 }
2123
2124 #[test]
2125 fn test_merge_from_some_config_v1() {
2126 let mut some = document(json!({ "config": { "bar": { "type": "bool" } } }));
2127 let mut other = document(json!({}));
2128
2129 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2130 let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
2131 assert_eq!(some.config, expected.config);
2132 }
2133
2134 #[test]
2135 fn test_merge_from_some_config() {
2136 let mut some = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2137 let other = document_context(r##"{}"##);
2138
2139 some.merge_from(other, &path::Path::new("some/path")).unwrap();
2140 let expected = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2141 assert_eq!(some.config, expected.config);
2142 }
2143
2144 #[test]
2145 fn test_merge_from_config_v1() {
2146 let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
2147 let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
2148 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2149
2150 assert_eq!(
2151 some,
2152 document(json!({
2153 "config": {
2154 "foo": { "type": "bool" },
2155 "bar": { "type": "bool" },
2156 }
2157 })),
2158 );
2159 }
2160
2161 #[test]
2162 fn test_merge_from_config() {
2163 let mut some = document_context(r##"{ "config": { "foo": { "type": "bool" } } }"##);
2164 let other = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2165 some.merge_from(other, &path::Path::new("some/path")).unwrap();
2166
2167 assert_eq!(
2168 to_value(some).unwrap(),
2169 json!({
2170 "config": {
2171 "foo": { "type": "bool" },
2172 "bar": { "type": "bool" }
2173 }
2174 }),
2175 );
2176 }
2177
2178 #[test]
2179 fn test_merge_from_config_dedupe_identical_fields_v1() {
2180 let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
2181 let mut other = document(json!({ "config": { "foo": { "type": "bool" } } }));
2182 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2183
2184 assert_eq!(some, document(json!({ "config": { "foo": { "type": "bool" } } })));
2185 }
2186
2187 #[test]
2188 fn test_merge_from_config_dedupe_identical_fields() {
2189 let mut some = document_context(r##"{ "config": { "foo": { "type": "bool" } } }"##);
2190 let other = document_context(r##"{ "config": { "foo": { "type": "bool" } } }"##);
2191 some.merge_from(other, &path::Path::new("some/path")).unwrap();
2192
2193 assert_eq!(to_value(some).unwrap(), json!({ "config": { "foo": { "type": "bool" } } }));
2194 }
2195
2196 #[test]
2197 fn test_merge_from_config_conflicting_keys_v1() {
2198 let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
2199 let mut other = document(json!({ "config": { "foo": { "type": "uint8" } } }));
2200
2201 assert_matches::assert_matches!(
2202 some.merge_from(&mut other, &path::Path::new("some/path")),
2203 Err(Error::Validate { err, .. })
2204 if err == "Found conflicting entry for config key `foo` in `some/path`."
2205 );
2206 }
2207
2208 #[test]
2209 fn test_merge_from_config_conflicting_keys() {
2210 let mut some = document_context(r##"{ "config": { "foo": { "type": "bool" } } }"##);
2211 let other = document_context(r##"{ "config": { "foo": { "type": "uint8" } } }"##);
2212
2213 assert_matches::assert_matches!(
2214 some.merge_from(other, &path::Path::new("some/path")),
2215 Err(Error::Merge { err, .. })
2216 if err == "Conflicting configuration key found: 'foo'"
2217 );
2218 }
2219
2220 #[test]
2221 fn test_canonicalize() {
2222 let mut some = document(json!({
2223 "children": [
2224 { "name": "b_child", "url": "http://foo/b" },
2226 { "name": "a_child", "url": "http://foo/a" },
2227 ],
2228 "environments": [
2229 { "name": "b_env" },
2231 { "name": "a_env" },
2232 ],
2233 "collections": [
2234 { "name": "b_coll", "durability": "transient" },
2236 { "name": "a_coll", "durability": "transient" },
2237 ],
2238 "capabilities": [
2241 { "protocol": ["foo"] },
2243 { "protocol": "bar" },
2244 { "protocol": "arg", "path": "/arg" },
2246 { "service": ["b", "a"] },
2248 { "event_stream": ["b", "a"] },
2250 { "runner": "myrunner" },
2251 { "runner": "mypathrunner1", "path": "/foo" },
2253 { "runner": "mypathrunner2", "path": "/foo" },
2254 ],
2255 "offer": [
2257 { "protocol": "baz", "from": "#a_child", "to": "#c_child" },
2259 { "protocol": ["foo"], "from": "#a_child", "to": "#b_child" },
2261 { "protocol": "bar", "from": "#a_child", "to": "#b_child" },
2262 { "service": ["b", "a"], "from": "#a_child", "to": "#b_child" },
2264 {
2266 "event_stream": ["b", "a"],
2267 "from": "#a_child",
2268 "to": "#b_child",
2269 "scope": ["#b", "#c", "#a"] },
2271 { "runner": [ "myrunner", "a" ], "from": "#a_child", "to": "#b_child" },
2272 { "runner": [ "b" ], "from": "#a_child", "to": "#b_child" },
2273 { "directory": [ "b" ], "from": "#a_child", "to": "#b_child" },
2274 ],
2275 "expose": [
2276 { "protocol": ["foo"], "from": "#a_child" },
2277 { "protocol": "bar", "from": "#a_child" }, { "service": ["b", "a"], "from": "#a_child" },
2280 {
2282 "event_stream": ["b", "a"],
2283 "from": "#a_child",
2284 "scope": ["#b", "#c", "#a"] },
2286 { "runner": [ "myrunner", "a" ], "from": "#a_child" },
2287 { "runner": [ "b" ], "from": "#a_child" },
2288 { "directory": [ "b" ], "from": "#a_child" },
2289 ],
2290 "use": [
2291 { "protocol": ["zazzle"], "path": "/zazbaz" },
2293 { "protocol": ["foo"] },
2295 { "protocol": "bar" },
2296 { "service": ["b", "a"] },
2298 { "event_stream": ["b", "a"], "scope": ["#b", "#a"] },
2300 ],
2301 }));
2302 some.canonicalize();
2303
2304 assert_json_eq!(
2305 some,
2306 document(json!({
2307 "children": [
2308 { "name": "a_child", "url": "http://foo/a" },
2309 { "name": "b_child", "url": "http://foo/b" },
2310 ],
2311 "collections": [
2312 { "name": "a_coll", "durability": "transient" },
2313 { "name": "b_coll", "durability": "transient" },
2314 ],
2315 "environments": [
2316 { "name": "a_env" },
2317 { "name": "b_env" },
2318 ],
2319 "capabilities": [
2320 { "event_stream": ["a", "b"] },
2321 { "protocol": "arg", "path": "/arg" },
2322 { "protocol": ["bar", "foo"] },
2323 { "runner": "mypathrunner1", "path": "/foo" },
2324 { "runner": "mypathrunner2", "path": "/foo" },
2325 { "runner": "myrunner" },
2326 { "service": ["a", "b"] },
2327 ],
2328 "use": [
2329 { "event_stream": ["a", "b"], "scope": ["#a", "#b"] },
2330 { "protocol": ["bar", "foo"] },
2331 { "protocol": "zazzle", "path": "/zazbaz" },
2332 { "service": ["a", "b"] },
2333 ],
2334 "offer": [
2335 { "directory": "b", "from": "#a_child", "to": "#b_child" },
2336 {
2337 "event_stream": ["a", "b"],
2338 "from": "#a_child",
2339 "to": "#b_child",
2340 "scope": ["#a", "#b", "#c"],
2341 },
2342 { "protocol": ["bar", "foo"], "from": "#a_child", "to": "#b_child" },
2343 { "protocol": "baz", "from": "#a_child", "to": "#c_child" },
2344 { "runner": [ "a", "b", "myrunner" ], "from": "#a_child", "to": "#b_child" },
2345 { "service": ["a", "b"], "from": "#a_child", "to": "#b_child" },
2346 ],
2347 "expose": [
2348 { "directory": "b", "from": "#a_child" },
2349 {
2350 "event_stream": ["a", "b"],
2351 "from": "#a_child",
2352 "scope": ["#a", "#b", "#c"],
2353 },
2354 { "protocol": ["bar", "foo"], "from": "#a_child" },
2355 { "runner": [ "a", "b", "myrunner" ], "from": "#a_child" },
2356 { "service": ["a", "b"], "from": "#a_child" },
2357 ],
2358 }))
2359 )
2360 }
2361
2362 #[test]
2363 fn test_canonicalize_context() {
2364 let mut some = document_context(
2365 &json!({
2366 "children": [
2367 { "name": "b_child", "url": "http://foo/b" },
2369 { "name": "a_child", "url": "http://foo/a" },
2370 ],
2371 "environments": [
2372 { "name": "b_env" },
2374 { "name": "a_env" },
2375 ],
2376 "collections": [
2377 { "name": "b_coll", "durability": "transient" },
2379 { "name": "a_coll", "durability": "transient" },
2380 ],
2381 "capabilities": [
2384 { "protocol": ["foo"] },
2386 { "protocol": "bar" },
2387 { "protocol": "arg", "path": "/arg" },
2389 { "service": ["b", "a"] },
2391 { "event_stream": ["b", "a"] },
2393 { "runner": "myrunner" },
2394 { "runner": "mypathrunner1", "path": "/foo" },
2396 { "runner": "mypathrunner2", "path": "/foo" },
2397 ],
2398 "offer": [
2400 { "protocol": "baz", "from": "#a_child", "to": "#c_child" },
2402 { "protocol": ["foo"], "from": "#a_child", "to": "#b_child" },
2404 { "protocol": "bar", "from": "#a_child", "to": "#b_child" },
2405 { "service": ["b", "a"], "from": "#a_child", "to": "#b_child" },
2407 {
2409 "event_stream": ["b", "a"],
2410 "from": "#a_child",
2411 "to": "#b_child",
2412 "scope": ["#b", "#c", "#a"] },
2414 { "runner": [ "myrunner", "a" ], "from": "#a_child", "to": "#b_child" },
2415 { "runner": [ "b" ], "from": "#a_child", "to": "#b_child" },
2416 { "directory": [ "b" ], "from": "#a_child", "to": "#b_child" },
2417 ],
2418 "expose": [
2419 { "protocol": ["foo"], "from": "#a_child" },
2420 { "protocol": "bar", "from": "#a_child" }, { "service": ["b", "a"], "from": "#a_child" },
2423 {
2425 "event_stream": ["b", "a"],
2426 "from": "#a_child",
2427 "scope": ["#b", "#c", "#a"] },
2429 { "runner": [ "myrunner", "a" ], "from": "#a_child" },
2430 { "runner": [ "b" ], "from": "#a_child" },
2431 { "directory": [ "b" ], "from": "#a_child" },
2432 ],
2433 "use": [
2434 { "protocol": ["zazzle"], "path": "/zazbaz" },
2436 { "protocol": ["foo"] },
2438 { "protocol": "bar" },
2439 { "service": ["b", "a"] },
2441 { "event_stream": ["b", "a"], "scope": ["#b", "#a"] },
2443 ],
2444 })
2445 .to_string(),
2446 );
2447 some.canonicalize();
2448
2449 assert_json_eq!(
2450 some,
2451 document_context(&json!({
2452 "children": [
2453 { "name": "a_child", "url": "http://foo/a" },
2454 { "name": "b_child", "url": "http://foo/b" },
2455 ],
2456 "collections": [
2457 { "name": "a_coll", "durability": "transient" },
2458 { "name": "b_coll", "durability": "transient" },
2459 ],
2460 "environments": [
2461 { "name": "a_env" },
2462 { "name": "b_env" },
2463 ],
2464 "capabilities": [
2465 { "event_stream": ["a", "b"] },
2466 { "protocol": "arg", "path": "/arg" },
2467 { "protocol": ["bar", "foo"] },
2468 { "runner": "mypathrunner1", "path": "/foo" },
2469 { "runner": "mypathrunner2", "path": "/foo" },
2470 { "runner": "myrunner" },
2471 { "service": ["a", "b"] },
2472 ],
2473 "use": [
2474 { "event_stream": ["a", "b"], "scope": ["#a", "#b"] },
2475 { "protocol": ["bar", "foo"] },
2476 { "protocol": "zazzle", "path": "/zazbaz" },
2477 { "service": ["a", "b"] },
2478 ],
2479 "offer": [
2480 { "directory": "b", "from": "#a_child", "to": "#b_child" },
2481 {
2482 "event_stream": ["a", "b"],
2483 "from": "#a_child",
2484 "to": "#b_child",
2485 "scope": ["#a", "#b", "#c"],
2486 },
2487 { "protocol": ["bar", "foo"], "from": "#a_child", "to": "#b_child" },
2488 { "protocol": "baz", "from": "#a_child", "to": "#c_child" },
2489 { "runner": [ "a", "b", "myrunner" ], "from": "#a_child", "to": "#b_child" },
2490 { "service": ["a", "b"], "from": "#a_child", "to": "#b_child" },
2491 ],
2492 "expose": [
2493 { "directory": "b", "from": "#a_child" },
2494 {
2495 "event_stream": ["a", "b"],
2496 "from": "#a_child",
2497 "scope": ["#a", "#b", "#c"],
2498 },
2499 { "protocol": ["bar", "foo"], "from": "#a_child" },
2500 { "runner": [ "a", "b", "myrunner" ], "from": "#a_child" },
2501 { "service": ["a", "b"], "from": "#a_child" },
2502 ],
2503 }).to_string())
2504 )
2505 }
2506
2507 #[test]
2508 fn deny_unknown_config_type_fields_v1() {
2509 let input = json!({ "config": { "foo": { "type": "bool", "unknown": "should error" } } });
2510 serde_json5::from_str::<Document>(&input.to_string())
2511 .expect_err("must reject unknown config field attributes");
2512 }
2513
2514 #[test]
2515 fn deny_unknown_config_type_fields() {
2516 let contents =
2517 json!({ "config": { "foo": { "type": "bool", "unknown": "should error" } } });
2518 let file_arc = Arc::new("test.cml".into());
2519 parse_and_hydrate(file_arc, &contents.to_string())
2520 .expect_err("must reject unknown config field attributes");
2521 }
2522
2523 #[test]
2524 fn deny_unknown_config_nested_type_fields() {
2525 let input = json!({
2526 "config": {
2527 "foo": {
2528 "type": "vector",
2529 "max_count": 10,
2530 "element": {
2531 "type": "bool",
2532 "unknown": "should error"
2533 },
2534
2535 }
2536 }
2537 });
2538 serde_json5::from_str::<Document>(&input.to_string())
2539 .expect_err("must reject unknown config field attributes");
2540
2541 let file_arc = Arc::new("test.cml".into());
2542 parse_and_hydrate(file_arc, &input.to_string())
2543 .expect_err("must reject unknown config field attributes");
2544 }
2545
2546 #[test]
2547 fn test_merge_from_program_v1() {
2548 let mut some = document(json!({ "program": { "binary": "bin/hello_world" } }));
2549 let mut other = document(json!({ "program": { "runner": "elf" } }));
2550 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2551 let expected =
2552 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2553 assert_eq!(some.program, expected.program);
2554 }
2555
2556 #[test]
2557 fn test_merge_from_program() {
2558 let mut some =
2559 document_context(&json!({ "program": { "binary": "bin/hello_world" } }).to_string());
2560 let other = document_context(&json!({ "program": { "runner": "elf" } }).to_string());
2561 some.merge_from(other, &Path::new("some/path")).unwrap();
2562 let expected = document_context(
2563 &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2564 );
2565 assert_eq!(some.program, expected.program);
2566 }
2567
2568 #[test]
2569 fn test_merge_from_program_without_runner_v1() {
2570 let mut some =
2571 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2572 let mut other = document(json!({ "program": {} }));
2575 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2576 let expected =
2577 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2578 assert_eq!(some.program, expected.program);
2579 }
2580
2581 #[test]
2582 fn test_merge_from_program_without_runner() {
2583 let mut some = document_context(
2584 &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2585 );
2586 let other = document_context(&json!({ "program": {} }).to_string());
2589 some.merge_from(other, &Path::new("some/path")).unwrap();
2590 let expected = document_context(
2591 &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2592 );
2593 assert_eq!(some.program, expected.program);
2594 }
2595
2596 #[test]
2597 fn test_merge_from_program_overlapping_environ_v1() {
2598 let mut some = document(json!({ "program": { "environ": ["1"] } }));
2600 let mut other = document(json!({ "program": { "environ": ["2"] } }));
2601 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2602 let expected = document(json!({ "program": { "environ": ["1", "2"] } }));
2603 assert_eq!(some.program, expected.program);
2604 }
2605
2606 #[test]
2607 fn test_merge_from_program_overlapping_environ() {
2608 let mut some = document_context(&json!({ "program": { "environ": ["1"] } }).to_string());
2610 let other = document_context(&json!({ "program": { "environ": ["2"] } }).to_string());
2611 some.merge_from(other, &Path::new("some/path")).unwrap();
2612 let expected =
2613 document_context(&json!({ "program": { "environ": ["1", "2"] } }).to_string());
2614 assert_eq!(some.program, expected.program);
2615 }
2616
2617 #[test]
2618 fn test_merge_from_program_overlapping_runner_v1() {
2619 let mut some =
2621 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2622 let mut other = document(json!({ "program": { "runner": "elf" } }));
2623 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2624 let expected =
2625 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2626 assert_eq!(some.program, expected.program);
2627 }
2628
2629 #[test]
2630 fn test_merge_from_program_overlapping_runner() {
2631 let mut some = document_context(
2633 &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2634 );
2635 let other = document_context(&json!({ "program": { "runner": "elf" } }).to_string());
2636 some.merge_from(other, &Path::new("some/path")).unwrap();
2637 let expected = document_context(
2638 &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2639 );
2640 assert_eq!(some.program, expected.program);
2641 }
2642
2643 #[test_case(
2644 document(json!({ "program": { "runner": "elf" } })),
2645 document(json!({ "program": { "runner": "fle" } })),
2646 "runner"
2647 ; "when_runner_conflicts"
2648 )]
2649 #[test_case(
2650 document(json!({ "program": { "binary": "bin/hello_world" } })),
2651 document(json!({ "program": { "binary": "bin/hola_mundo" } })),
2652 "binary"
2653 ; "when_binary_conflicts"
2654 )]
2655 #[test_case(
2656 document(json!({ "program": { "args": ["a".to_owned()] } })),
2657 document(json!({ "program": { "args": ["b".to_owned()] } })),
2658 "args"
2659 ; "when_args_conflicts"
2660 )]
2661 fn test_merge_from_program_error_v1(mut some: Document, mut other: Document, field: &str) {
2662 assert_matches::assert_matches!(
2663 some.merge_from(&mut other, &path::Path::new("some/path")),
2664 Err(Error::Validate { err, .. })
2665 if err == format!("manifest include had a conflicting `program.{}`: some/path", field)
2666 );
2667 }
2668
2669 #[test]
2670 fn test_merge_from_program_error_runner() {
2671 let mut some = document_context(&json!({ "program": { "runner": "elf" } }).to_string());
2672 let other = document_context(&json!({ "program": { "runner": "fle" } }).to_string());
2673 assert_matches::assert_matches!(
2674 some.merge_from(other, &Path::new("some/path")),
2675 Err(Error::Merge { err, .. })
2676 if err == format!("Manifest include had a conflicting `program.runner`: parent='elf', include='fle'"));
2677 }
2678
2679 #[test]
2680 fn test_merge_from_program_error_binary() {
2681 let mut some =
2682 document_context(&json!({ "program": { "binary": "bin/hello_world" } }).to_string());
2683 let other =
2684 document_context(&json!({ "program": { "binary": "bin/hola_mundo" } }).to_string());
2685 assert_matches::assert_matches!(
2686 some.merge_from(other, &Path::new("some/path")),
2687 Err(Error::Merge { err, .. })
2688 if err == format!("Manifest include 'some/path' had a conflicting value for field \"program.binary\""));
2689 }
2690
2691 #[test]
2692 fn test_merge_from_program_error_args() {
2693 let mut some =
2694 document_context(&json!({ "program": { "args": ["a".to_owned()] } }).to_string());
2695 let other =
2696 document_context(&json!({ "program": { "args": ["b".to_owned()] } }).to_string());
2697 assert_matches::assert_matches!(
2698 some.merge_from(other, &Path::new("some/path")),
2699 Err(Error::Merge { err, .. })
2700 if err == format!("Conflicting array values for field \"program.args\""));
2701 }
2702
2703 #[test_case(
2704 document(json!({ "facets": { "my.key": "my.value" } })),
2705 document(json!({ "facets": { "other.key": "other.value" } })),
2706 document(json!({ "facets": { "my.key": "my.value", "other.key": "other.value" } }))
2707 ; "two separate keys"
2708 )]
2709 #[test_case(
2710 document(json!({ "facets": { "my.key": "my.value" } })),
2711 document(json!({ "facets": {} })),
2712 document(json!({ "facets": { "my.key": "my.value" } }))
2713 ; "empty other facet"
2714 )]
2715 #[test_case(
2716 document(json!({ "facets": {} })),
2717 document(json!({ "facets": { "other.key": "other.value" } })),
2718 document(json!({ "facets": { "other.key": "other.value" } }))
2719 ; "empty my facet"
2720 )]
2721 #[test_case(
2722 document(json!({ "facets": { "key": { "type": "some_type" } } })),
2723 document(json!({ "facets": { "key": { "runner": "some_runner"} } })),
2724 document(json!({ "facets": { "key": { "type": "some_type", "runner": "some_runner" } } }))
2725 ; "nested facet key"
2726 )]
2727 #[test_case(
2728 document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "type": "new type" }}}})),
2729 document(json!({ "facets": { "key": { "nested_key": { "runner": "some_runner" }} } })),
2730 document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "runner": "some_runner", "type": "new type" }}}}))
2731 ; "double nested facet key"
2732 )]
2733 #[test_case(
2734 document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2"] } } })),
2735 document(json!({ "facets": { "key": { "array_key": ["value_3", "value_4"] } } })),
2736 document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2", "value_3", "value_4"] } } }))
2737 ; "merge array values"
2738 )]
2739 fn test_merge_from_facets_v1(mut my: Document, mut other: Document, expected: Document) {
2740 my.merge_from(&mut other, &Path::new("some/path")).unwrap();
2741 assert_eq!(my.facets, expected.facets);
2742 }
2743
2744 #[test_case(
2745 document_context(&json!({ "facets": { "my.key": "my.value" } }).to_string()),
2746 document_context(&json!({ "facets": { "other.key": "other.value" } }).to_string()),
2747 document_context(&json!({ "facets": { "my.key": "my.value", "other.key": "other.value" } }).to_string())
2748 ; "two separate keys"
2749 )]
2750 #[test_case(
2751 document_context(&json!({ "facets": { "my.key": "my.value" } }).to_string()),
2752 document_context(&json!({ "facets": {} }).to_string()),
2753 document_context(&json!({ "facets": { "my.key": "my.value" } }).to_string())
2754 ; "empty other facet"
2755 )]
2756 #[test_case(
2757 document_context(&json!({ "facets": {} }).to_string()),
2758 document_context(&json!({ "facets": { "other.key": "other.value" } }).to_string()),
2759 document_context(&json!({ "facets": { "other.key": "other.value" } }).to_string())
2760 ; "empty my facet"
2761 )]
2762 #[test_case(
2763 document_context(&json!({ "facets": { "key": { "type": "some_type" } } }).to_string()),
2764 document_context(&json!({ "facets": { "key": { "runner": "some_runner"} } }).to_string()),
2765 document_context(&json!({ "facets": { "key": { "type": "some_type", "runner": "some_runner" } } }).to_string())
2766 ; "nested facet key"
2767 )]
2768 #[test_case(
2769 document_context(&json!({ "facets": { "key": { "type": "some_type", "nested_key": { "type": "new type" }}}}).to_string()),
2770 document_context(&json!({ "facets": { "key": { "nested_key": { "runner": "some_runner" }} } }).to_string()),
2771 document_context(&json!({ "facets": { "key": { "type": "some_type", "nested_key": { "runner": "some_runner", "type": "new type" }}}}).to_string())
2772 ; "double nested facet key"
2773 )]
2774 #[test_case(
2775 document_context(&json!({ "facets": { "key": { "array_key": ["value_1", "value_2"] } } }).to_string()),
2776 document_context(&json!({ "facets": { "key": { "array_key": ["value_3", "value_4"] } } }).to_string()),
2777 document_context(&json!({ "facets": { "key": { "array_key": ["value_1", "value_2", "value_3", "value_4"] } } }).to_string())
2778 ; "merge array values" )]
2780 fn test_merge_from_facets(
2781 mut my: DocumentContext,
2782 other: DocumentContext,
2783 expected: DocumentContext,
2784 ) {
2785 my.merge_from(other, &Path::new("some/path")).unwrap();
2786 assert_eq!(my.facets, expected.facets);
2787 }
2788
2789 #[test_case(
2790 document(json!({ "facets": { "key": "my.value" }})),
2791 document(json!({ "facets": { "key": "other.value" }})),
2792 "facets.key"
2793 ; "conflict first level keys"
2794 )]
2795 #[test_case(
2796 document(json!({ "facets": { "key": {"type": "cts" }}})),
2797 document(json!({ "facets": { "key": {"type": "system" }}})),
2798 "facets.key.type"
2799 ; "conflict second level keys"
2800 )]
2801 #[test_case(
2802 document(json!({ "facets": { "key": {"type": {"key": "value" }}}})),
2803 document(json!({ "facets": { "key": {"type": "system" }}})),
2804 "facets.key.type"
2805 ; "incompatible self nested type"
2806 )]
2807 #[test_case(
2808 document(json!({ "facets": { "key": {"type": "system" }}})),
2809 document(json!({ "facets": { "key": {"type": {"key": "value" }}}})),
2810 "facets.key.type"
2811 ; "incompatible other nested type"
2812 )]
2813 #[test_case(
2814 document(json!({ "facets": { "key": {"type": {"key": "my.value" }}}})),
2815 document(json!({ "facets": { "key": {"type": {"key": "some.value" }}}})),
2816 "facets.key.type.key"
2817 ; "conflict third level keys"
2818 )]
2819 #[test_case(
2820 document(json!({ "facets": { "key": {"type": [ "value_1" ]}}})),
2821 document(json!({ "facets": { "key": {"type": "value_2" }}})),
2822 "facets.key.type"
2823 ; "incompatible keys"
2824 )]
2825 fn test_merge_from_facet_error_v1(mut my: Document, mut other: Document, field: &str) {
2826 assert_matches::assert_matches!(
2827 my.merge_from(&mut other, &path::Path::new("some/path")),
2828 Err(Error::Validate { err, .. })
2829 if err == format!("manifest include had a conflicting `{}`: some/path", field)
2830 );
2831 }
2832
2833 #[test_case(
2834 document_context(&json!({ "facets": { "key": "my.value" }}).to_string()),
2835 document_context(&json!({ "facets": { "key": "other.value" }}).to_string()),
2836 "facets.key"
2837 ; "conflict first level keys" )]
2839 #[test_case(
2840 document_context(&json!({ "facets": { "key": {"type": "cts" }}}).to_string()),
2841 document_context(&json!({ "facets": { "key": {"type": "system" }}}).to_string()),
2842 "facets.key.type"
2843 ; "conflict second level keys"
2844 )]
2845 #[test_case(
2846 document_context(&json!({ "facets": { "key": {"type": {"key": "value" }}}}).to_string()),
2847 document_context(&json!({ "facets": { "key": {"type": "system" }}}).to_string()),
2848 "facets.key.type"
2849 ; "incompatible self nested type"
2850 )]
2851 #[test_case(
2852 document_context(&json!({ "facets": { "key": {"type": "system" }}}).to_string()),
2853 document_context(&json!({ "facets": { "key": {"type": {"key": "value" }}}}).to_string()),
2854 "facets.key.type"
2855 ; "incompatible other nested type"
2856 )]
2857 #[test_case(
2858 document_context(&json!({ "facets": { "key": {"type": {"key": "my.value" }}}}).to_string()),
2859 document_context(&json!({ "facets": { "key": {"type": {"key": "some.value" }}}}).to_string()),
2860 "facets.key.type.key"
2861 ; "conflict third level keys"
2862 )]
2863 #[test_case(
2864 document_context(&json!({ "facets": { "key": {"type": [ "value_1" ]}}}).to_string()),
2865 document_context(&json!({ "facets": { "key": {"type": "value_2" }}}).to_string()),
2866 "facets.key.type"
2867 ; "incompatible keys"
2868 )]
2869 fn test_merge_from_facet_error(mut my: DocumentContext, other: DocumentContext, field: &str) {
2870 assert_matches::assert_matches!(
2871 my.merge_from(other, &path::Path::new("some/path")),
2872 Err(Error::Merge { err, .. })
2873 if err == format!("Manifest include 'some/path' had a conflicting value for field \"{}\"", field)
2874 );
2875 }
2876
2877 #[test_case("protocol")]
2878 #[test_case("service")]
2879 #[test_case("event_stream")]
2880 fn test_merge_from_duplicate_use_array_v1(typename: &str) {
2881 let mut my = document(json!({ "use": [{ typename: "a" }]}));
2882 let mut other = document(json!({ "use": [
2883 { typename: ["a", "b"], "availability": "optional"}
2884 ]}));
2885 let result = document(json!({ "use": [
2886 { typename: "a" },
2887 { typename: "b", "availability": "optional" },
2888 ]}));
2889
2890 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2891 assert_eq!(my, result);
2892 }
2893
2894 #[test_case("protocol")]
2895 #[test_case("service")]
2896 #[test_case("event_stream")]
2897 fn test_merge_from_duplicate_use_array(typename: &str) {
2898 let mut my = document_context(&json!({ "use": [{ typename: "a" }]}).to_string());
2899 let other = document_context(
2900 &json!({ "use": [
2901 { typename: ["a", "b"], "availability": "optional"}
2902 ]})
2903 .to_string(),
2904 );
2905 let result = document_context(
2906 &json!({ "use": [
2907 { typename: "a" },
2908 { typename: "b", "availability": "optional" },
2909 ]})
2910 .to_string(),
2911 );
2912
2913 my.merge_from(other, &path::Path::new("some/path")).unwrap();
2914 assert_eq!(my, result);
2915 }
2916
2917 #[test_case("directory")]
2918 #[test_case("storage")]
2919 fn test_merge_from_duplicate_use_noarray_v1(typename: &str) {
2920 let mut my = document(json!({ "use": [{ typename: "a", "path": "/a"}]}));
2921 let mut other = document(json!({ "use": [
2922 { typename: "a", "path": "/a", "availability": "optional" },
2923 { typename: "b", "path": "/b", "availability": "optional" },
2924 ]}));
2925 let result = document(json!({ "use": [
2926 { typename: "a", "path": "/a" },
2927 { typename: "b", "path": "/b", "availability": "optional" },
2928 ]}));
2929 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2930 assert_eq!(my, result);
2931 }
2932
2933 #[test_case("directory")]
2934 #[test_case("storage")]
2935 fn test_merge_from_duplicate_use_noarray(typename: &str) {
2936 let mut my =
2937 document_context(&json!({ "use": [{ typename: "a", "path": "/a"}]}).to_string());
2938 let other = document_context(
2939 &json!({ "use": [
2940 { typename: "a", "path": "/a", "availability": "optional" },
2941 { typename: "b", "path": "/b", "availability": "optional" },
2942 ]})
2943 .to_string(),
2944 );
2945 let result = document_context(
2946 &json!({ "use": [
2947 { typename: "a", "path": "/a" },
2948 { typename: "b", "path": "/b", "availability": "optional" },
2949 ]})
2950 .to_string(),
2951 );
2952 my.merge_from(other, &path::Path::new("some/path")).unwrap();
2953 assert_eq!(my, result);
2954 }
2955
2956 #[test_case("protocol")]
2957 #[test_case("service")]
2958 #[test_case("event_stream")]
2959 fn test_merge_from_duplicate_capabilities_array_v1(typename: &str) {
2960 let mut my = document(json!({ "capabilities": [{ typename: "a" }]}));
2961 let mut other = document(json!({ "capabilities": [ { typename: ["a", "b"] } ]}));
2962 let result = document(json!({ "capabilities": [ { typename: "a" }, { typename: "b" } ]}));
2963
2964 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2965 assert_eq!(my, result);
2966 }
2967
2968 #[test_case("protocol")]
2969 #[test_case("service")]
2970 #[test_case("event_stream")]
2971 fn test_merge_from_duplicate_capabilities_array(typename: &str) {
2972 let mut my = document_context(&json!({ "capabilities": [{ typename: "a" }]}).to_string());
2973 let other =
2974 document_context(&json!({ "capabilities": [ { typename: ["a", "b"] } ]}).to_string());
2975 let result = document_context(
2976 &json!({ "capabilities": [ { typename: "a" }, { typename: "b" } ]}).to_string(),
2977 );
2978
2979 my.merge_from(other, &path::Path::new("some/path")).unwrap();
2980 assert_eq!(my, result);
2981 }
2982
2983 #[test_case("directory")]
2984 #[test_case("storage")]
2985 #[test_case("runner")]
2986 #[test_case("resolver")]
2987 fn test_merge_from_duplicate_capabilities_noarray_v1(typename: &str) {
2988 let mut my = document(json!({ "capabilities": [{ typename: "a", "path": "/a"}]}));
2989 let mut other = document(json!({ "capabilities": [
2990 { typename: "a", "path": "/a" },
2991 { typename: "b", "path": "/b" },
2992 ]}));
2993 let result = document(json!({ "capabilities": [
2994 { typename: "a", "path": "/a" },
2995 { typename: "b", "path": "/b" },
2996 ]}));
2997 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2998 assert_eq!(my, result);
2999 }
3000
3001 #[test_case("directory")]
3002 #[test_case("storage")]
3003 #[test_case("runner")]
3004 #[test_case("resolver")]
3005 fn test_merge_from_duplicate_capabilities_noarray(typename: &str) {
3006 let mut my = document_context(
3007 &json!({ "capabilities": [{ typename: "a", "path": "/a"}]}).to_string(),
3008 );
3009 let other = document_context(
3010 &json!({ "capabilities": [
3011 { typename: "a", "path": "/a" },
3012 { typename: "b", "path": "/b" },
3013 ]})
3014 .to_string(),
3015 );
3016 let result = document_context(
3017 &json!({ "capabilities": [
3018 { typename: "a", "path": "/a" },
3019 { typename: "b", "path": "/b" },
3020 ]})
3021 .to_string(),
3022 );
3023 my.merge_from(other, &path::Path::new("some/path")).unwrap();
3024 assert_eq!(my, result);
3025 }
3026
3027 #[test]
3028 fn test_merge_with_empty_names_v1() {
3029 let mut my = document(json!({ "capabilities": [{ "path": "/a"}]}));
3031
3032 let mut other = document(json!({ "capabilities": [
3033 { "directory": "a", "path": "/a" },
3034 { "directory": "b", "path": "/b" },
3035 ]}));
3036 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap_err();
3037 }
3038
3039 #[test]
3040 fn test_merge_with_empty_names() {
3041 let mut my = document_context(&json!({ "capabilities": [{ "path": "/a"}]}).to_string());
3043
3044 let other = document_context(
3045 &json!({ "capabilities": [
3046 { "directory": "a", "path": "/a" },
3047 { "directory": "b", "path": "/b" },
3048 ]})
3049 .to_string(),
3050 );
3051 my.merge_from(other, &path::Path::new("some/path")).unwrap_err();
3052 }
3053
3054 #[test_case("protocol")]
3055 #[test_case("service")]
3056 #[test_case("event_stream")]
3057 #[test_case("directory")]
3058 #[test_case("storage")]
3059 #[test_case("runner")]
3060 #[test_case("resolver")]
3061 fn test_merge_from_duplicate_offers_v1(typename: &str) {
3062 let mut my = document(json!({ "offer": [{ typename: "a", "from": "self", "to": "#c" }]}));
3063 let mut other = document(json!({ "offer": [
3064 { typename: ["a", "b"], "from": "self", "to": "#c", "availability": "optional" }
3065 ]}));
3066 let result = document(json!({ "offer": [
3067 { typename: "a", "from": "self", "to": "#c" },
3068 { typename: "b", "from": "self", "to": "#c", "availability": "optional" },
3069 ]}));
3070
3071 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
3072 assert_eq!(my, result);
3073 }
3074
3075 #[test_case("protocol")]
3076 #[test_case("service")]
3077 #[test_case("event_stream")]
3078 #[test_case("directory")]
3079 #[test_case("storage")]
3080 #[test_case("runner")]
3081 #[test_case("resolver")]
3082 fn test_merge_from_duplicate_offers(typename: &str) {
3083 let mut my = document_context(
3084 &json!({ "offer": [{ typename: "a", "from": "self", "to": "#c" }]}).to_string(),
3085 );
3086 let other = document_context(
3087 &json!({ "offer": [
3088 { typename: ["a", "b"], "from": "self", "to": "#c", "availability": "optional" }
3089 ]})
3090 .to_string(),
3091 );
3092 let result = document_context(
3093 &json!({ "offer": [
3094 { typename: "a", "from": "self", "to": "#c" },
3095 { typename: "b", "from": "self", "to": "#c", "availability": "optional" },
3096 ]})
3097 .to_string(),
3098 );
3099
3100 my.merge_from(other, &path::Path::new("some/path")).unwrap();
3101 assert_eq!(my, result);
3102 }
3103
3104 #[test_case("protocol")]
3105 #[test_case("service")]
3106 #[test_case("event_stream")]
3107 #[test_case("directory")]
3108 #[test_case("runner")]
3109 #[test_case("resolver")]
3110 fn test_merge_from_duplicate_exposes_v1(typename: &str) {
3111 let mut my = document(json!({ "expose": [{ typename: "a", "from": "self" }]}));
3112 let mut other = document(json!({ "expose": [
3113 { typename: ["a", "b"], "from": "self" }
3114 ]}));
3115 let result = document(json!({ "expose": [
3116 { typename: "a", "from": "self" },
3117 { typename: "b", "from": "self" },
3118 ]}));
3119
3120 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
3121 assert_eq!(my, result);
3122 }
3123
3124 #[test_case("protocol")]
3125 #[test_case("service")]
3126 #[test_case("event_stream")]
3127 #[test_case("directory")]
3128 #[test_case("runner")]
3129 #[test_case("resolver")]
3130 fn test_merge_from_duplicate_exposes(typename: &str) {
3131 let mut my =
3132 document_context(&json!({ "expose": [{ typename: "a", "from": "self" }]}).to_string());
3133 let other = document_context(
3134 &json!({ "expose": [
3135 { typename: ["a", "b"], "from": "self" }
3136 ]})
3137 .to_string(),
3138 );
3139 let result = document_context(
3140 &json!({ "expose": [
3141 { typename: "a", "from": "self" },
3142 { typename: "b", "from": "self" },
3143 ]})
3144 .to_string(),
3145 );
3146
3147 my.merge_from(other, &path::Path::new("some/path")).unwrap();
3148 assert_eq!(my, result);
3149 }
3150
3151 #[test_case(
3152 document(json!({ "use": [
3153 { "protocol": "a", "availability": "required" },
3154 { "protocol": "b", "availability": "optional" },
3155 { "protocol": "c", "availability": "transitional" },
3156 { "protocol": "d", "availability": "same_as_target" },
3157 ]})),
3158 document(json!({ "use": [
3159 { "protocol": ["a"], "availability": "required" },
3160 { "protocol": ["b"], "availability": "optional" },
3161 { "protocol": ["c"], "availability": "transitional" },
3162 { "protocol": ["d"], "availability": "same_as_target" },
3163 ]})),
3164 document(json!({ "use": [
3165 { "protocol": "a", "availability": "required" },
3166 { "protocol": "b", "availability": "optional" },
3167 { "protocol": "c", "availability": "transitional" },
3168 { "protocol": "d", "availability": "same_as_target" },
3169 ]}))
3170 ; "merge both same"
3171 )]
3172 #[test_case(
3173 document(json!({ "use": [
3174 { "protocol": "a", "availability": "optional" },
3175 { "protocol": "b", "availability": "transitional" },
3176 { "protocol": "c", "availability": "transitional" },
3177 ]})),
3178 document(json!({ "use": [
3179 { "protocol": ["a", "x"], "availability": "required" },
3180 { "protocol": ["b", "y"], "availability": "optional" },
3181 { "protocol": ["c", "z"], "availability": "required" },
3182 ]})),
3183 document(json!({ "use": [
3184 { "protocol": ["a", "x"], "availability": "required" },
3185 { "protocol": ["b", "y"], "availability": "optional" },
3186 { "protocol": ["c", "z"], "availability": "required" },
3187 ]}))
3188 ; "merge with upgrade"
3189 )]
3190 #[test_case(
3191 document(json!({ "use": [
3192 { "protocol": "a", "availability": "required" },
3193 { "protocol": "b", "availability": "optional" },
3194 { "protocol": "c", "availability": "required" },
3195 ]})),
3196 document(json!({ "use": [
3197 { "protocol": ["a", "x"], "availability": "optional" },
3198 { "protocol": ["b", "y"], "availability": "transitional" },
3199 { "protocol": ["c", "z"], "availability": "transitional" },
3200 ]})),
3201 document(json!({ "use": [
3202 { "protocol": "a", "availability": "required" },
3203 { "protocol": "b", "availability": "optional" },
3204 { "protocol": "c", "availability": "required" },
3205 { "protocol": "x", "availability": "optional" },
3206 { "protocol": "y", "availability": "transitional" },
3207 { "protocol": "z", "availability": "transitional" },
3208 ]}))
3209 ; "merge with downgrade"
3210 )]
3211 #[test_case(
3212 document(json!({ "use": [
3213 { "protocol": "a", "availability": "optional" },
3214 { "protocol": "b", "availability": "transitional" },
3215 { "protocol": "c", "availability": "transitional" },
3216 ]})),
3217 document(json!({ "use": [
3218 { "protocol": ["a", "x"], "availability": "same_as_target" },
3219 { "protocol": ["b", "y"], "availability": "same_as_target" },
3220 { "protocol": ["c", "z"], "availability": "same_as_target" },
3221 ]})),
3222 document(json!({ "use": [
3223 { "protocol": "a", "availability": "optional" },
3224 { "protocol": "b", "availability": "transitional" },
3225 { "protocol": "c", "availability": "transitional" },
3226 { "protocol": ["a", "x"], "availability": "same_as_target" },
3227 { "protocol": ["b", "y"], "availability": "same_as_target" },
3228 { "protocol": ["c", "z"], "availability": "same_as_target" },
3229 ]}))
3230 ; "merge with no replacement"
3231 )]
3232 #[test_case(
3233 document(json!({ "use": [
3234 { "protocol": ["a", "b", "c"], "availability": "optional" },
3235 { "protocol": "d", "availability": "same_as_target" },
3236 { "protocol": ["e", "f"] },
3237 ]})),
3238 document(json!({ "use": [
3239 { "protocol": ["c", "e", "g"] },
3240 { "protocol": ["d", "h"] },
3241 { "protocol": ["f", "i"], "availability": "transitional" },
3242 ]})),
3243 document(json!({ "use": [
3244 { "protocol": ["a", "b"], "availability": "optional" },
3245 { "protocol": "d", "availability": "same_as_target" },
3246 { "protocol": ["e", "f"] },
3247 { "protocol": ["c", "g"] },
3248 { "protocol": ["d", "h"] },
3249 { "protocol": "i", "availability": "transitional" },
3250 ]}))
3251 ; "merge multiple"
3252 )]
3253
3254 fn test_merge_from_duplicate_capability_availability_v1(
3255 mut my: Document,
3256 mut other: Document,
3257 result: Document,
3258 ) {
3259 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
3260 assert_eq!(my, result);
3261 }
3262
3263 #[test_case(
3264 document_context(&json!({ "use": [
3265 { "protocol": "a", "availability": "required" },
3266 { "protocol": "b", "availability": "optional" },
3267 { "protocol": "c", "availability": "transitional" },
3268 { "protocol": "d", "availability": "same_as_target" },
3269 ]}).to_string()),
3270 document_context(&json!({ "use": [
3271 { "protocol": ["a"], "availability": "required" },
3272 { "protocol": ["b"], "availability": "optional" },
3273 { "protocol": ["c"], "availability": "transitional" },
3274 { "protocol": ["d"], "availability": "same_as_target" },
3275 ]}).to_string()),
3276 document_context(&json!({ "use": [
3277 { "protocol": "a", "availability": "required" },
3278 { "protocol": "b", "availability": "optional" },
3279 { "protocol": "c", "availability": "transitional" },
3280 { "protocol": "d", "availability": "same_as_target" },
3281 ]}).to_string())
3282 ; "merge both same"
3283 )]
3284 #[test_case(
3285 document_context(&json!({ "use": [
3286 { "protocol": "a", "availability": "optional" },
3287 { "protocol": "b", "availability": "transitional" },
3288 { "protocol": "c", "availability": "transitional" },
3289 ]}).to_string()),
3290 document_context(&json!({ "use": [
3291 { "protocol": ["a", "x"], "availability": "required" },
3292 { "protocol": ["b", "y"], "availability": "optional" },
3293 { "protocol": ["c", "z"], "availability": "required" },
3294 ]}).to_string()),
3295 document_context(&json!({ "use": [
3296 { "protocol": ["a", "x"], "availability": "required" },
3297 { "protocol": ["b", "y"], "availability": "optional" },
3298 { "protocol": ["c", "z"], "availability": "required" },
3299 ]}).to_string())
3300 ; "merge with upgrade"
3301 )]
3302 #[test_case(
3303 document_context(&json!({ "use": [
3304 { "protocol": "a", "availability": "required" },
3305 { "protocol": "b", "availability": "optional" },
3306 { "protocol": "c", "availability": "required" },
3307 ]}).to_string()),
3308 document_context(&json!({ "use": [
3309 { "protocol": ["a", "x"], "availability": "optional" },
3310 { "protocol": ["b", "y"], "availability": "transitional" },
3311 { "protocol": ["c", "z"], "availability": "transitional" },
3312 ]}).to_string()),
3313 document_context(&json!({ "use": [
3314 { "protocol": "a", "availability": "required" },
3315 { "protocol": "b", "availability": "optional" },
3316 { "protocol": "c", "availability": "required" },
3317 { "protocol": "x", "availability": "optional" },
3318 { "protocol": "y", "availability": "transitional" },
3319 { "protocol": "z", "availability": "transitional" },
3320 ]}).to_string())
3321 ; "merge with downgrade"
3322 )]
3323 #[test_case(
3324 document_context(&json!({ "use": [
3325 { "protocol": "a", "availability": "optional" },
3326 { "protocol": "b", "availability": "transitional" },
3327 { "protocol": "c", "availability": "transitional" },
3328 ]}).to_string()),
3329 document_context(&json!({ "use": [
3330 { "protocol": ["a", "x"], "availability": "same_as_target" },
3331 { "protocol": ["b", "y"], "availability": "same_as_target" },
3332 { "protocol": ["c", "z"], "availability": "same_as_target" },
3333 ]}).to_string()),
3334 document_context(&json!({ "use": [
3335 { "protocol": "a", "availability": "optional" },
3336 { "protocol": "b", "availability": "transitional" },
3337 { "protocol": "c", "availability": "transitional" },
3338 { "protocol": ["a", "x"], "availability": "same_as_target" },
3339 { "protocol": ["b", "y"], "availability": "same_as_target" },
3340 { "protocol": ["c", "z"], "availability": "same_as_target" },
3341 ]}).to_string())
3342 ; "merge with no replacement"
3343 )]
3344 #[test_case(
3345 document_context(&json!({ "use": [
3346 { "protocol": ["a", "b", "c"], "availability": "optional" },
3347 { "protocol": "d", "availability": "same_as_target" },
3348 { "protocol": ["e", "f"] },
3349 ]}).to_string()),
3350 document_context(&json!({ "use": [
3351 { "protocol": ["c", "e", "g"] },
3352 { "protocol": ["d", "h"] },
3353 { "protocol": ["f", "i"], "availability": "transitional" },
3354 ]}).to_string()),
3355 document_context(&json!({ "use": [
3356 { "protocol": ["a", "b"], "availability": "optional" },
3357 { "protocol": "d", "availability": "same_as_target" },
3358 { "protocol": ["e", "f"] },
3359 { "protocol": ["c", "g"] },
3360 { "protocol": ["d", "h"] },
3361 { "protocol": "i", "availability": "transitional" },
3362 ]}).to_string())
3363 ; "merge multiple"
3364 )]
3365
3366 fn test_merge_from_duplicate_capability_availability(
3367 mut my: DocumentContext,
3368 other: DocumentContext,
3369 result: DocumentContext,
3370 ) {
3371 my.merge_from(other, &path::Path::new("some/path")).unwrap();
3372 assert_eq!(my, result);
3373 }
3374
3375 #[test_case(
3376 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
3377 document(json!({ "use": [{ "protocol": ["c", "d"] }]})),
3378 document(json!({ "use": [
3379 { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
3380 ]}))
3381 ; "merge capabilities with disjoint sets"
3382 )]
3383 #[test_case(
3384 document(json!({ "use": [
3385 { "protocol": ["a"] },
3386 { "protocol": "b" },
3387 ]})),
3388 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
3389 document(json!({ "use": [
3390 { "protocol": ["a"] }, { "protocol": "b" },
3391 ]}))
3392 ; "merge capabilities with equal set"
3393 )]
3394 #[test_case(
3395 document(json!({ "use": [
3396 { "protocol": ["a", "b"] },
3397 { "protocol": "c" },
3398 ]})),
3399 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
3400 document(json!({ "use": [
3401 { "protocol": ["a", "b"] }, { "protocol": "c" },
3402 ]}))
3403 ; "merge capabilities with subset"
3404 )]
3405 #[test_case(
3406 document(json!({ "use": [
3407 { "protocol": ["a", "b"] },
3408 ]})),
3409 document(json!({ "use": [{ "protocol": ["a", "b", "c"] }]})),
3410 document(json!({ "use": [
3411 { "protocol": ["a", "b"] },
3412 { "protocol": "c" },
3413 ]}))
3414 ; "merge capabilities with superset"
3415 )]
3416 #[test_case(
3417 document(json!({ "use": [
3418 { "protocol": ["a", "b"] },
3419 ]})),
3420 document(json!({ "use": [{ "protocol": ["b", "c", "d"] }]})),
3421 document(json!({ "use": [
3422 { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
3423 ]}))
3424 ; "merge capabilities with intersection"
3425 )]
3426 #[test_case(
3427 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
3428 document(json!({ "use": [
3429 { "protocol": ["c", "b", "d"] },
3430 { "protocol": ["e", "d"] },
3431 ]})),
3432 document(json!({ "use": [
3433 {"protocol": ["a", "b"] },
3434 {"protocol": ["c", "d"] },
3435 {"protocol": "e" }]}))
3436 ; "merge capabilities from multiple arrays"
3437 )]
3438 #[test_case(
3439 document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
3440 document(json!({ "use": [{ "service": "foo.bar.Baz", "from": "self"}]})),
3441 document(json!({ "use": [
3442 {"protocol": "foo.bar.Baz", "from": "self"},
3443 {"service": "foo.bar.Baz", "from": "self"}]}))
3444 ; "merge capabilities, types don't match"
3445 )]
3446 #[test_case(
3447 document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
3448 document(json!({ "use": [{ "protocol": "foo.bar.Baz" }]})),
3449 document(json!({ "use": [
3450 {"protocol": "foo.bar.Baz", "from": "self"},
3451 {"protocol": "foo.bar.Baz"}]}))
3452 ; "merge capabilities, fields don't match"
3453 )]
3454
3455 fn test_merge_from_duplicate_capability_v1(
3456 mut my: Document,
3457 mut other: Document,
3458 result: Document,
3459 ) {
3460 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
3461 assert_eq!(my, result);
3462 }
3463
3464 #[test_case(
3465 document_context(&json!({ "use": [{ "protocol": ["a", "b"] }]}).to_string()),
3466 document_context(&json!({ "use": [{ "protocol": ["c", "d"] }]}).to_string()),
3467 document_context(&json!({ "use": [
3468 { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
3469 ]}).to_string())
3470 ; "merge capabilities with disjoint sets"
3471 )]
3472 #[test_case(
3473 document_context(&json!({ "use": [
3474 { "protocol": ["a"] },
3475 { "protocol": "b" },
3476 ]}).to_string()),
3477 document_context(&json!({ "use": [{ "protocol": ["a", "b"] }]}).to_string()),
3478 document_context(&json!({ "use": [
3479 { "protocol": ["a"] }, { "protocol": "b" },
3480 ]}).to_string())
3481 ; "merge capabilities with equal set"
3482 )]
3483 #[test_case(
3484 document_context(&json!({ "use": [
3485 { "protocol": ["a", "b"] },
3486 { "protocol": "c" },
3487 ]}).to_string()),
3488 document_context(&json!({ "use": [{ "protocol": ["a", "b"] }]}).to_string()),
3489 document_context(&json!({ "use": [
3490 { "protocol": ["a", "b"] }, { "protocol": "c" },
3491 ]}).to_string())
3492 ; "merge capabilities with subset"
3493 )]
3494 #[test_case(
3495 document_context(&json!({ "use": [
3496 { "protocol": ["a", "b"] },
3497 ]}).to_string()),
3498 document_context(&json!({ "use": [{ "protocol": ["a", "b", "c"] }]}).to_string()),
3499 document_context(&json!({ "use": [
3500 { "protocol": ["a", "b"] },
3501 { "protocol": "c" },
3502 ]}).to_string())
3503 ; "merge capabilities with superset"
3504 )]
3505 #[test_case(
3506 document_context(&json!({ "use": [
3507 { "protocol": ["a", "b"] },
3508 ]}).to_string()),
3509 document_context(&json!({ "use": [{ "protocol": ["b", "c", "d"] }]}).to_string()),
3510 document_context(&json!({ "use": [
3511 { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
3512 ]}).to_string())
3513 ; "merge capabilities with intersection"
3514 )]
3515 #[test_case(
3516 document_context(&json!({ "use": [{ "protocol": ["a", "b"] }]}).to_string()),
3517 document_context(&json!({ "use": [
3518 { "protocol": ["c", "b", "d"] },
3519 { "protocol": ["e", "d"] },
3520 ]}).to_string()),
3521 document_context(&json!({ "use": [
3522 {"protocol": ["a", "b"] },
3523 {"protocol": ["c", "d"] },
3524 {"protocol": "e" }]}).to_string())
3525 ; "merge capabilities from multiple arrays"
3526 )]
3527 #[test_case(
3528 document_context(&json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]}).to_string()),
3529 document_context(&json!({ "use": [{ "service": "foo.bar.Baz", "from": "self"}]}).to_string()),
3530 document_context(&json!({ "use": [
3531 {"protocol": "foo.bar.Baz", "from": "self"},
3532 {"service": "foo.bar.Baz", "from": "self"}]}).to_string())
3533 ; "merge capabilities, types don't match"
3534 )]
3535 #[test_case(
3536 document_context(&json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]}).to_string()),
3537 document_context(&json!({ "use": [{ "protocol": "foo.bar.Baz" }]}).to_string()),
3538 document_context(&json!({ "use": [
3539 {"protocol": "foo.bar.Baz", "from": "self"},
3540 {"protocol": "foo.bar.Baz"}]}).to_string())
3541 ; "merge capabilities, fields don't match"
3542 )]
3543
3544 fn test_merge_from_duplicate_capability(
3545 mut my: DocumentContext,
3546 other: DocumentContext,
3547 result: DocumentContext,
3548 ) {
3549 my.merge_from(other, &path::Path::new("some/path")).unwrap();
3550 assert_eq!(my, result);
3551 }
3552
3553 #[test_case(&Right::Connect; "connect right")]
3554 #[test_case(&Right::Enumerate; "enumerate right")]
3555 #[test_case(&Right::Execute; "execute right")]
3556 #[test_case(&Right::GetAttributes; "getattr right")]
3557 #[test_case(&Right::ModifyDirectory; "modifydir right")]
3558 #[test_case(&Right::ReadBytes; "readbytes right")]
3559 #[test_case(&Right::Traverse; "traverse right")]
3560 #[test_case(&Right::UpdateAttributes; "updateattrs right")]
3561 #[test_case(&Right::WriteBytes; "writebytes right")]
3562 #[test_case(&Right::ReadAlias; "r right")]
3563 #[test_case(&Right::WriteAlias; "w right")]
3564 #[test_case(&Right::ExecuteAlias; "x right")]
3565 #[test_case(&Right::ReadWriteAlias; "rw right")]
3566 #[test_case(&Right::ReadExecuteAlias; "rx right")]
3567 #[test_case(&OfferFromRef::Self_; "offer from self")]
3568 #[test_case(&OfferFromRef::Parent; "offer from parent")]
3569 #[test_case(&OfferFromRef::Named(Name::new("child".to_string()).unwrap()); "offer from named")]
3570 #[test_case(
3571 &document(json!({}));
3572 "empty document"
3573 )]
3574 #[test_case(
3575 &document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
3576 "use one from self"
3577 )]
3578 #[test_case(
3579 &document(json!({ "use": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
3580 "use multiple from self"
3581 )]
3582 #[test_case(
3583 &document(json!({
3584 "offer": [{ "protocol": "foo.bar.Baz", "from": "self", "to": "#elements"}],
3585 "collections" :[{"name": "elements", "durability": "transient" }]
3586 }));
3587 "offer from self to collection"
3588 )]
3589 #[test_case(
3590 &document(json!({
3591 "offer": [
3592 { "service": "foo.bar.Baz", "from": "self", "to": "#elements" },
3593 { "service": "some.other.Service", "from": "self", "to": "#elements"},
3594 ],
3595 "collections":[ {"name": "elements", "durability": "transient"} ]}));
3596 "service offers"
3597 )]
3598 #[test_case(
3599 &document(json!({ "expose": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
3600 "expose protocols from self"
3601 )]
3602 #[test_case(
3603 &document(json!({ "expose": [{ "service": ["foo.bar.Baz", "some.other.Service"], "from": "self"}]}));
3604 "expose service from self"
3605 )]
3606 #[test_case(
3607 &document(json!({ "capabilities": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
3608 "capabilities from self"
3609 )]
3610 #[test_case(
3611 &document(json!({ "facets": { "my.key": "my.value" } }));
3612 "facets"
3613 )]
3614 #[test_case(
3615 &document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
3616 "elf runner program"
3617 )]
3618 fn serialize_roundtrips<T>(val: &T)
3619 where
3620 T: serde::de::DeserializeOwned + Serialize + PartialEq + std::fmt::Debug,
3621 {
3622 let raw = serde_json::to_string(val).expect("serializing `val` should work");
3623 let parsed: T =
3624 serde_json::from_str(&raw).expect("must be able to parse back serialized value");
3625 assert_eq!(val, &parsed, "parsed value must equal original value");
3626 }
3627}