1use indexmap::IndexMap;
6use itertools::Itertools;
7
8use crate::types::capability::{ContextCapability, ParsedCapability};
9use crate::types::child::{ContextChild, ParsedChild};
10use crate::types::collection::{ContextCollection, ParsedCollection};
11use crate::types::common::*;
12use crate::types::environment::{ContextEnvironment, ParsedEnvironment};
13use crate::types::expose::{ContextExpose, ParsedExpose};
14use crate::types::offer::{ContextOffer, ParsedOffer};
15use crate::types::program::{ContextProgram, ParsedProgram};
16use crate::types::r#use::{ContextUse, ParsedUse};
17use crate::{
18 Canonicalize, Capability, CapabilityClause, CapabilityFromRef, Child, Collection, ConfigKey,
19 ConfigValueType, Environment, Error, Expose, Location, Offer, Program, Use, merge_spanned_vec,
20};
21
22pub use cm_types::{
23 Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
24 OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
25};
26use json_spanned_value::Spanned;
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_other_field<T: std::cmp::PartialEq>(
910 us: &mut Option<Vec<T>>,
911 other: &mut Option<Vec<T>>,
912) {
913 if let Some(ours) = us {
914 if let Some(theirs) = other.take() {
915 for t in theirs {
917 if !ours.contains(&t) {
918 ours.push(t);
919 }
920 }
921 }
922 } else if let Some(theirs) = other.take() {
923 us.replace(theirs);
924 }
925}
926
927fn compute_diff<T: CapabilityClause>(ours: &mut T, theirs: &mut T) {
934 if ours.names().is_empty() || theirs.names().is_empty() {
936 return;
937 }
938
939 if ours.capability_type().unwrap() != theirs.capability_type().unwrap() {
941 return;
942 }
943
944 let mut ours_partial = ours.clone();
946 let mut theirs_partial = theirs.clone();
947 for e in [&mut ours_partial, &mut theirs_partial] {
948 e.set_names(Vec::new());
949 e.set_availability(None);
951 }
952 if ours_partial != theirs_partial {
953 return;
955 }
956
957 let Some(avail_cmp) = ours
959 .availability()
960 .unwrap_or_default()
961 .partial_cmp(&theirs.availability().unwrap_or_default())
962 else {
963 return;
965 };
966
967 let mut our_names: Vec<Name> = ours.names().into_iter().map(Into::into).collect();
968 let mut their_names: Vec<Name> = theirs.names().into_iter().map(Into::into).collect();
969
970 let mut our_entries_to_remove = HashSet::new();
971 let mut their_entries_to_remove = HashSet::new();
972 for e in &their_names {
973 if !our_names.contains(e) {
974 continue;
976 }
977 match avail_cmp {
978 cmp::Ordering::Less => {
979 our_entries_to_remove.insert(e.clone());
982 }
983 cmp::Ordering::Greater => {
984 their_entries_to_remove.insert(e.clone());
987 }
988 cmp::Ordering::Equal => {
989 their_entries_to_remove.insert(e.clone());
991 }
992 }
993 }
994 our_names.retain(|e| !our_entries_to_remove.contains(e));
995 their_names.retain(|e| !their_entries_to_remove.contains(e));
996
997 ours.set_names(our_names);
998 theirs.set_names(their_names);
999}
1000
1001trait ValueMap {
1003 fn get_mut(&mut self, key: &str) -> Option<&mut Value>;
1004 fn insert(&mut self, key: String, val: Value);
1005}
1006
1007impl ValueMap for Map<String, Value> {
1008 fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
1009 self.get_mut(key)
1010 }
1011
1012 fn insert(&mut self, key: String, val: Value) {
1013 self.insert(key, val);
1014 }
1015}
1016
1017impl ValueMap for IndexMap<String, Value> {
1018 fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
1019 self.get_mut(key)
1020 }
1021
1022 fn insert(&mut self, key: String, val: Value) {
1023 self.insert(key, val);
1024 }
1025}
1026
1027#[derive(Deserialize, Debug, Default, PartialEq)]
1031#[serde(deny_unknown_fields)]
1032pub struct ParsedDocument {
1033 pub program: Option<Spanned<ParsedProgram>>,
1034 pub children: Option<Spanned<Vec<Spanned<ParsedChild>>>>,
1035 pub collections: Option<Spanned<Vec<Spanned<ParsedCollection>>>>,
1036 pub environments: Option<Spanned<Vec<Spanned<ParsedEnvironment>>>>,
1037 pub capabilities: Option<Spanned<Vec<Spanned<ParsedCapability>>>>,
1038 pub r#use: Option<Spanned<Vec<Spanned<ParsedUse>>>>,
1039 pub expose: Option<Spanned<Vec<Spanned<ParsedExpose>>>>,
1040 pub offer: Option<Spanned<Vec<Spanned<ParsedOffer>>>>,
1041 pub facets: Option<IndexMap<String, Spanned<Value>>>,
1042 pub config: Option<BTreeMap<ConfigKey, Spanned<ConfigValueType>>>,
1043}
1044
1045#[derive(Debug, Default)]
1046pub struct DocumentContext {
1047 pub program: Option<ContextSpanned<ContextProgram>>,
1048 pub children: Option<Vec<ContextSpanned<ContextChild>>>,
1049 pub collections: Option<Vec<ContextSpanned<ContextCollection>>>,
1050 pub environments: Option<Vec<ContextSpanned<ContextEnvironment>>>,
1051 pub capabilities: Option<Vec<ContextSpanned<ContextCapability>>>,
1052 pub r#use: Option<Vec<ContextSpanned<ContextUse>>>,
1053 pub expose: Option<Vec<ContextSpanned<ContextExpose>>>,
1054 pub offer: Option<Vec<ContextSpanned<ContextOffer>>>,
1055 pub facets: Option<IndexMap<String, ContextSpanned<Value>>>,
1056 pub config: Option<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>,
1057}
1058
1059impl DocumentContext {
1060 pub fn merge_from(
1061 &mut self,
1062 mut other: DocumentContext,
1063 include_path: &path::Path,
1064 ) -> Result<(), Error> {
1065 self.merge_program(&mut other, include_path)?;
1066 merge_spanned_vec!(self, other, children);
1067 merge_spanned_vec!(self, other, collections);
1068 self.merge_environment(&mut other)?;
1069 merge_spanned_vec!(self, other, capabilities);
1070 merge_spanned_vec!(self, other, r#use);
1071 merge_spanned_vec!(self, other, expose);
1072 merge_spanned_vec!(self, other, offer);
1073 self.merge_facets(&mut other, include_path)?;
1074 self.merge_config(&mut other)?;
1075 Ok(())
1076 }
1077
1078 pub fn all_storage_with_sources<'a>(
1079 &'a self,
1080 ) -> HashMap<&'a BorrowedName, &'a CapabilityFromRef> {
1081 if let Some(capabilities) = self.capabilities.as_ref() {
1082 capabilities
1083 .iter()
1084 .filter_map(|cap_wrapper| {
1085 let c = &cap_wrapper.value;
1086
1087 let storage_span_opt = c.storage.as_ref();
1088 let source_span_opt = c.from.as_ref();
1089
1090 match (storage_span_opt, source_span_opt) {
1091 (Some(s_span), Some(f_span)) => {
1092 let name_ref: &BorrowedName = s_span.value.as_ref();
1093 let source_ref: &CapabilityFromRef = &f_span.value;
1094
1095 Some((name_ref, source_ref))
1096 }
1097 _ => None,
1098 }
1099 })
1100 .collect()
1101 } else {
1102 HashMap::new()
1103 }
1104 }
1105
1106 pub fn all_capability_names(&self) -> HashSet<&BorrowedName> {
1107 self.capabilities
1108 .as_ref()
1109 .map(|c| {
1110 c.iter().fold(HashSet::new(), |mut acc, capability_wrapper| {
1111 let capability = &capability_wrapper.value;
1112 acc.extend(capability.names());
1113 acc
1114 })
1115 })
1116 .unwrap_or_default()
1117 }
1118
1119 pub fn all_collection_names(&self) -> HashSet<&BorrowedName> {
1120 if let Some(collections) = self.collections.as_ref() {
1121 collections.iter().map(|c| c.value.name.value.as_ref()).collect()
1122 } else {
1123 HashSet::new()
1124 }
1125 }
1126
1127 pub fn all_config_names(&self) -> HashSet<&BorrowedName> {
1128 self.capabilities
1129 .as_ref()
1130 .map(|caps| {
1131 caps.iter()
1132 .filter_map(|cap_wrapper| {
1133 let cap = &cap_wrapper.value;
1134
1135 cap.config.as_ref().map(|spanned_key| spanned_key.value.as_ref())
1136 })
1137 .collect()
1138 })
1139 .unwrap_or_default()
1140 }
1141
1142 pub fn all_children_names(&self) -> HashSet<&BorrowedName> {
1143 if let Some(children) = self.children.as_ref() {
1144 children.iter().map(|c| c.value.name.value.as_ref()).collect()
1145 } else {
1146 HashSet::new()
1147 }
1148 }
1149
1150 pub fn all_dictionaries<'a>(&'a self) -> HashMap<&'a BorrowedName, &'a ContextCapability> {
1151 if let Some(capabilities) = self.capabilities.as_ref() {
1152 capabilities
1153 .iter()
1154 .filter_map(|cap_wrapper| {
1155 let cap = &cap_wrapper.value;
1156 let dict_span_opt = cap.dictionary.as_ref();
1157
1158 dict_span_opt.and_then(|dict_span| {
1159 let name_value = &dict_span.value;
1160 let borrowed_name: &BorrowedName = name_value.as_ref();
1161 Some((borrowed_name, cap))
1162 })
1163 })
1164 .collect()
1165 } else {
1166 HashMap::new()
1167 }
1168 }
1169
1170 fn merge_program(
1171 &mut self,
1172 other: &mut DocumentContext,
1173 include_path: &path::Path,
1174 ) -> Result<(), Error> {
1175 if let None = other.program {
1176 return Ok(());
1177 }
1178 if let None = self.program {
1179 let synthetic_origin = Origin {
1180 file: Arc::new(include_path.to_path_buf()),
1181 location: Location { line: 0, column: 0 },
1182 };
1183
1184 self.program =
1185 Some(ContextSpanned { value: ContextProgram::default(), origin: synthetic_origin });
1186 }
1187 let my_program = &mut self.program.as_mut().unwrap().value;
1188 let other_program = &mut other.program.as_mut().unwrap().value;
1189 if let Some(other_runner_wrapper) = other_program.runner.take() {
1190 if let Some(my_runner_wrapper) = my_program.runner.as_ref() {
1191 if my_runner_wrapper.value != other_runner_wrapper.value {
1192 return Err(Error::merge(
1193 format!(
1194 "Manifest include had a conflicting `program.runner`: parent='{}', include='{}'",
1195 my_runner_wrapper.value, other_runner_wrapper.value
1196 ),
1197 Some(other_runner_wrapper.origin),
1198 ));
1199 }
1200 } else {
1201 my_program.runner = Some(other_runner_wrapper);
1202 }
1203 }
1204 Self::merge_maps_unified(
1205 &mut my_program.info,
1206 &other_program.info,
1207 "program",
1208 include_path,
1209 None,
1210 Some(&vec!["environ", "features"]),
1211 )
1212 }
1213
1214 fn merge_environment(&mut self, other: &mut DocumentContext) -> Result<(), Error> {
1215 if other.environments.is_none() {
1216 return Ok(());
1217 }
1218 if self.environments.is_none() {
1219 self.environments = Some(vec![]);
1220 }
1221
1222 let merged_results = {
1223 let my_environments = self.environments.as_mut().unwrap();
1224 let other_environments = other.environments.as_mut().unwrap();
1225
1226 my_environments.sort_by(|x, y| x.value.name.value.cmp(&y.value.name.value));
1227 other_environments.sort_by(|x, y| x.value.name.value.cmp(&y.value.name.value));
1228
1229 let all_environments =
1230 my_environments.drain(..).merge_by(other_environments.drain(..), |x, y| {
1231 x.value.name.value <= y.value.name.value
1232 });
1233
1234 let groups = all_environments.chunk_by(|e| e.value.name.value.clone());
1235
1236 let mut results = vec![];
1237 for (_name_value, group) in &groups {
1238 let mut group_iter = group.into_iter();
1239 let first_wrapper = group_iter.next().expect("chunk cannot be empty");
1240 let first_origin = first_wrapper.origin.clone();
1241 let mut merged_inner = first_wrapper.value;
1242
1243 for subsequent in group_iter {
1244 merged_inner.merge_from(subsequent.value)?;
1245 }
1246
1247 results.push(ContextSpanned { value: merged_inner, origin: first_origin });
1248 }
1249 results
1250 };
1251
1252 self.environments = Some(merged_results);
1253 Ok(())
1254 }
1255
1256 fn merge_facets(
1257 &mut self,
1258 other: &mut DocumentContext,
1259 include_path: &path::Path,
1260 ) -> Result<(), Error> {
1261 if let None = other.facets {
1262 return Ok(());
1263 }
1264 if let None = self.facets {
1265 self.facets = Some(Default::default());
1266 }
1267 let other_facets = other.facets.as_ref().unwrap();
1268
1269 for (key, include_spanned) in other_facets {
1270 let entry_origin = Some(&include_spanned.origin);
1271 let my_facets = self.facets.as_mut().unwrap();
1272
1273 if !my_facets.contains_key(key) {
1274 my_facets.insert(key.clone(), include_spanned.clone());
1275 } else {
1276 let self_spanned = my_facets.get_mut(key).unwrap();
1277 Self::merge_maps_unified(
1278 self_spanned.value.as_object_mut().unwrap(),
1279 include_spanned.value.as_object().unwrap(),
1280 &format!("facets.{}", key),
1281 include_path,
1282 entry_origin,
1283 Some(&vec![]),
1284 )?;
1285 }
1286 }
1287 Ok(())
1288 }
1289
1290 fn merge_config(&mut self, other: &mut DocumentContext) -> Result<(), Error> {
1291 if other.config.is_none() {
1292 return Ok(());
1293 }
1294 if self.config.is_none() {
1295 self.config = Some(BTreeMap::new());
1296 }
1297
1298 let my_config = self.config.as_mut().unwrap();
1299 let other_config = other.config.as_ref().unwrap();
1300
1301 for (key, other_spanned) in other_config {
1302 if let Some(my_spanned) = my_config.get(key) {
1303 if my_spanned.value != other_spanned.value {
1304 return Err(Error::merge(
1305 format!("Conflicting configuration key found: '{}'", key),
1306 Some(other_spanned.origin.clone()),
1307 ));
1308 }
1309 } else {
1310 my_config.insert(key.clone(), other_spanned.clone());
1311 }
1312 }
1313 Ok(())
1314 }
1315
1316 fn merge_maps_unified<'s, Source, Dest>(
1317 self_map: &mut Dest,
1318 include_map: Source,
1319 outer_key: &str,
1320 include_path: &path::Path,
1321 origin: Option<&Origin>,
1322 allow_array_concatenation_keys: Option<&Vec<&str>>,
1323 ) -> Result<(), Error>
1324 where
1325 Source: IntoIterator<Item = (&'s String, &'s serde_json::Value)>,
1326 Dest: ValueMap,
1327 {
1328 for (key, include_val) in include_map {
1329 match self_map.get_mut(key) {
1330 None => {
1331 self_map.insert(key.clone(), include_val.clone());
1332 }
1333 Some(self_val) => match (self_val, include_val) {
1334 (serde_json::Value::Object(s_inner), serde_json::Value::Object(i_inner)) => {
1335 let combined_key = format!("{}.{}", outer_key, key);
1336 Self::merge_maps_unified(
1337 s_inner,
1338 i_inner,
1339 &combined_key,
1340 include_path,
1341 origin,
1342 allow_array_concatenation_keys,
1343 )?;
1344 }
1345 (serde_json::Value::Array(s_arr), serde_json::Value::Array(i_arr)) => {
1346 let is_allowed = allow_array_concatenation_keys
1347 .map_or(true, |keys| keys.contains(&key.as_str()));
1348
1349 if is_allowed {
1350 s_arr.extend(i_arr.clone());
1351 } else if s_arr != i_arr {
1352 return Err(Error::merge(
1353 format!(
1354 "Conflicting array values for field \"{}.{}\"",
1355 outer_key, key
1356 ),
1357 origin.cloned(),
1358 ));
1359 }
1360 }
1361 (v1, v2) if v1 == v2 => {}
1362 _ => {
1363 return Err(Error::merge(
1364 format!(
1365 "Manifest include '{}' had a conflicting value for field \"{}.{}\"",
1366 include_path.display(),
1367 outer_key,
1368 key
1369 ),
1370 origin.cloned(),
1371 ));
1372 }
1373 },
1374 }
1375 }
1376 Ok(())
1377 }
1378}
1379
1380pub fn convert_parsed_to_document(
1381 parsed_doc: ParsedDocument,
1382 file_arc: Arc<PathBuf>,
1383 buffer: &String,
1384) -> Result<DocumentContext, Error> {
1385 let facets = parsed_doc.facets.map(|raw_facets| {
1386 raw_facets
1387 .into_iter()
1388 .map(|(key, spanned_val)| {
1389 let context_val = hydrate_simple(spanned_val, &file_arc, buffer);
1390 (key, context_val)
1391 })
1392 .collect::<IndexMap<String, ContextSpanned<serde_json::Value>>>()
1393 });
1394
1395 let config = parsed_doc.config.map(|raw_config| {
1396 raw_config
1397 .into_iter()
1398 .map(|(key, spanned_val)| {
1399 let context_val = hydrate_simple(spanned_val, &file_arc, buffer);
1400 (key, context_val)
1401 })
1402 .collect::<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>()
1403 });
1404
1405 Ok(DocumentContext {
1406 program: hydrate_opt(parsed_doc.program, &file_arc, buffer)?,
1407 children: hydrate_list(parsed_doc.children, &file_arc, buffer)?,
1408 collections: hydrate_list(parsed_doc.collections, &file_arc, buffer)?,
1409 environments: hydrate_list(parsed_doc.environments, &file_arc, buffer)?,
1410 capabilities: hydrate_list(parsed_doc.capabilities, &file_arc, buffer)?,
1411 r#use: hydrate_list(parsed_doc.r#use, &file_arc, buffer)?,
1412 expose: hydrate_list(parsed_doc.expose, &file_arc, buffer)?,
1413 offer: hydrate_list(parsed_doc.offer, &file_arc, buffer)?,
1414 facets,
1415 config,
1416 })
1417}
1418
1419#[cfg(test)]
1420mod tests {
1421 use super::*;
1422 use crate::OneOrMany;
1423 use crate::types::document::Document;
1424 use crate::types::offer::OfferFromRef;
1425 use crate::types::right::Right;
1426 use difference::Changeset;
1427 use serde_json::{json, to_string_pretty, to_value};
1428 use std::path;
1429 use std::path::Path;
1430 use test_case::test_case;
1431
1432 fn document(contents: serde_json::Value) -> Document {
1433 serde_json5::from_str::<Document>(&contents.to_string()).unwrap()
1434 }
1435
1436 macro_rules! assert_json_eq {
1437 ($a:expr, $e:expr) => {{
1438 if $a != $e {
1439 let expected = to_string_pretty(&$e).unwrap();
1440 let actual = to_string_pretty(&$a).unwrap();
1441 assert_eq!(
1442 $a,
1443 $e,
1444 "JSON actual != expected. Diffs:\n\n{}",
1445 Changeset::new(&actual, &expected, "\n")
1446 );
1447 }
1448 }};
1449 }
1450
1451 #[test]
1452 fn test_includes() {
1453 assert_eq!(document(json!({})).includes(), Vec::<String>::new());
1454 assert_eq!(document(json!({ "include": []})).includes(), Vec::<String>::new());
1455 assert_eq!(
1456 document(json!({ "include": [ "foo.cml", "bar.cml" ]})).includes(),
1457 vec!["foo.cml", "bar.cml"]
1458 );
1459 }
1460
1461 #[test]
1462 fn test_merge_same_section() {
1463 let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1464 let mut other = document(json!({ "use": [{ "protocol": "bar" }] }));
1465 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1466 let uses = some.r#use.as_ref().unwrap();
1467 assert_eq!(uses.len(), 2);
1468 assert_eq!(
1469 uses[0].protocol.as_ref().unwrap(),
1470 &OneOrMany::One("foo".parse::<Name>().unwrap())
1471 );
1472 assert_eq!(
1473 uses[1].protocol.as_ref().unwrap(),
1474 &OneOrMany::One("bar".parse::<Name>().unwrap())
1475 );
1476 }
1477
1478 #[test]
1479 fn test_merge_upgraded_availability() {
1480 let mut some =
1481 document(json!({ "use": [{ "protocol": "foo", "availability": "optional" }] }));
1482 let mut other1 = document(json!({ "use": [{ "protocol": "foo" }] }));
1483 let mut other2 =
1484 document(json!({ "use": [{ "protocol": "foo", "availability": "transitional" }] }));
1485 let mut other3 =
1486 document(json!({ "use": [{ "protocol": "foo", "availability": "same_as_target" }] }));
1487 some.merge_from(&mut other1, &Path::new("some/path")).unwrap();
1488 some.merge_from(&mut other2, &Path::new("some/path")).unwrap();
1489 some.merge_from(&mut other3, &Path::new("some/path")).unwrap();
1490 let uses = some.r#use.as_ref().unwrap();
1491 assert_eq!(uses.len(), 2);
1492 assert_eq!(
1493 uses[0].protocol.as_ref().unwrap(),
1494 &OneOrMany::One("foo".parse::<Name>().unwrap())
1495 );
1496 assert!(uses[0].availability.is_none());
1497 assert_eq!(
1498 uses[1].protocol.as_ref().unwrap(),
1499 &OneOrMany::One("foo".parse::<Name>().unwrap())
1500 );
1501 assert_eq!(uses[1].availability.as_ref().unwrap(), &Availability::SameAsTarget,);
1502 }
1503
1504 #[test]
1505 fn test_merge_different_sections() {
1506 let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1507 let mut other = document(json!({ "expose": [{ "protocol": "bar", "from": "self" }] }));
1508 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1509 let uses = some.r#use.as_ref().unwrap();
1510 let exposes = some.expose.as_ref().unwrap();
1511 assert_eq!(uses.len(), 1);
1512 assert_eq!(exposes.len(), 1);
1513 assert_eq!(
1514 uses[0].protocol.as_ref().unwrap(),
1515 &OneOrMany::One("foo".parse::<Name>().unwrap())
1516 );
1517 assert_eq!(
1518 exposes[0].protocol.as_ref().unwrap(),
1519 &OneOrMany::One("bar".parse::<Name>().unwrap())
1520 );
1521 }
1522
1523 #[test]
1524 fn test_merge_environments() {
1525 let mut some = document(json!({ "environments": [
1526 {
1527 "name": "one",
1528 "extends": "realm",
1529 },
1530 {
1531 "name": "two",
1532 "extends": "none",
1533 "runners": [
1534 {
1535 "runner": "r1",
1536 "from": "#c1",
1537 },
1538 {
1539 "runner": "r2",
1540 "from": "#c2",
1541 },
1542 ],
1543 "resolvers": [
1544 {
1545 "resolver": "res1",
1546 "from": "#c1",
1547 "scheme": "foo",
1548 },
1549 ],
1550 "debug": [
1551 {
1552 "protocol": "baz",
1553 "from": "#c2"
1554 }
1555 ]
1556 },
1557 ]}));
1558 let mut other = document(json!({ "environments": [
1559 {
1560 "name": "two",
1561 "__stop_timeout_ms": 100,
1562 "runners": [
1563 {
1564 "runner": "r3",
1565 "from": "#c3",
1566 },
1567 ],
1568 "resolvers": [
1569 {
1570 "resolver": "res2",
1571 "from": "#c1",
1572 "scheme": "bar",
1573 },
1574 ],
1575 "debug": [
1576 {
1577 "protocol": "faz",
1578 "from": "#c2"
1579 }
1580 ]
1581 },
1582 {
1583 "name": "three",
1584 "__stop_timeout_ms": 1000,
1585 },
1586 ]}));
1587 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1588 assert_eq!(
1589 to_value(some).unwrap(),
1590 json!({"environments": [
1591 {
1592 "name": "one",
1593 "extends": "realm",
1594 },
1595 {
1596 "name": "three",
1597 "__stop_timeout_ms": 1000,
1598 },
1599 {
1600 "name": "two",
1601 "extends": "none",
1602 "__stop_timeout_ms": 100,
1603 "runners": [
1604 {
1605 "runner": "r1",
1606 "from": "#c1",
1607 },
1608 {
1609 "runner": "r2",
1610 "from": "#c2",
1611 },
1612 {
1613 "runner": "r3",
1614 "from": "#c3",
1615 },
1616 ],
1617 "resolvers": [
1618 {
1619 "resolver": "res1",
1620 "from": "#c1",
1621 "scheme": "foo",
1622 },
1623 {
1624 "resolver": "res2",
1625 "from": "#c1",
1626 "scheme": "bar",
1627 },
1628 ],
1629 "debug": [
1630 {
1631 "protocol": "baz",
1632 "from": "#c2"
1633 },
1634 {
1635 "protocol": "faz",
1636 "from": "#c2"
1637 }
1638 ]
1639 },
1640 ]})
1641 );
1642 }
1643
1644 #[test]
1645 fn test_merge_environments_errors() {
1646 {
1647 let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
1648 let mut other = document(json!({"environments": [{"name": "one", "extends": "none"}]}));
1649 assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
1650 }
1651 {
1652 let mut some =
1653 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
1654 let mut other =
1655 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 20}]}));
1656 assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
1657 }
1658
1659 {
1661 let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
1662 let mut other =
1663 document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
1664 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1665 assert_eq!(
1666 to_value(some).unwrap(),
1667 json!({"environments": [{"name": "one", "extends": "realm"}]})
1668 );
1669 }
1670 {
1671 let mut some =
1672 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
1673 let mut other =
1674 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
1675 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1676 assert_eq!(
1677 to_value(some).unwrap(),
1678 json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]})
1679 );
1680 }
1681 }
1682
1683 #[test]
1684 fn test_merge_from_other_config() {
1685 let mut some = document(json!({}));
1686 let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
1687
1688 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1689 let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
1690 assert_eq!(some.config, expected.config);
1691 }
1692
1693 #[test]
1694 fn test_merge_from_some_config() {
1695 let mut some = document(json!({ "config": { "bar": { "type": "bool" } } }));
1696 let mut other = document(json!({}));
1697
1698 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1699 let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
1700 assert_eq!(some.config, expected.config);
1701 }
1702
1703 #[test]
1704 fn test_merge_from_config() {
1705 let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
1706 let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
1707 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1708
1709 assert_eq!(
1710 some,
1711 document(json!({
1712 "config": {
1713 "foo": { "type": "bool" },
1714 "bar": { "type": "bool" },
1715 }
1716 })),
1717 );
1718 }
1719
1720 #[test]
1721 fn test_merge_from_config_dedupe_identical_fields() {
1722 let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
1723 let mut other = document(json!({ "config": { "foo": { "type": "bool" } } }));
1724 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1725
1726 assert_eq!(some, document(json!({ "config": { "foo": { "type": "bool" } } })));
1727 }
1728
1729 #[test]
1730 fn test_merge_from_config_conflicting_keys() {
1731 let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
1732 let mut other = document(json!({ "config": { "foo": { "type": "uint8" } } }));
1733
1734 assert_matches::assert_matches!(
1735 some.merge_from(&mut other, &path::Path::new("some/path")),
1736 Err(Error::Validate { err, .. })
1737 if err == "Found conflicting entry for config key `foo` in `some/path`."
1738 );
1739 }
1740
1741 #[test]
1742 fn test_canonicalize() {
1743 let mut some = document(json!({
1744 "children": [
1745 { "name": "b_child", "url": "http://foo/b" },
1747 { "name": "a_child", "url": "http://foo/a" },
1748 ],
1749 "environments": [
1750 { "name": "b_env" },
1752 { "name": "a_env" },
1753 ],
1754 "collections": [
1755 { "name": "b_coll", "durability": "transient" },
1757 { "name": "a_coll", "durability": "transient" },
1758 ],
1759 "capabilities": [
1762 { "protocol": ["foo"] },
1764 { "protocol": "bar" },
1765 { "protocol": "arg", "path": "/arg" },
1767 { "service": ["b", "a"] },
1769 { "event_stream": ["b", "a"] },
1771 { "runner": "myrunner" },
1772 { "runner": "mypathrunner1", "path": "/foo" },
1774 { "runner": "mypathrunner2", "path": "/foo" },
1775 ],
1776 "offer": [
1778 { "protocol": "baz", "from": "#a_child", "to": "#c_child" },
1780 { "protocol": ["foo"], "from": "#a_child", "to": "#b_child" },
1782 { "protocol": "bar", "from": "#a_child", "to": "#b_child" },
1783 { "service": ["b", "a"], "from": "#a_child", "to": "#b_child" },
1785 {
1787 "event_stream": ["b", "a"],
1788 "from": "#a_child",
1789 "to": "#b_child",
1790 "scope": ["#b", "#c", "#a"] },
1792 { "runner": [ "myrunner", "a" ], "from": "#a_child", "to": "#b_child" },
1793 { "runner": [ "b" ], "from": "#a_child", "to": "#b_child" },
1794 { "directory": [ "b" ], "from": "#a_child", "to": "#b_child" },
1795 ],
1796 "expose": [
1797 { "protocol": ["foo"], "from": "#a_child" },
1798 { "protocol": "bar", "from": "#a_child" }, { "service": ["b", "a"], "from": "#a_child" },
1801 {
1803 "event_stream": ["b", "a"],
1804 "from": "#a_child",
1805 "scope": ["#b", "#c", "#a"] },
1807 { "runner": [ "myrunner", "a" ], "from": "#a_child" },
1808 { "runner": [ "b" ], "from": "#a_child" },
1809 { "directory": [ "b" ], "from": "#a_child" },
1810 ],
1811 "use": [
1812 { "protocol": ["zazzle"], "path": "/zazbaz" },
1814 { "protocol": ["foo"] },
1816 { "protocol": "bar" },
1817 { "service": ["b", "a"] },
1819 { "event_stream": ["b", "a"], "scope": ["#b", "#a"] },
1821 ],
1822 }));
1823 some.canonicalize();
1824
1825 assert_json_eq!(
1826 some,
1827 document(json!({
1828 "children": [
1829 { "name": "a_child", "url": "http://foo/a" },
1830 { "name": "b_child", "url": "http://foo/b" },
1831 ],
1832 "collections": [
1833 { "name": "a_coll", "durability": "transient" },
1834 { "name": "b_coll", "durability": "transient" },
1835 ],
1836 "environments": [
1837 { "name": "a_env" },
1838 { "name": "b_env" },
1839 ],
1840 "capabilities": [
1841 { "event_stream": ["a", "b"] },
1842 { "protocol": "arg", "path": "/arg" },
1843 { "protocol": ["bar", "foo"] },
1844 { "runner": "mypathrunner1", "path": "/foo" },
1845 { "runner": "mypathrunner2", "path": "/foo" },
1846 { "runner": "myrunner" },
1847 { "service": ["a", "b"] },
1848 ],
1849 "use": [
1850 { "event_stream": ["a", "b"], "scope": ["#a", "#b"] },
1851 { "protocol": ["bar", "foo"] },
1852 { "protocol": "zazzle", "path": "/zazbaz" },
1853 { "service": ["a", "b"] },
1854 ],
1855 "offer": [
1856 { "directory": "b", "from": "#a_child", "to": "#b_child" },
1857 {
1858 "event_stream": ["a", "b"],
1859 "from": "#a_child",
1860 "to": "#b_child",
1861 "scope": ["#a", "#b", "#c"],
1862 },
1863 { "protocol": ["bar", "foo"], "from": "#a_child", "to": "#b_child" },
1864 { "protocol": "baz", "from": "#a_child", "to": "#c_child" },
1865 { "runner": [ "a", "b", "myrunner" ], "from": "#a_child", "to": "#b_child" },
1866 { "service": ["a", "b"], "from": "#a_child", "to": "#b_child" },
1867 ],
1868 "expose": [
1869 { "directory": "b", "from": "#a_child" },
1870 {
1871 "event_stream": ["a", "b"],
1872 "from": "#a_child",
1873 "scope": ["#a", "#b", "#c"],
1874 },
1875 { "protocol": ["bar", "foo"], "from": "#a_child" },
1876 { "runner": [ "a", "b", "myrunner" ], "from": "#a_child" },
1877 { "service": ["a", "b"], "from": "#a_child" },
1878 ],
1879 }))
1880 )
1881 }
1882
1883 #[test]
1884 fn deny_unknown_config_type_fields() {
1885 let input = json!({ "config": { "foo": { "type": "bool", "unknown": "should error" } } });
1886 serde_json5::from_str::<Document>(&input.to_string())
1887 .expect_err("must reject unknown config field attributes");
1888 }
1889
1890 #[test]
1891 fn deny_unknown_config_nested_type_fields() {
1892 let input = json!({
1893 "config": {
1894 "foo": {
1895 "type": "vector",
1896 "max_count": 10,
1897 "element": {
1898 "type": "bool",
1899 "unknown": "should error"
1900 },
1901
1902 }
1903 }
1904 });
1905 serde_json5::from_str::<Document>(&input.to_string())
1906 .expect_err("must reject unknown config field attributes");
1907 }
1908
1909 #[test]
1910 fn test_merge_from_program() {
1911 let mut some = document(json!({ "program": { "binary": "bin/hello_world" } }));
1912 let mut other = document(json!({ "program": { "runner": "elf" } }));
1913 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1914 let expected =
1915 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1916 assert_eq!(some.program, expected.program);
1917 }
1918
1919 #[test]
1920 fn test_merge_from_program_without_runner() {
1921 let mut some =
1922 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1923 let mut other = document(json!({ "program": {} }));
1926 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1927 let expected =
1928 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1929 assert_eq!(some.program, expected.program);
1930 }
1931
1932 #[test]
1933 fn test_merge_from_program_overlapping_environ() {
1934 let mut some = document(json!({ "program": { "environ": ["1"] } }));
1936 let mut other = document(json!({ "program": { "environ": ["2"] } }));
1937 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1938 let expected = document(json!({ "program": { "environ": ["1", "2"] } }));
1939 assert_eq!(some.program, expected.program);
1940 }
1941
1942 #[test]
1943 fn test_merge_from_program_overlapping_runner() {
1944 let mut some =
1946 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1947 let mut other = document(json!({ "program": { "runner": "elf" } }));
1948 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1949 let expected =
1950 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1951 assert_eq!(some.program, expected.program);
1952 }
1953
1954 #[test_case(
1955 document(json!({ "program": { "runner": "elf" } })),
1956 document(json!({ "program": { "runner": "fle" } })),
1957 "runner"
1958 ; "when_runner_conflicts"
1959 )]
1960 #[test_case(
1961 document(json!({ "program": { "binary": "bin/hello_world" } })),
1962 document(json!({ "program": { "binary": "bin/hola_mundo" } })),
1963 "binary"
1964 ; "when_binary_conflicts"
1965 )]
1966 #[test_case(
1967 document(json!({ "program": { "args": ["a".to_owned()] } })),
1968 document(json!({ "program": { "args": ["b".to_owned()] } })),
1969 "args"
1970 ; "when_args_conflicts"
1971 )]
1972 fn test_merge_from_program_error(mut some: Document, mut other: Document, field: &str) {
1973 assert_matches::assert_matches!(
1974 some.merge_from(&mut other, &path::Path::new("some/path")),
1975 Err(Error::Validate { err, .. })
1976 if err == format!("manifest include had a conflicting `program.{}`: some/path", field)
1977 );
1978 }
1979
1980 #[test_case(
1981 document(json!({ "facets": { "my.key": "my.value" } })),
1982 document(json!({ "facets": { "other.key": "other.value" } })),
1983 document(json!({ "facets": { "my.key": "my.value", "other.key": "other.value" } }))
1984 ; "two separate keys"
1985 )]
1986 #[test_case(
1987 document(json!({ "facets": { "my.key": "my.value" } })),
1988 document(json!({ "facets": {} })),
1989 document(json!({ "facets": { "my.key": "my.value" } }))
1990 ; "empty other facet"
1991 )]
1992 #[test_case(
1993 document(json!({ "facets": {} })),
1994 document(json!({ "facets": { "other.key": "other.value" } })),
1995 document(json!({ "facets": { "other.key": "other.value" } }))
1996 ; "empty my facet"
1997 )]
1998 #[test_case(
1999 document(json!({ "facets": { "key": { "type": "some_type" } } })),
2000 document(json!({ "facets": { "key": { "runner": "some_runner"} } })),
2001 document(json!({ "facets": { "key": { "type": "some_type", "runner": "some_runner" } } }))
2002 ; "nested facet key"
2003 )]
2004 #[test_case(
2005 document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "type": "new type" }}}})),
2006 document(json!({ "facets": { "key": { "nested_key": { "runner": "some_runner" }} } })),
2007 document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "runner": "some_runner", "type": "new type" }}}}))
2008 ; "double nested facet key"
2009 )]
2010 #[test_case(
2011 document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2"] } } })),
2012 document(json!({ "facets": { "key": { "array_key": ["value_3", "value_4"] } } })),
2013 document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2", "value_3", "value_4"] } } }))
2014 ; "merge array values"
2015 )]
2016 fn test_merge_from_facets(mut my: Document, mut other: Document, expected: Document) {
2017 my.merge_from(&mut other, &Path::new("some/path")).unwrap();
2018 assert_eq!(my.facets, expected.facets);
2019 }
2020
2021 #[test_case(
2022 document(json!({ "facets": { "key": "my.value" }})),
2023 document(json!({ "facets": { "key": "other.value" }})),
2024 "facets.key"
2025 ; "conflict first level keys"
2026 )]
2027 #[test_case(
2028 document(json!({ "facets": { "key": {"type": "cts" }}})),
2029 document(json!({ "facets": { "key": {"type": "system" }}})),
2030 "facets.key.type"
2031 ; "conflict second level keys"
2032 )]
2033 #[test_case(
2034 document(json!({ "facets": { "key": {"type": {"key": "value" }}}})),
2035 document(json!({ "facets": { "key": {"type": "system" }}})),
2036 "facets.key.type"
2037 ; "incompatible self nested type"
2038 )]
2039 #[test_case(
2040 document(json!({ "facets": { "key": {"type": "system" }}})),
2041 document(json!({ "facets": { "key": {"type": {"key": "value" }}}})),
2042 "facets.key.type"
2043 ; "incompatible other nested type"
2044 )]
2045 #[test_case(
2046 document(json!({ "facets": { "key": {"type": {"key": "my.value" }}}})),
2047 document(json!({ "facets": { "key": {"type": {"key": "some.value" }}}})),
2048 "facets.key.type.key"
2049 ; "conflict third level keys"
2050 )]
2051 #[test_case(
2052 document(json!({ "facets": { "key": {"type": [ "value_1" ]}}})),
2053 document(json!({ "facets": { "key": {"type": "value_2" }}})),
2054 "facets.key.type"
2055 ; "incompatible keys"
2056 )]
2057 fn test_merge_from_facet_error(mut my: Document, mut other: Document, field: &str) {
2058 assert_matches::assert_matches!(
2059 my.merge_from(&mut other, &path::Path::new("some/path")),
2060 Err(Error::Validate { err, .. })
2061 if err == format!("manifest include had a conflicting `{}`: some/path", field)
2062 );
2063 }
2064
2065 #[test_case("protocol")]
2066 #[test_case("service")]
2067 #[test_case("event_stream")]
2068 fn test_merge_from_duplicate_use_array(typename: &str) {
2069 let mut my = document(json!({ "use": [{ typename: "a" }]}));
2070 let mut other = document(json!({ "use": [
2071 { typename: ["a", "b"], "availability": "optional"}
2072 ]}));
2073 let result = document(json!({ "use": [
2074 { typename: "a" },
2075 { typename: "b", "availability": "optional" },
2076 ]}));
2077
2078 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2079 assert_eq!(my, result);
2080 }
2081
2082 #[test_case("directory")]
2083 #[test_case("storage")]
2084 fn test_merge_from_duplicate_use_noarray(typename: &str) {
2085 let mut my = document(json!({ "use": [{ typename: "a", "path": "/a"}]}));
2086 let mut other = document(json!({ "use": [
2087 { typename: "a", "path": "/a", "availability": "optional" },
2088 { typename: "b", "path": "/b", "availability": "optional" },
2089 ]}));
2090 let result = document(json!({ "use": [
2091 { typename: "a", "path": "/a" },
2092 { typename: "b", "path": "/b", "availability": "optional" },
2093 ]}));
2094 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2095 assert_eq!(my, result);
2096 }
2097
2098 #[test_case("protocol")]
2099 #[test_case("service")]
2100 #[test_case("event_stream")]
2101 fn test_merge_from_duplicate_capabilities_array(typename: &str) {
2102 let mut my = document(json!({ "capabilities": [{ typename: "a" }]}));
2103 let mut other = document(json!({ "capabilities": [ { typename: ["a", "b"] } ]}));
2104 let result = document(json!({ "capabilities": [ { typename: "a" }, { typename: "b" } ]}));
2105
2106 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2107 assert_eq!(my, result);
2108 }
2109
2110 #[test_case("directory")]
2111 #[test_case("storage")]
2112 #[test_case("runner")]
2113 #[test_case("resolver")]
2114 fn test_merge_from_duplicate_capabilities_noarray(typename: &str) {
2115 let mut my = document(json!({ "capabilities": [{ typename: "a", "path": "/a"}]}));
2116 let mut other = document(json!({ "capabilities": [
2117 { typename: "a", "path": "/a" },
2118 { typename: "b", "path": "/b" },
2119 ]}));
2120 let result = document(json!({ "capabilities": [
2121 { typename: "a", "path": "/a" },
2122 { typename: "b", "path": "/b" },
2123 ]}));
2124 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2125 assert_eq!(my, result);
2126 }
2127
2128 #[test]
2129 fn test_merge_with_empty_names() {
2130 let mut my = document(json!({ "capabilities": [{ "path": "/a"}]}));
2132
2133 let mut other = document(json!({ "capabilities": [
2134 { "directory": "a", "path": "/a" },
2135 { "directory": "b", "path": "/b" },
2136 ]}));
2137 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap_err();
2138 }
2139
2140 #[test_case("protocol")]
2141 #[test_case("service")]
2142 #[test_case("event_stream")]
2143 #[test_case("directory")]
2144 #[test_case("storage")]
2145 #[test_case("runner")]
2146 #[test_case("resolver")]
2147 fn test_merge_from_duplicate_offers(typename: &str) {
2148 let mut my = document(json!({ "offer": [{ typename: "a", "from": "self", "to": "#c" }]}));
2149 let mut other = document(json!({ "offer": [
2150 { typename: ["a", "b"], "from": "self", "to": "#c", "availability": "optional" }
2151 ]}));
2152 let result = document(json!({ "offer": [
2153 { typename: "a", "from": "self", "to": "#c" },
2154 { typename: "b", "from": "self", "to": "#c", "availability": "optional" },
2155 ]}));
2156
2157 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2158 assert_eq!(my, result);
2159 }
2160
2161 #[test_case("protocol")]
2162 #[test_case("service")]
2163 #[test_case("event_stream")]
2164 #[test_case("directory")]
2165 #[test_case("runner")]
2166 #[test_case("resolver")]
2167 fn test_merge_from_duplicate_exposes(typename: &str) {
2168 let mut my = document(json!({ "expose": [{ typename: "a", "from": "self" }]}));
2169 let mut other = document(json!({ "expose": [
2170 { typename: ["a", "b"], "from": "self" }
2171 ]}));
2172 let result = document(json!({ "expose": [
2173 { typename: "a", "from": "self" },
2174 { typename: "b", "from": "self" },
2175 ]}));
2176
2177 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2178 assert_eq!(my, result);
2179 }
2180
2181 #[test_case(
2182 document(json!({ "use": [
2183 { "protocol": "a", "availability": "required" },
2184 { "protocol": "b", "availability": "optional" },
2185 { "protocol": "c", "availability": "transitional" },
2186 { "protocol": "d", "availability": "same_as_target" },
2187 ]})),
2188 document(json!({ "use": [
2189 { "protocol": ["a"], "availability": "required" },
2190 { "protocol": ["b"], "availability": "optional" },
2191 { "protocol": ["c"], "availability": "transitional" },
2192 { "protocol": ["d"], "availability": "same_as_target" },
2193 ]})),
2194 document(json!({ "use": [
2195 { "protocol": "a", "availability": "required" },
2196 { "protocol": "b", "availability": "optional" },
2197 { "protocol": "c", "availability": "transitional" },
2198 { "protocol": "d", "availability": "same_as_target" },
2199 ]}))
2200 ; "merge both same"
2201 )]
2202 #[test_case(
2203 document(json!({ "use": [
2204 { "protocol": "a", "availability": "optional" },
2205 { "protocol": "b", "availability": "transitional" },
2206 { "protocol": "c", "availability": "transitional" },
2207 ]})),
2208 document(json!({ "use": [
2209 { "protocol": ["a", "x"], "availability": "required" },
2210 { "protocol": ["b", "y"], "availability": "optional" },
2211 { "protocol": ["c", "z"], "availability": "required" },
2212 ]})),
2213 document(json!({ "use": [
2214 { "protocol": ["a", "x"], "availability": "required" },
2215 { "protocol": ["b", "y"], "availability": "optional" },
2216 { "protocol": ["c", "z"], "availability": "required" },
2217 ]}))
2218 ; "merge with upgrade"
2219 )]
2220 #[test_case(
2221 document(json!({ "use": [
2222 { "protocol": "a", "availability": "required" },
2223 { "protocol": "b", "availability": "optional" },
2224 { "protocol": "c", "availability": "required" },
2225 ]})),
2226 document(json!({ "use": [
2227 { "protocol": ["a", "x"], "availability": "optional" },
2228 { "protocol": ["b", "y"], "availability": "transitional" },
2229 { "protocol": ["c", "z"], "availability": "transitional" },
2230 ]})),
2231 document(json!({ "use": [
2232 { "protocol": "a", "availability": "required" },
2233 { "protocol": "b", "availability": "optional" },
2234 { "protocol": "c", "availability": "required" },
2235 { "protocol": "x", "availability": "optional" },
2236 { "protocol": "y", "availability": "transitional" },
2237 { "protocol": "z", "availability": "transitional" },
2238 ]}))
2239 ; "merge with downgrade"
2240 )]
2241 #[test_case(
2242 document(json!({ "use": [
2243 { "protocol": "a", "availability": "optional" },
2244 { "protocol": "b", "availability": "transitional" },
2245 { "protocol": "c", "availability": "transitional" },
2246 ]})),
2247 document(json!({ "use": [
2248 { "protocol": ["a", "x"], "availability": "same_as_target" },
2249 { "protocol": ["b", "y"], "availability": "same_as_target" },
2250 { "protocol": ["c", "z"], "availability": "same_as_target" },
2251 ]})),
2252 document(json!({ "use": [
2253 { "protocol": "a", "availability": "optional" },
2254 { "protocol": "b", "availability": "transitional" },
2255 { "protocol": "c", "availability": "transitional" },
2256 { "protocol": ["a", "x"], "availability": "same_as_target" },
2257 { "protocol": ["b", "y"], "availability": "same_as_target" },
2258 { "protocol": ["c", "z"], "availability": "same_as_target" },
2259 ]}))
2260 ; "merge with no replacement"
2261 )]
2262 #[test_case(
2263 document(json!({ "use": [
2264 { "protocol": ["a", "b", "c"], "availability": "optional" },
2265 { "protocol": "d", "availability": "same_as_target" },
2266 { "protocol": ["e", "f"] },
2267 ]})),
2268 document(json!({ "use": [
2269 { "protocol": ["c", "e", "g"] },
2270 { "protocol": ["d", "h"] },
2271 { "protocol": ["f", "i"], "availability": "transitional" },
2272 ]})),
2273 document(json!({ "use": [
2274 { "protocol": ["a", "b"], "availability": "optional" },
2275 { "protocol": "d", "availability": "same_as_target" },
2276 { "protocol": ["e", "f"] },
2277 { "protocol": ["c", "g"] },
2278 { "protocol": ["d", "h"] },
2279 { "protocol": "i", "availability": "transitional" },
2280 ]}))
2281 ; "merge multiple"
2282 )]
2283
2284 fn test_merge_from_duplicate_capability_availability(
2285 mut my: Document,
2286 mut other: Document,
2287 result: Document,
2288 ) {
2289 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2290 assert_eq!(my, result);
2291 }
2292
2293 #[test_case(
2294 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2295 document(json!({ "use": [{ "protocol": ["c", "d"] }]})),
2296 document(json!({ "use": [
2297 { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
2298 ]}))
2299 ; "merge capabilities with disjoint sets"
2300 )]
2301 #[test_case(
2302 document(json!({ "use": [
2303 { "protocol": ["a"] },
2304 { "protocol": "b" },
2305 ]})),
2306 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2307 document(json!({ "use": [
2308 { "protocol": ["a"] }, { "protocol": "b" },
2309 ]}))
2310 ; "merge capabilities with equal set"
2311 )]
2312 #[test_case(
2313 document(json!({ "use": [
2314 { "protocol": ["a", "b"] },
2315 { "protocol": "c" },
2316 ]})),
2317 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2318 document(json!({ "use": [
2319 { "protocol": ["a", "b"] }, { "protocol": "c" },
2320 ]}))
2321 ; "merge capabilities with subset"
2322 )]
2323 #[test_case(
2324 document(json!({ "use": [
2325 { "protocol": ["a", "b"] },
2326 ]})),
2327 document(json!({ "use": [{ "protocol": ["a", "b", "c"] }]})),
2328 document(json!({ "use": [
2329 { "protocol": ["a", "b"] },
2330 { "protocol": "c" },
2331 ]}))
2332 ; "merge capabilities with superset"
2333 )]
2334 #[test_case(
2335 document(json!({ "use": [
2336 { "protocol": ["a", "b"] },
2337 ]})),
2338 document(json!({ "use": [{ "protocol": ["b", "c", "d"] }]})),
2339 document(json!({ "use": [
2340 { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
2341 ]}))
2342 ; "merge capabilities with intersection"
2343 )]
2344 #[test_case(
2345 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2346 document(json!({ "use": [
2347 { "protocol": ["c", "b", "d"] },
2348 { "protocol": ["e", "d"] },
2349 ]})),
2350 document(json!({ "use": [
2351 {"protocol": ["a", "b"] },
2352 {"protocol": ["c", "d"] },
2353 {"protocol": "e" }]}))
2354 ; "merge capabilities from multiple arrays"
2355 )]
2356 #[test_case(
2357 document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
2358 document(json!({ "use": [{ "service": "foo.bar.Baz", "from": "self"}]})),
2359 document(json!({ "use": [
2360 {"protocol": "foo.bar.Baz", "from": "self"},
2361 {"service": "foo.bar.Baz", "from": "self"}]}))
2362 ; "merge capabilities, types don't match"
2363 )]
2364 #[test_case(
2365 document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
2366 document(json!({ "use": [{ "protocol": "foo.bar.Baz" }]})),
2367 document(json!({ "use": [
2368 {"protocol": "foo.bar.Baz", "from": "self"},
2369 {"protocol": "foo.bar.Baz"}]}))
2370 ; "merge capabilities, fields don't match"
2371 )]
2372
2373 fn test_merge_from_duplicate_capability(
2374 mut my: Document,
2375 mut other: Document,
2376 result: Document,
2377 ) {
2378 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2379 assert_eq!(my, result);
2380 }
2381
2382 #[test_case(&Right::Connect; "connect right")]
2383 #[test_case(&Right::Enumerate; "enumerate right")]
2384 #[test_case(&Right::Execute; "execute right")]
2385 #[test_case(&Right::GetAttributes; "getattr right")]
2386 #[test_case(&Right::ModifyDirectory; "modifydir right")]
2387 #[test_case(&Right::ReadBytes; "readbytes right")]
2388 #[test_case(&Right::Traverse; "traverse right")]
2389 #[test_case(&Right::UpdateAttributes; "updateattrs right")]
2390 #[test_case(&Right::WriteBytes; "writebytes right")]
2391 #[test_case(&Right::ReadAlias; "r right")]
2392 #[test_case(&Right::WriteAlias; "w right")]
2393 #[test_case(&Right::ExecuteAlias; "x right")]
2394 #[test_case(&Right::ReadWriteAlias; "rw right")]
2395 #[test_case(&Right::ReadExecuteAlias; "rx right")]
2396 #[test_case(&OfferFromRef::Self_; "offer from self")]
2397 #[test_case(&OfferFromRef::Parent; "offer from parent")]
2398 #[test_case(&OfferFromRef::Named(Name::new("child".to_string()).unwrap()); "offer from named")]
2399 #[test_case(
2400 &document(json!({}));
2401 "empty document"
2402 )]
2403 #[test_case(
2404 &document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
2405 "use one from self"
2406 )]
2407 #[test_case(
2408 &document(json!({ "use": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
2409 "use multiple from self"
2410 )]
2411 #[test_case(
2412 &document(json!({
2413 "offer": [{ "protocol": "foo.bar.Baz", "from": "self", "to": "#elements"}],
2414 "collections" :[{"name": "elements", "durability": "transient" }]
2415 }));
2416 "offer from self to collection"
2417 )]
2418 #[test_case(
2419 &document(json!({
2420 "offer": [
2421 { "service": "foo.bar.Baz", "from": "self", "to": "#elements" },
2422 { "service": "some.other.Service", "from": "self", "to": "#elements"},
2423 ],
2424 "collections":[ {"name": "elements", "durability": "transient"} ]}));
2425 "service offers"
2426 )]
2427 #[test_case(
2428 &document(json!({ "expose": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
2429 "expose protocols from self"
2430 )]
2431 #[test_case(
2432 &document(json!({ "expose": [{ "service": ["foo.bar.Baz", "some.other.Service"], "from": "self"}]}));
2433 "expose service from self"
2434 )]
2435 #[test_case(
2436 &document(json!({ "capabilities": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
2437 "capabilities from self"
2438 )]
2439 #[test_case(
2440 &document(json!({ "facets": { "my.key": "my.value" } }));
2441 "facets"
2442 )]
2443 #[test_case(
2444 &document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2445 "elf runner program"
2446 )]
2447 fn serialize_roundtrips<T>(val: &T)
2448 where
2449 T: serde::de::DeserializeOwned + Serialize + PartialEq + std::fmt::Debug,
2450 {
2451 let raw = serde_json::to_string(val).expect("serializing `val` should work");
2452 let parsed: T =
2453 serde_json::from_str(&raw).expect("must be able to parse back serialized value");
2454 assert_eq!(val, &parsed, "parsed value must equal original value");
2455 }
2456}