1pub mod error;
12pub mod features;
13pub mod one_or_many;
14pub mod types;
15pub(crate) mod validate;
16
17#[allow(unused)] pub mod translate;
19
20use crate::error::Error;
21use cml_macro::{OneOrMany, Reference};
22use json5format::{FormatOptions, PathOption};
23use maplit::{hashmap, hashset};
24use serde::{Deserialize, Serialize, de, ser};
25use serde_json::{Map, Value};
26use std::fmt;
27use std::hash::Hash;
28use std::num::NonZeroU32;
29use std::str::FromStr;
30use std::sync::Arc;
31
32pub use crate::types::capability::{Capability, CapabilityFromRef, ContextCapability};
33pub use crate::types::capability_id::CapabilityId;
34pub use crate::types::child::Child;
35pub use crate::types::collection::Collection;
36use crate::types::common::{ContextCapabilityClause, ContextSpanned, Origin};
37pub use crate::types::document::{
38 Document, DocumentContext, ParsedDocument, convert_parsed_to_document,
39};
40pub use crate::types::environment::{Environment, ResolverRegistration};
41pub use crate::types::expose::{ContextExpose, Expose};
42pub use crate::types::offer::{
43 Offer, OfferFromRef, OfferToAllCapability, OfferToRef, offer_to_all_from_offer,
44};
45pub use crate::types::program::Program;
46pub use crate::types::r#use::{Use, UseFromRef};
47
48pub use cm_types::{
49 AllowedOffers, Availability, BorrowedName, BoundedName, DeliveryType, DependencyType,
50 Durability, HandleType, Name, NamespacePath, OnTerminate, ParseError, Path, RelativePath,
51 StartupMode, StorageId, Url,
52};
53use error::Location;
54
55pub use crate::one_or_many::OneOrMany;
56pub use crate::translate::{CompileOptions, compile};
57pub use crate::validate::{CapabilityRequirements, MustUseRequirement};
58
59pub fn parse_one_document(buffer: &String, file: &std::path::Path) -> Result<Document, Error> {
61 serde_json5::from_str(&buffer).map_err(|e| {
62 let serde_json5::Error::Message { location, msg } = e;
63 let location = location.map(|l| Location { line: l.line, column: l.column });
64 Error::parse(msg, location, Some(file))
65 })
66}
67
68pub fn load_cml_with_context(
69 buffer: &String,
70 file: &std::path::Path,
71) -> Result<DocumentContext, Error> {
72 let file_arc = Arc::new(file.to_path_buf());
73 let parsed_doc: ParsedDocument = json_spanned_value::from_str(&buffer).map_err(|e| {
74 let location = Location { line: e.line(), column: e.column() };
75 Error::parse(e, Some(location), Some(file))
76 })?;
77 let doc = convert_parsed_to_document(parsed_doc, file_arc, buffer);
78 Ok(doc)
79}
80
81pub fn parse_many_documents(
84 buffer: &String,
85 file: &std::path::Path,
86) -> Result<Vec<Document>, Error> {
87 let res: Result<Vec<Document>, _> = serde_json5::from_str(&buffer);
88 match res {
89 Err(_) => {
90 let d = parse_one_document(buffer, file)?;
91 Ok(vec![d])
92 }
93 Ok(docs) => Ok(docs),
94 }
95}
96
97pub fn byte_index_to_location(source: &String, index: usize) -> Location {
98 let mut line = 1usize;
99 let mut column = 1usize;
100
101 for (i, ch) in source.char_indices() {
102 if i == index {
103 break;
104 }
105
106 if ch == '\n' {
107 line += 1;
108 column = 1;
109 } else {
110 column += 1;
111 }
112 }
113
114 return Location { line, column };
115}
116
117#[derive(OneOrMany, Debug, Clone)]
119#[one_or_many(
120 expected = "a name or nonempty array of names, with unique elements",
121 inner_type = "Name",
122 min_length = 1,
123 unique_items = true
124)]
125pub struct OneOrManyNames;
126
127#[derive(OneOrMany, Debug, Clone)]
129#[one_or_many(
130 expected = "a path or nonempty array of paths, with unique elements",
131 inner_type = "Path",
132 min_length = 1,
133 unique_items = true
134)]
135pub struct OneOrManyPaths;
136
137#[derive(OneOrMany, Debug, Clone)]
139#[one_or_many(
140 expected = "one or an array of \"#<collection-name>\", or \"#<child-name>\"",
141 inner_type = "EventScope",
142 min_length = 1,
143 unique_items = true
144)]
145pub struct OneOrManyEventScope;
146
147#[derive(Debug, Deserialize, PartialEq, Eq, Hash, Clone, Serialize)]
149#[serde(rename_all = "snake_case")]
150pub enum SourceAvailability {
151 Required,
152 Unknown,
153}
154
155impl Default for SourceAvailability {
156 fn default() -> Self {
157 Self::Required
158 }
159}
160
161impl<T> Canonicalize for Vec<T>
162where
163 T: Canonicalize + CapabilityClause + PathClause,
164{
165 fn canonicalize(&mut self) {
166 let mut to_merge: Vec<(T, Vec<Name>)> = vec![];
170 let mut to_keep: Vec<T> = vec![];
171 self.iter().for_each(|c| {
172 if !c.are_many_names_allowed() || c.path().is_some() {
174 to_keep.push(c.clone());
175 return;
176 }
177 let mut names: Vec<Name> = c.names().into_iter().map(Into::into).collect();
178 let mut copy: T = c.clone();
179 copy.set_names(vec![Name::from_str("a").unwrap()]); let r = to_merge.iter().position(|(t, _)| t == ©);
181 match r {
182 Some(i) => to_merge[i].1.append(&mut names),
183 None => to_merge.push((copy, names)),
184 };
185 });
186 let mut merged = to_merge
187 .into_iter()
188 .map(|(mut t, names)| {
189 t.set_names(names);
190 t
191 })
192 .collect::<Vec<_>>();
193 to_keep.append(&mut merged);
194 *self = to_keep;
195
196 self.iter_mut().for_each(|c| c.canonicalize());
197 self.sort_by(|a, b| {
198 let a_type = a.capability_type().unwrap();
201 let b_type = b.capability_type().unwrap();
202 a_type.cmp(b_type).then_with(|| {
203 let a_names = a.names();
204 let b_names = b.names();
205 let a_first_name = a_names.first().unwrap();
206 let b_first_name = b_names.first().unwrap();
207 a_first_name.cmp(b_first_name)
208 })
209 });
210 }
211}
212
213#[derive(Debug, PartialEq, Eq, Hash, Clone)]
221pub enum AnyRef<'a> {
222 Named(&'a BorrowedName),
224 Parent,
226 Framework,
228 Debug,
230 Self_,
232 Void,
234 Dictionary(&'a DictionaryRef),
236 OwnDictionary(&'a BorrowedName),
239}
240
241impl fmt::Display for AnyRef<'_> {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 match self {
245 Self::Named(name) => write!(f, "#{}", name),
246 Self::Parent => write!(f, "parent"),
247 Self::Framework => write!(f, "framework"),
248 Self::Debug => write!(f, "debug"),
249 Self::Self_ => write!(f, "self"),
250 Self::Void => write!(f, "void"),
251 Self::Dictionary(d) => write!(f, "{}", d),
252 Self::OwnDictionary(name) => write!(f, "self/{}", name),
253 }
254 }
255}
256
257#[derive(Debug, PartialEq, Eq, Hash, Clone)]
259pub struct DictionaryRef {
260 pub path: RelativePath,
262 pub root: RootDictionaryRef,
263}
264
265impl<'a> From<&'a DictionaryRef> for AnyRef<'a> {
266 fn from(r: &'a DictionaryRef) -> Self {
267 Self::Dictionary(r)
268 }
269}
270
271impl FromStr for DictionaryRef {
272 type Err = ParseError;
273
274 fn from_str(path: &str) -> Result<Self, ParseError> {
275 match path.find('/') {
276 Some(n) => {
277 let root = path[..n].parse().map_err(|_| ParseError::InvalidValue)?;
278 let path = RelativePath::new(&path[n + 1..])?;
279 Ok(Self { root, path })
280 }
281 None => Err(ParseError::InvalidValue),
282 }
283 }
284}
285
286impl fmt::Display for DictionaryRef {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 write!(f, "{}/{}", self.root, self.path)
289 }
290}
291
292impl ser::Serialize for DictionaryRef {
293 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
294 where
295 S: serde::ser::Serializer,
296 {
297 format!("{}", self).serialize(serializer)
298 }
299}
300
301const DICTIONARY_REF_EXPECT_STR: &str = "a path to a dictionary no more \
302 than 4095 characters in length";
303
304impl<'de> de::Deserialize<'de> for DictionaryRef {
305 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
306 where
307 D: de::Deserializer<'de>,
308 {
309 struct Visitor;
310
311 impl<'de> de::Visitor<'de> for Visitor {
312 type Value = DictionaryRef;
313
314 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 f.write_str(DICTIONARY_REF_EXPECT_STR)
316 }
317
318 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
319 where
320 E: de::Error,
321 {
322 s.parse().map_err(|err| match err {
323 ParseError::InvalidValue => {
324 E::invalid_value(de::Unexpected::Str(s), &DICTIONARY_REF_EXPECT_STR)
325 }
326 ParseError::TooLong | ParseError::Empty => {
327 E::invalid_length(s.len(), &DICTIONARY_REF_EXPECT_STR)
328 }
329 e => {
330 panic!("unexpected parse error: {:?}", e);
331 }
332 })
333 }
334 }
335
336 deserializer.deserialize_string(Visitor)
337 }
338}
339
340#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
342#[reference(expected = "\"parent\", \"self\", \"#<child-name>\"")]
343pub enum RootDictionaryRef {
344 Named(Name),
346 Parent,
348 Self_,
350}
351
352#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference, Ord, PartialOrd)]
354#[reference(expected = "\"#<collection-name>\", \"#<child-name>\", or none")]
355pub enum EventScope {
356 Named(Name),
358}
359
360#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
361#[serde(rename_all = "snake_case")]
362pub enum ConfigType {
363 Bool,
364 Uint8,
365 Uint16,
366 Uint32,
367 Uint64,
368 Int8,
369 Int16,
370 Int32,
371 Int64,
372 String,
373 Vector,
374}
375
376impl From<&cm_rust::ConfigValueType> for ConfigType {
377 fn from(value: &cm_rust::ConfigValueType) -> Self {
378 match value {
379 cm_rust::ConfigValueType::Bool => ConfigType::Bool,
380 cm_rust::ConfigValueType::Uint8 => ConfigType::Uint8,
381 cm_rust::ConfigValueType::Int8 => ConfigType::Int8,
382 cm_rust::ConfigValueType::Uint16 => ConfigType::Uint16,
383 cm_rust::ConfigValueType::Int16 => ConfigType::Int16,
384 cm_rust::ConfigValueType::Uint32 => ConfigType::Uint32,
385 cm_rust::ConfigValueType::Int32 => ConfigType::Int32,
386 cm_rust::ConfigValueType::Uint64 => ConfigType::Uint64,
387 cm_rust::ConfigValueType::Int64 => ConfigType::Int64,
388 cm_rust::ConfigValueType::String { .. } => ConfigType::String,
389 cm_rust::ConfigValueType::Vector { .. } => ConfigType::Vector,
390 }
391 }
392}
393
394#[derive(Clone, Deserialize, Debug, PartialEq, Serialize)]
395#[serde(tag = "type", deny_unknown_fields, rename_all = "lowercase")]
396pub enum ConfigNestedValueType {
397 Bool {},
398 Uint8 {},
399 Uint16 {},
400 Uint32 {},
401 Uint64 {},
402 Int8 {},
403 Int16 {},
404 Int32 {},
405 Int64 {},
406 String { max_size: NonZeroU32 },
407}
408
409impl ConfigNestedValueType {
410 pub fn update_digest(&self, hasher: &mut impl sha2::Digest) {
412 let val = match self {
413 ConfigNestedValueType::Bool {} => 0u8,
414 ConfigNestedValueType::Uint8 {} => 1u8,
415 ConfigNestedValueType::Uint16 {} => 2u8,
416 ConfigNestedValueType::Uint32 {} => 3u8,
417 ConfigNestedValueType::Uint64 {} => 4u8,
418 ConfigNestedValueType::Int8 {} => 5u8,
419 ConfigNestedValueType::Int16 {} => 6u8,
420 ConfigNestedValueType::Int32 {} => 7u8,
421 ConfigNestedValueType::Int64 {} => 8u8,
422 ConfigNestedValueType::String { max_size } => {
423 hasher.update(max_size.get().to_le_bytes());
424 9u8
425 }
426 };
427 hasher.update([val])
428 }
429}
430
431impl From<ConfigNestedValueType> for cm_rust::ConfigNestedValueType {
432 fn from(value: ConfigNestedValueType) -> Self {
433 match value {
434 ConfigNestedValueType::Bool {} => cm_rust::ConfigNestedValueType::Bool,
435 ConfigNestedValueType::Uint8 {} => cm_rust::ConfigNestedValueType::Uint8,
436 ConfigNestedValueType::Uint16 {} => cm_rust::ConfigNestedValueType::Uint16,
437 ConfigNestedValueType::Uint32 {} => cm_rust::ConfigNestedValueType::Uint32,
438 ConfigNestedValueType::Uint64 {} => cm_rust::ConfigNestedValueType::Uint64,
439 ConfigNestedValueType::Int8 {} => cm_rust::ConfigNestedValueType::Int8,
440 ConfigNestedValueType::Int16 {} => cm_rust::ConfigNestedValueType::Int16,
441 ConfigNestedValueType::Int32 {} => cm_rust::ConfigNestedValueType::Int32,
442 ConfigNestedValueType::Int64 {} => cm_rust::ConfigNestedValueType::Int64,
443 ConfigNestedValueType::String { max_size } => {
444 cm_rust::ConfigNestedValueType::String { max_size: max_size.into() }
445 }
446 }
447 }
448}
449
450impl TryFrom<&cm_rust::ConfigNestedValueType> for ConfigNestedValueType {
451 type Error = ();
452 fn try_from(nested: &cm_rust::ConfigNestedValueType) -> Result<Self, ()> {
453 Ok(match nested {
454 cm_rust::ConfigNestedValueType::Bool => ConfigNestedValueType::Bool {},
455 cm_rust::ConfigNestedValueType::Uint8 => ConfigNestedValueType::Uint8 {},
456 cm_rust::ConfigNestedValueType::Int8 => ConfigNestedValueType::Int8 {},
457 cm_rust::ConfigNestedValueType::Uint16 => ConfigNestedValueType::Uint16 {},
458 cm_rust::ConfigNestedValueType::Int16 => ConfigNestedValueType::Int16 {},
459 cm_rust::ConfigNestedValueType::Uint32 => ConfigNestedValueType::Uint32 {},
460 cm_rust::ConfigNestedValueType::Int32 => ConfigNestedValueType::Int32 {},
461 cm_rust::ConfigNestedValueType::Uint64 => ConfigNestedValueType::Uint64 {},
462 cm_rust::ConfigNestedValueType::Int64 => ConfigNestedValueType::Int64 {},
463 cm_rust::ConfigNestedValueType::String { max_size } => {
464 ConfigNestedValueType::String { max_size: NonZeroU32::new(*max_size).ok_or(())? }
465 }
466 })
467 }
468}
469
470#[derive(Clone, Hash, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize)]
471pub struct ConfigKey(String);
472
473impl ConfigKey {
474 pub fn as_str(&self) -> &str {
475 self.0.as_str()
476 }
477}
478
479impl std::fmt::Display for ConfigKey {
480 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
481 write!(f, "{}", self.0)
482 }
483}
484
485impl FromStr for ConfigKey {
486 type Err = ParseError;
487
488 fn from_str(s: &str) -> Result<Self, ParseError> {
489 let length = s.len();
490 if length == 0 {
491 return Err(ParseError::Empty);
492 }
493 if length > 64 {
494 return Err(ParseError::TooLong);
495 }
496
497 let first_is_letter = s.chars().next().expect("non-empty string").is_ascii_lowercase();
499 let contains_invalid_chars =
501 s.chars().any(|c| !(c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_'));
502 let last_is_underscore = s.chars().next_back().expect("non-empty string") == '_';
504
505 if !first_is_letter || contains_invalid_chars || last_is_underscore {
506 return Err(ParseError::InvalidValue);
507 }
508
509 Ok(Self(s.to_string()))
510 }
511}
512
513impl<'de> de::Deserialize<'de> for ConfigKey {
514 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
515 where
516 D: de::Deserializer<'de>,
517 {
518 struct Visitor;
519
520 impl<'de> de::Visitor<'de> for Visitor {
521 type Value = ConfigKey;
522
523 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524 f.write_str(
525 "a non-empty string no more than 64 characters in length, which must \
526 start with a letter, can contain letters, numbers, and underscores, \
527 but cannot end with an underscore",
528 )
529 }
530
531 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
532 where
533 E: de::Error,
534 {
535 s.parse().map_err(|err| match err {
536 ParseError::InvalidValue => E::invalid_value(
537 de::Unexpected::Str(s),
538 &"a name which must start with a letter, can contain letters, \
539 numbers, and underscores, but cannot end with an underscore",
540 ),
541 ParseError::TooLong | ParseError::Empty => E::invalid_length(
542 s.len(),
543 &"a non-empty name no more than 64 characters in length",
544 ),
545 e => {
546 panic!("unexpected parse error: {:?}", e);
547 }
548 })
549 }
550 }
551 deserializer.deserialize_string(Visitor)
552 }
553}
554
555#[derive(Clone, Deserialize, Debug, PartialEq, Serialize)]
556#[serde(deny_unknown_fields, rename_all = "lowercase")]
557pub enum ConfigRuntimeSource {
558 Parent,
559}
560
561#[derive(Clone, Deserialize, Debug, PartialEq, Serialize)]
562#[serde(tag = "type", deny_unknown_fields, rename_all = "lowercase")]
563pub enum ConfigValueType {
564 Bool {
565 mutability: Option<Vec<ConfigRuntimeSource>>,
566 },
567 Uint8 {
568 mutability: Option<Vec<ConfigRuntimeSource>>,
569 },
570 Uint16 {
571 mutability: Option<Vec<ConfigRuntimeSource>>,
572 },
573 Uint32 {
574 mutability: Option<Vec<ConfigRuntimeSource>>,
575 },
576 Uint64 {
577 mutability: Option<Vec<ConfigRuntimeSource>>,
578 },
579 Int8 {
580 mutability: Option<Vec<ConfigRuntimeSource>>,
581 },
582 Int16 {
583 mutability: Option<Vec<ConfigRuntimeSource>>,
584 },
585 Int32 {
586 mutability: Option<Vec<ConfigRuntimeSource>>,
587 },
588 Int64 {
589 mutability: Option<Vec<ConfigRuntimeSource>>,
590 },
591 String {
592 max_size: NonZeroU32,
593 mutability: Option<Vec<ConfigRuntimeSource>>,
594 },
595 Vector {
596 max_count: NonZeroU32,
597 element: ConfigNestedValueType,
598 mutability: Option<Vec<ConfigRuntimeSource>>,
599 },
600}
601
602impl ConfigValueType {
603 pub fn update_digest(&self, hasher: &mut impl sha2::Digest) {
605 let val = match self {
606 ConfigValueType::Bool { .. } => 0u8,
607 ConfigValueType::Uint8 { .. } => 1u8,
608 ConfigValueType::Uint16 { .. } => 2u8,
609 ConfigValueType::Uint32 { .. } => 3u8,
610 ConfigValueType::Uint64 { .. } => 4u8,
611 ConfigValueType::Int8 { .. } => 5u8,
612 ConfigValueType::Int16 { .. } => 6u8,
613 ConfigValueType::Int32 { .. } => 7u8,
614 ConfigValueType::Int64 { .. } => 8u8,
615 ConfigValueType::String { max_size, .. } => {
616 hasher.update(max_size.get().to_le_bytes());
617 9u8
618 }
619 ConfigValueType::Vector { max_count, element, .. } => {
620 hasher.update(max_count.get().to_le_bytes());
621 element.update_digest(hasher);
622 10u8
623 }
624 };
625 hasher.update([val])
626 }
627}
628
629impl From<ConfigValueType> for cm_rust::ConfigValueType {
630 fn from(value: ConfigValueType) -> Self {
631 match value {
632 ConfigValueType::Bool { .. } => cm_rust::ConfigValueType::Bool,
633 ConfigValueType::Uint8 { .. } => cm_rust::ConfigValueType::Uint8,
634 ConfigValueType::Uint16 { .. } => cm_rust::ConfigValueType::Uint16,
635 ConfigValueType::Uint32 { .. } => cm_rust::ConfigValueType::Uint32,
636 ConfigValueType::Uint64 { .. } => cm_rust::ConfigValueType::Uint64,
637 ConfigValueType::Int8 { .. } => cm_rust::ConfigValueType::Int8,
638 ConfigValueType::Int16 { .. } => cm_rust::ConfigValueType::Int16,
639 ConfigValueType::Int32 { .. } => cm_rust::ConfigValueType::Int32,
640 ConfigValueType::Int64 { .. } => cm_rust::ConfigValueType::Int64,
641 ConfigValueType::String { max_size, .. } => {
642 cm_rust::ConfigValueType::String { max_size: max_size.into() }
643 }
644 ConfigValueType::Vector { max_count, element, .. } => {
645 cm_rust::ConfigValueType::Vector {
646 max_count: max_count.into(),
647 nested_type: element.into(),
648 }
649 }
650 }
651 }
652}
653
654pub trait FromClause {
655 fn from_(&self) -> OneOrMany<AnyRef<'_>>;
656}
657
658pub trait FromClauseContext {
659 fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>>;
660}
661
662pub trait CapabilityClause: Clone + PartialEq + std::fmt::Debug {
663 fn service(&self) -> Option<OneOrMany<&BorrowedName>>;
664 fn protocol(&self) -> Option<OneOrMany<&BorrowedName>>;
665 fn directory(&self) -> Option<OneOrMany<&BorrowedName>>;
666 fn storage(&self) -> Option<OneOrMany<&BorrowedName>>;
667 fn runner(&self) -> Option<OneOrMany<&BorrowedName>>;
668 fn resolver(&self) -> Option<OneOrMany<&BorrowedName>>;
669 fn event_stream(&self) -> Option<OneOrMany<&BorrowedName>>;
670 fn dictionary(&self) -> Option<OneOrMany<&BorrowedName>>;
671 fn config(&self) -> Option<OneOrMany<&BorrowedName>>;
672 fn set_service(&mut self, o: Option<OneOrMany<Name>>);
673 fn set_protocol(&mut self, o: Option<OneOrMany<Name>>);
674 fn set_directory(&mut self, o: Option<OneOrMany<Name>>);
675 fn set_storage(&mut self, o: Option<OneOrMany<Name>>);
676 fn set_runner(&mut self, o: Option<OneOrMany<Name>>);
677 fn set_resolver(&mut self, o: Option<OneOrMany<Name>>);
678 fn set_event_stream(&mut self, o: Option<OneOrMany<Name>>);
679 fn set_dictionary(&mut self, o: Option<OneOrMany<Name>>);
680 fn set_config(&mut self, o: Option<OneOrMany<Name>>);
681
682 fn availability(&self) -> Option<Availability>;
683 fn set_availability(&mut self, a: Option<Availability>);
684
685 fn capability_type(&self) -> Result<&'static str, Error> {
690 let mut types = Vec::new();
691 if self.service().is_some() {
692 types.push("service");
693 }
694 if self.protocol().is_some() {
695 types.push("protocol");
696 }
697 if self.directory().is_some() {
698 types.push("directory");
699 }
700 if self.storage().is_some() {
701 types.push("storage");
702 }
703 if self.event_stream().is_some() {
704 types.push("event_stream");
705 }
706 if self.runner().is_some() {
707 types.push("runner");
708 }
709 if self.config().is_some() {
710 types.push("config");
711 }
712 if self.resolver().is_some() {
713 types.push("resolver");
714 }
715 if self.dictionary().is_some() {
716 types.push("dictionary");
717 }
718 match types.len() {
719 0 => {
720 let supported_keywords = self
721 .supported()
722 .into_iter()
723 .map(|k| format!("\"{}\"", k))
724 .collect::<Vec<_>>()
725 .join(", ");
726 Err(Error::validate(format!(
727 "`{}` declaration is missing a capability keyword, one of: {}",
728 self.decl_type(),
729 supported_keywords,
730 )))
731 }
732 1 => Ok(types[0]),
733 _ => Err(Error::validate(format!(
734 "{} declaration has multiple capability types defined: {:?}",
735 self.decl_type(),
736 types
737 ))),
738 }
739 }
740
741 fn are_many_names_allowed(&self) -> bool;
743
744 fn decl_type(&self) -> &'static str;
745 fn supported(&self) -> &[&'static str];
746
747 fn names(&self) -> Vec<&BorrowedName> {
750 let res = vec![
751 self.service(),
752 self.protocol(),
753 self.directory(),
754 self.storage(),
755 self.runner(),
756 self.config(),
757 self.resolver(),
758 self.event_stream(),
759 self.dictionary(),
760 ];
761 res.into_iter()
762 .map(|o| o.map(|o| o.into_iter().collect::<Vec<&BorrowedName>>()).unwrap_or(vec![]))
763 .flatten()
764 .collect()
765 }
766
767 fn set_names(&mut self, names: Vec<Name>) {
768 let names = match names.len() {
769 0 => None,
770 1 => Some(OneOrMany::One(names.first().unwrap().clone())),
771 _ => Some(OneOrMany::Many(names)),
772 };
773
774 let cap_type = self.capability_type().unwrap();
775 if cap_type == "protocol" {
776 self.set_protocol(names);
777 } else if cap_type == "service" {
778 self.set_service(names);
779 } else if cap_type == "directory" {
780 self.set_directory(names);
781 } else if cap_type == "storage" {
782 self.set_storage(names);
783 } else if cap_type == "runner" {
784 self.set_runner(names);
785 } else if cap_type == "resolver" {
786 self.set_resolver(names);
787 } else if cap_type == "event_stream" {
788 self.set_event_stream(names);
789 } else if cap_type == "dictionary" {
790 self.set_dictionary(names);
791 } else if cap_type == "config" {
792 self.set_config(names);
793 } else {
794 panic!("Unknown capability type {}", cap_type);
795 }
796 }
797}
798
799trait Canonicalize {
800 fn canonicalize(&mut self);
801}
802
803pub trait AsClause {
804 fn r#as(&self) -> Option<&BorrowedName>;
805}
806
807pub trait AsClauseContext {
808 fn r#as(&self) -> Option<ContextSpanned<&BorrowedName>>;
809}
810
811pub trait PathClause {
812 fn path(&self) -> Option<&Path>;
813}
814
815pub trait FilterClause {
816 fn filter(&self) -> Option<&Map<String, Value>>;
817}
818
819pub fn alias_or_name<'a>(
820 alias: Option<&'a BorrowedName>,
821 name: &'a BorrowedName,
822) -> &'a BorrowedName {
823 alias.unwrap_or(name)
824}
825
826pub fn alias_or_name_context<'a>(
827 alias: Option<ContextSpanned<&'a BorrowedName>>,
828 name: &'a BorrowedName,
829 origin: Origin,
830) -> ContextSpanned<&'a BorrowedName> {
831 alias.unwrap_or(ContextSpanned { value: name, origin })
832}
833
834pub fn alias_or_path<'a>(alias: Option<&'a Path>, path: &'a Path) -> &'a Path {
835 alias.unwrap_or(path)
836}
837
838pub fn format_cml(buffer: &str, file: Option<&std::path::Path>) -> Result<Vec<u8>, Error> {
839 let general_order = PathOption::PropertyNameOrder(vec![
840 "name",
841 "url",
842 "startup",
843 "environment",
844 "config",
845 "dictionary",
846 "durability",
847 "service",
848 "protocol",
849 "directory",
850 "storage",
851 "runner",
852 "resolver",
853 "event",
854 "event_stream",
855 "from",
856 "as",
857 "to",
858 "rights",
859 "path",
860 "subdir",
861 "filter",
862 "dependency",
863 "extends",
864 "runners",
865 "resolvers",
866 "debug",
867 ]);
868 let options = FormatOptions {
869 collapse_containers_of_one: true,
870 sort_array_items: true, options_by_path: hashmap! {
872 "/*" => hashset! {
873 PathOption::PropertyNameOrder(vec![
874 "include",
875 "program",
876 "children",
877 "collections",
878 "capabilities",
879 "use",
880 "offer",
881 "expose",
882 "environments",
883 "facets",
884 ])
885 },
886 "/*/program" => hashset! {
887 PathOption::CollapseContainersOfOne(false),
888 PathOption::PropertyNameOrder(vec![
889 "runner",
890 "binary",
891 "args",
892 ]),
893 },
894 "/*/program/*" => hashset! {
895 PathOption::SortArrayItems(false),
896 },
897 "/*/*/*" => hashset! {
898 general_order.clone()
899 },
900 "/*/*/*/*/*" => hashset! {
901 general_order
902 },
903 },
904 ..Default::default()
905 };
906
907 json5format::format(buffer, file.map(|f| f.to_string_lossy().to_string()), Some(options))
908 .map_err(|e| Error::json5(e, file))
909}
910
911#[cfg(test)]
912mod tests {
913 use super::*;
914 use crate::types::document::Document;
915 use crate::types::environment::RunnerRegistration;
916 use crate::types::right::Right;
917 use assert_matches::assert_matches;
918 use difference::Changeset;
919 use fidl_fuchsia_io as fio;
920 use serde_json::{json, to_string_pretty, to_value};
921 use std::path;
922 use std::path::Path;
923 use test_case::test_case;
924
925 macro_rules! assert_json_eq {
926 ($a:expr, $e:expr) => {{
927 if $a != $e {
928 let expected = to_string_pretty(&$e).unwrap();
929 let actual = to_string_pretty(&$a).unwrap();
930 assert_eq!(
931 $a,
932 $e,
933 "JSON actual != expected. Diffs:\n\n{}",
934 Changeset::new(&actual, &expected, "\n")
935 );
936 }
937 }};
938 }
939
940 #[test]
944 fn test_parse_named_reference() {
945 assert_matches!("#some-child".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "some-child");
946 assert_matches!("#A".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "A");
947 assert_matches!("#7".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "7");
948 assert_matches!("#_".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "_");
949
950 assert_matches!("#-".parse::<OfferFromRef>(), Err(_));
951 assert_matches!("#.".parse::<OfferFromRef>(), Err(_));
952 assert_matches!("#".parse::<OfferFromRef>(), Err(_));
953 assert_matches!("some-child".parse::<OfferFromRef>(), Err(_));
954 }
955
956 #[test]
957 fn test_parse_reference_test() {
958 assert_matches!("parent".parse::<OfferFromRef>(), Ok(OfferFromRef::Parent));
959 assert_matches!("framework".parse::<OfferFromRef>(), Ok(OfferFromRef::Framework));
960 assert_matches!("self".parse::<OfferFromRef>(), Ok(OfferFromRef::Self_));
961 assert_matches!("#child".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "child");
962
963 assert_matches!("invalid".parse::<OfferFromRef>(), Err(_));
964 assert_matches!("#invalid-child^".parse::<OfferFromRef>(), Err(_));
965 }
966
967 fn json_value_from_str(json: &str, filename: &Path) -> Result<Value, Error> {
968 serde_json::from_str(json).map_err(|e| {
969 Error::parse(
970 format!("Couldn't read input as JSON: {}", e),
971 Some(Location { line: e.line(), column: e.column() }),
972 Some(filename),
973 )
974 })
975 }
976
977 fn parse_as_ref(input: &str) -> Result<OfferFromRef, Error> {
978 serde_json::from_value::<OfferFromRef>(json_value_from_str(input, &Path::new("test.cml"))?)
979 .map_err(|e| Error::parse(format!("{}", e), None, None))
980 }
981
982 #[test]
983 fn test_deserialize_ref() -> Result<(), Error> {
984 assert_matches!(parse_as_ref("\"self\""), Ok(OfferFromRef::Self_));
985 assert_matches!(parse_as_ref("\"parent\""), Ok(OfferFromRef::Parent));
986 assert_matches!(parse_as_ref("\"#child\""), Ok(OfferFromRef::Named(name)) if name == "child");
987
988 assert_matches!(parse_as_ref(r#""invalid""#), Err(_));
989
990 Ok(())
991 }
992
993 macro_rules! test_parse_rights {
994 (
995 $(
996 ($input:expr, $expected:expr),
997 )+
998 ) => {
999 #[test]
1000 fn parse_rights() {
1001 $(
1002 parse_rights_test($input, $expected);
1003 )+
1004 }
1005 }
1006 }
1007
1008 fn parse_rights_test(input: &str, expected: Right) {
1009 let r: Right = serde_json5::from_str(&format!("\"{}\"", input)).expect("invalid json");
1010 assert_eq!(r, expected);
1011 }
1012
1013 test_parse_rights! {
1014 ("connect", Right::Connect),
1015 ("enumerate", Right::Enumerate),
1016 ("execute", Right::Execute),
1017 ("get_attributes", Right::GetAttributes),
1018 ("modify_directory", Right::ModifyDirectory),
1019 ("read_bytes", Right::ReadBytes),
1020 ("traverse", Right::Traverse),
1021 ("update_attributes", Right::UpdateAttributes),
1022 ("write_bytes", Right::WriteBytes),
1023 ("r*", Right::ReadAlias),
1024 ("w*", Right::WriteAlias),
1025 ("x*", Right::ExecuteAlias),
1026 ("rw*", Right::ReadWriteAlias),
1027 ("rx*", Right::ReadExecuteAlias),
1028 }
1029
1030 macro_rules! test_expand_rights {
1031 (
1032 $(
1033 ($input:expr, $expected:expr),
1034 )+
1035 ) => {
1036 #[test]
1037 fn expand_rights() {
1038 $(
1039 expand_rights_test($input, $expected);
1040 )+
1041 }
1042 }
1043 }
1044
1045 fn expand_rights_test(input: Right, expected: Vec<fio::Operations>) {
1046 assert_eq!(input.expand(), expected);
1047 }
1048
1049 test_expand_rights! {
1050 (Right::Connect, vec![fio::Operations::CONNECT]),
1051 (Right::Enumerate, vec![fio::Operations::ENUMERATE]),
1052 (Right::Execute, vec![fio::Operations::EXECUTE]),
1053 (Right::GetAttributes, vec![fio::Operations::GET_ATTRIBUTES]),
1054 (Right::ModifyDirectory, vec![fio::Operations::MODIFY_DIRECTORY]),
1055 (Right::ReadBytes, vec![fio::Operations::READ_BYTES]),
1056 (Right::Traverse, vec![fio::Operations::TRAVERSE]),
1057 (Right::UpdateAttributes, vec![fio::Operations::UPDATE_ATTRIBUTES]),
1058 (Right::WriteBytes, vec![fio::Operations::WRITE_BYTES]),
1059 (Right::ReadAlias, vec![
1060 fio::Operations::CONNECT,
1061 fio::Operations::ENUMERATE,
1062 fio::Operations::TRAVERSE,
1063 fio::Operations::READ_BYTES,
1064 fio::Operations::GET_ATTRIBUTES,
1065 ]),
1066 (Right::WriteAlias, vec![
1067 fio::Operations::CONNECT,
1068 fio::Operations::ENUMERATE,
1069 fio::Operations::TRAVERSE,
1070 fio::Operations::WRITE_BYTES,
1071 fio::Operations::MODIFY_DIRECTORY,
1072 fio::Operations::UPDATE_ATTRIBUTES,
1073 ]),
1074 (Right::ExecuteAlias, vec![
1075 fio::Operations::CONNECT,
1076 fio::Operations::ENUMERATE,
1077 fio::Operations::TRAVERSE,
1078 fio::Operations::EXECUTE,
1079 ]),
1080 (Right::ReadWriteAlias, vec![
1081 fio::Operations::CONNECT,
1082 fio::Operations::ENUMERATE,
1083 fio::Operations::TRAVERSE,
1084 fio::Operations::READ_BYTES,
1085 fio::Operations::WRITE_BYTES,
1086 fio::Operations::MODIFY_DIRECTORY,
1087 fio::Operations::GET_ATTRIBUTES,
1088 fio::Operations::UPDATE_ATTRIBUTES,
1089 ]),
1090 (Right::ReadExecuteAlias, vec![
1091 fio::Operations::CONNECT,
1092 fio::Operations::ENUMERATE,
1093 fio::Operations::TRAVERSE,
1094 fio::Operations::READ_BYTES,
1095 fio::Operations::GET_ATTRIBUTES,
1096 fio::Operations::EXECUTE,
1097 ]),
1098 }
1099
1100 #[test]
1101 fn test_deny_unknown_fields() {
1102 assert_matches!(serde_json5::from_str::<Document>("{ unknown: \"\" }"), Err(_));
1103 assert_matches!(serde_json5::from_str::<Environment>("{ unknown: \"\" }"), Err(_));
1104 assert_matches!(serde_json5::from_str::<RunnerRegistration>("{ unknown: \"\" }"), Err(_));
1105 assert_matches!(serde_json5::from_str::<ResolverRegistration>("{ unknown: \"\" }"), Err(_));
1106 assert_matches!(serde_json5::from_str::<Use>("{ unknown: \"\" }"), Err(_));
1107 assert_matches!(serde_json5::from_str::<Expose>("{ unknown: \"\" }"), Err(_));
1108 assert_matches!(serde_json5::from_str::<Offer>("{ unknown: \"\" }"), Err(_));
1109 assert_matches!(serde_json5::from_str::<Capability>("{ unknown: \"\" }"), Err(_));
1110 assert_matches!(serde_json5::from_str::<Child>("{ unknown: \"\" }"), Err(_));
1111 assert_matches!(serde_json5::from_str::<Collection>("{ unknown: \"\" }"), Err(_));
1112 }
1113
1114 fn empty_offer() -> Offer {
1117 Offer {
1118 service: None,
1119 protocol: None,
1120 directory: None,
1121 storage: None,
1122 runner: None,
1123 resolver: None,
1124 dictionary: None,
1125 config: None,
1126 from: OneOrMany::One(OfferFromRef::Self_),
1127 to: OneOrMany::Many(vec![]),
1128 r#as: None,
1129 rights: None,
1130 subdir: None,
1131 dependency: None,
1132 event_stream: None,
1133 scope: None,
1134 availability: None,
1135 source_availability: None,
1136 target_availability: None,
1137 }
1138 }
1139
1140 fn empty_use() -> Use {
1141 Use {
1142 service: None,
1143 protocol: None,
1144 scope: None,
1145 directory: None,
1146 storage: None,
1147 config: None,
1148 dictionary: None,
1149 key: None,
1150 from: None,
1151 path: None,
1152 numbered_handle: None,
1153 rights: None,
1154 subdir: None,
1155 event_stream: None,
1156 runner: None,
1157 filter: None,
1158 dependency: None,
1159 availability: None,
1160 config_element_type: None,
1161 config_max_count: None,
1162 config_max_size: None,
1163 config_type: None,
1164 config_default: None,
1165 }
1166 }
1167
1168 #[test]
1169 fn test_capability_id() -> Result<(), Error> {
1170 let a: Name = "a".parse().unwrap();
1172 let b: Name = "b".parse().unwrap();
1173 assert_eq!(
1174 CapabilityId::from_offer_expose(&Offer {
1175 service: Some(OneOrMany::One(a.clone())),
1176 ..empty_offer()
1177 },)?,
1178 vec![CapabilityId::Service(&a)]
1179 );
1180 assert_eq!(
1181 CapabilityId::from_offer_expose(&Offer {
1182 service: Some(OneOrMany::Many(vec![a.clone(), b.clone()],)),
1183 ..empty_offer()
1184 },)?,
1185 vec![CapabilityId::Service(&a), CapabilityId::Service(&b)]
1186 );
1187 assert_eq!(
1188 CapabilityId::from_use(&Use {
1189 service: Some(OneOrMany::One(a.clone())),
1190 ..empty_use()
1191 },)?,
1192 vec![CapabilityId::UsedService("/svc/a".parse().unwrap())]
1193 );
1194 assert_eq!(
1195 CapabilityId::from_use(&Use {
1196 service: Some(OneOrMany::Many(vec![a.clone(), b.clone(),],)),
1197 ..empty_use()
1198 },)?,
1199 vec![
1200 CapabilityId::UsedService("/svc/a".parse().unwrap()),
1201 CapabilityId::UsedService("/svc/b".parse().unwrap())
1202 ]
1203 );
1204 assert_eq!(
1205 CapabilityId::from_use(&Use {
1206 event_stream: Some(OneOrMany::One(Name::new("test".to_string()).unwrap())),
1207 path: Some(cm_types::Path::new("/svc/myevent".to_string()).unwrap()),
1208 ..empty_use()
1209 },)?,
1210 vec![CapabilityId::UsedEventStream("/svc/myevent".parse().unwrap()),]
1211 );
1212 assert_eq!(
1213 CapabilityId::from_use(&Use {
1214 event_stream: Some(OneOrMany::One(Name::new("test".to_string()).unwrap())),
1215 ..empty_use()
1216 },)?,
1217 vec![CapabilityId::UsedEventStream(
1218 "/svc/fuchsia.component.EventStream".parse().unwrap()
1219 ),]
1220 );
1221 assert_eq!(
1222 CapabilityId::from_use(&Use {
1223 service: Some(OneOrMany::One(a.clone())),
1224 path: Some("/b".parse().unwrap()),
1225 ..empty_use()
1226 },)?,
1227 vec![CapabilityId::UsedService("/b".parse().unwrap())]
1228 );
1229
1230 assert_eq!(
1232 CapabilityId::from_offer_expose(&Offer {
1233 protocol: Some(OneOrMany::One(a.clone())),
1234 ..empty_offer()
1235 },)?,
1236 vec![CapabilityId::Protocol(&a)]
1237 );
1238 assert_eq!(
1239 CapabilityId::from_offer_expose(&Offer {
1240 protocol: Some(OneOrMany::Many(vec![a.clone(), b.clone()],)),
1241 ..empty_offer()
1242 },)?,
1243 vec![CapabilityId::Protocol(&a), CapabilityId::Protocol(&b)]
1244 );
1245 assert_eq!(
1246 CapabilityId::from_use(&Use {
1247 protocol: Some(OneOrMany::One(a.clone())),
1248 ..empty_use()
1249 },)?,
1250 vec![CapabilityId::UsedProtocol("/svc/a".parse().unwrap())]
1251 );
1252 assert_eq!(
1253 CapabilityId::from_use(&Use {
1254 protocol: Some(OneOrMany::Many(vec![a.clone(), b.clone(),],)),
1255 ..empty_use()
1256 },)?,
1257 vec![
1258 CapabilityId::UsedProtocol("/svc/a".parse().unwrap()),
1259 CapabilityId::UsedProtocol("/svc/b".parse().unwrap())
1260 ]
1261 );
1262 assert_eq!(
1263 CapabilityId::from_use(&Use {
1264 protocol: Some(OneOrMany::One(a.clone())),
1265 path: Some("/b".parse().unwrap()),
1266 ..empty_use()
1267 },)?,
1268 vec![CapabilityId::UsedProtocol("/b".parse().unwrap())]
1269 );
1270
1271 assert_eq!(
1273 CapabilityId::from_offer_expose(&Offer {
1274 directory: Some(OneOrMany::One(a.clone())),
1275 ..empty_offer()
1276 },)?,
1277 vec![CapabilityId::Directory(&a)]
1278 );
1279 assert_eq!(
1280 CapabilityId::from_offer_expose(&Offer {
1281 directory: Some(OneOrMany::Many(vec![a.clone(), b.clone()])),
1282 ..empty_offer()
1283 },)?,
1284 vec![CapabilityId::Directory(&a), CapabilityId::Directory(&b),]
1285 );
1286 assert_eq!(
1287 CapabilityId::from_use(&Use {
1288 directory: Some(a.clone()),
1289 path: Some("/b".parse().unwrap()),
1290 ..empty_use()
1291 },)?,
1292 vec![CapabilityId::UsedDirectory("/b".parse().unwrap())]
1293 );
1294
1295 assert_eq!(
1297 CapabilityId::from_offer_expose(&Offer {
1298 storage: Some(OneOrMany::One(a.clone())),
1299 ..empty_offer()
1300 },)?,
1301 vec![CapabilityId::Storage(&a)]
1302 );
1303 assert_eq!(
1304 CapabilityId::from_offer_expose(&Offer {
1305 storage: Some(OneOrMany::Many(vec![a.clone(), b.clone()])),
1306 ..empty_offer()
1307 },)?,
1308 vec![CapabilityId::Storage(&a), CapabilityId::Storage(&b),]
1309 );
1310 assert_eq!(
1311 CapabilityId::from_use(&Use {
1312 storage: Some(a.clone()),
1313 path: Some("/b".parse().unwrap()),
1314 ..empty_use()
1315 },)?,
1316 vec![CapabilityId::UsedStorage("/b".parse().unwrap())]
1317 );
1318
1319 assert_eq!(
1321 CapabilityId::from_use(&Use { runner: Some("elf".parse().unwrap()), ..empty_use() })?,
1322 vec![CapabilityId::UsedRunner(BorrowedName::new("elf").unwrap())]
1323 );
1324
1325 assert_eq!(
1327 CapabilityId::from_offer_expose(&Offer {
1328 dictionary: Some(OneOrMany::One(a.clone())),
1329 ..empty_offer()
1330 },)?,
1331 vec![CapabilityId::Dictionary(&a)]
1332 );
1333 assert_eq!(
1334 CapabilityId::from_offer_expose(&Offer {
1335 dictionary: Some(OneOrMany::Many(vec![a.clone(), b.clone()],)),
1336 ..empty_offer()
1337 },)?,
1338 vec![CapabilityId::Dictionary(&a), CapabilityId::Dictionary(&b)]
1339 );
1340 assert_eq!(
1341 CapabilityId::from_use(&Use {
1342 dictionary: Some(OneOrMany::One(a.clone())),
1343 ..empty_use()
1344 },)?,
1345 vec![CapabilityId::UsedDictionary("/svc/a".parse().unwrap())]
1346 );
1347 assert_eq!(
1348 CapabilityId::from_use(&Use {
1349 dictionary: Some(OneOrMany::Many(vec![a.clone(), b.clone(),],)),
1350 ..empty_use()
1351 },)?,
1352 vec![
1353 CapabilityId::UsedDictionary("/svc/a".parse().unwrap()),
1354 CapabilityId::UsedDictionary("/svc/b".parse().unwrap())
1355 ]
1356 );
1357 assert_eq!(
1358 CapabilityId::from_use(&Use {
1359 dictionary: Some(OneOrMany::One(a.clone())),
1360 path: Some("/b".parse().unwrap()),
1361 ..empty_use()
1362 },)?,
1363 vec![CapabilityId::UsedDictionary("/b".parse().unwrap())]
1364 );
1365
1366 assert_eq!(
1368 CapabilityId::from_offer_expose(&Offer {
1369 service: Some(OneOrMany::One(a.clone())),
1370 r#as: Some(b.clone()),
1371 ..empty_offer()
1372 },)?,
1373 vec![CapabilityId::Service(&b)]
1374 );
1375
1376 assert_matches!(CapabilityId::from_offer_expose(&empty_offer()), Err(_));
1378
1379 Ok(())
1380 }
1381
1382 fn document(contents: serde_json::Value) -> Document {
1383 serde_json5::from_str::<Document>(&contents.to_string()).unwrap()
1384 }
1385
1386 #[test]
1387 fn test_includes() {
1388 assert_eq!(document(json!({})).includes(), Vec::<String>::new());
1389 assert_eq!(document(json!({ "include": []})).includes(), Vec::<String>::new());
1390 assert_eq!(
1391 document(json!({ "include": [ "foo.cml", "bar.cml" ]})).includes(),
1392 vec!["foo.cml", "bar.cml"]
1393 );
1394 }
1395
1396 #[test]
1397 fn test_merge_same_section() {
1398 let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1399 let mut other = document(json!({ "use": [{ "protocol": "bar" }] }));
1400 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1401 let uses = some.r#use.as_ref().unwrap();
1402 assert_eq!(uses.len(), 2);
1403 assert_eq!(
1404 uses[0].protocol.as_ref().unwrap(),
1405 &OneOrMany::One("foo".parse::<Name>().unwrap())
1406 );
1407 assert_eq!(
1408 uses[1].protocol.as_ref().unwrap(),
1409 &OneOrMany::One("bar".parse::<Name>().unwrap())
1410 );
1411 }
1412
1413 #[test]
1414 fn test_merge_upgraded_availability() {
1415 let mut some =
1416 document(json!({ "use": [{ "protocol": "foo", "availability": "optional" }] }));
1417 let mut other1 = document(json!({ "use": [{ "protocol": "foo" }] }));
1418 let mut other2 =
1419 document(json!({ "use": [{ "protocol": "foo", "availability": "transitional" }] }));
1420 let mut other3 =
1421 document(json!({ "use": [{ "protocol": "foo", "availability": "same_as_target" }] }));
1422 some.merge_from(&mut other1, &Path::new("some/path")).unwrap();
1423 some.merge_from(&mut other2, &Path::new("some/path")).unwrap();
1424 some.merge_from(&mut other3, &Path::new("some/path")).unwrap();
1425 let uses = some.r#use.as_ref().unwrap();
1426 assert_eq!(uses.len(), 2);
1427 assert_eq!(
1428 uses[0].protocol.as_ref().unwrap(),
1429 &OneOrMany::One("foo".parse::<Name>().unwrap())
1430 );
1431 assert!(uses[0].availability.is_none());
1432 assert_eq!(
1433 uses[1].protocol.as_ref().unwrap(),
1434 &OneOrMany::One("foo".parse::<Name>().unwrap())
1435 );
1436 assert_eq!(uses[1].availability.as_ref().unwrap(), &Availability::SameAsTarget,);
1437 }
1438
1439 #[test]
1440 fn test_merge_different_sections() {
1441 let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1442 let mut other = document(json!({ "expose": [{ "protocol": "bar", "from": "self" }] }));
1443 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1444 let uses = some.r#use.as_ref().unwrap();
1445 let exposes = some.expose.as_ref().unwrap();
1446 assert_eq!(uses.len(), 1);
1447 assert_eq!(exposes.len(), 1);
1448 assert_eq!(
1449 uses[0].protocol.as_ref().unwrap(),
1450 &OneOrMany::One("foo".parse::<Name>().unwrap())
1451 );
1452 assert_eq!(
1453 exposes[0].protocol.as_ref().unwrap(),
1454 &OneOrMany::One("bar".parse::<Name>().unwrap())
1455 );
1456 }
1457
1458 #[test]
1459 fn test_merge_environments() {
1460 let mut some = document(json!({ "environments": [
1461 {
1462 "name": "one",
1463 "extends": "realm",
1464 },
1465 {
1466 "name": "two",
1467 "extends": "none",
1468 "runners": [
1469 {
1470 "runner": "r1",
1471 "from": "#c1",
1472 },
1473 {
1474 "runner": "r2",
1475 "from": "#c2",
1476 },
1477 ],
1478 "resolvers": [
1479 {
1480 "resolver": "res1",
1481 "from": "#c1",
1482 "scheme": "foo",
1483 },
1484 ],
1485 "debug": [
1486 {
1487 "protocol": "baz",
1488 "from": "#c2"
1489 }
1490 ]
1491 },
1492 ]}));
1493 let mut other = document(json!({ "environments": [
1494 {
1495 "name": "two",
1496 "__stop_timeout_ms": 100,
1497 "runners": [
1498 {
1499 "runner": "r3",
1500 "from": "#c3",
1501 },
1502 ],
1503 "resolvers": [
1504 {
1505 "resolver": "res2",
1506 "from": "#c1",
1507 "scheme": "bar",
1508 },
1509 ],
1510 "debug": [
1511 {
1512 "protocol": "faz",
1513 "from": "#c2"
1514 }
1515 ]
1516 },
1517 {
1518 "name": "three",
1519 "__stop_timeout_ms": 1000,
1520 },
1521 ]}));
1522 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1523 assert_eq!(
1524 to_value(some).unwrap(),
1525 json!({"environments": [
1526 {
1527 "name": "one",
1528 "extends": "realm",
1529 },
1530 {
1531 "name": "three",
1532 "__stop_timeout_ms": 1000,
1533 },
1534 {
1535 "name": "two",
1536 "extends": "none",
1537 "__stop_timeout_ms": 100,
1538 "runners": [
1539 {
1540 "runner": "r1",
1541 "from": "#c1",
1542 },
1543 {
1544 "runner": "r2",
1545 "from": "#c2",
1546 },
1547 {
1548 "runner": "r3",
1549 "from": "#c3",
1550 },
1551 ],
1552 "resolvers": [
1553 {
1554 "resolver": "res1",
1555 "from": "#c1",
1556 "scheme": "foo",
1557 },
1558 {
1559 "resolver": "res2",
1560 "from": "#c1",
1561 "scheme": "bar",
1562 },
1563 ],
1564 "debug": [
1565 {
1566 "protocol": "baz",
1567 "from": "#c2"
1568 },
1569 {
1570 "protocol": "faz",
1571 "from": "#c2"
1572 }
1573 ]
1574 },
1575 ]})
1576 );
1577 }
1578
1579 #[test]
1580 fn test_merge_environments_errors() {
1581 {
1582 let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
1583 let mut other = document(json!({"environments": [{"name": "one", "extends": "none"}]}));
1584 assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
1585 }
1586 {
1587 let mut some =
1588 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
1589 let mut other =
1590 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 20}]}));
1591 assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
1592 }
1593
1594 {
1596 let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
1597 let mut other =
1598 document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
1599 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1600 assert_eq!(
1601 to_value(some).unwrap(),
1602 json!({"environments": [{"name": "one", "extends": "realm"}]})
1603 );
1604 }
1605 {
1606 let mut some =
1607 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
1608 let mut other =
1609 document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
1610 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1611 assert_eq!(
1612 to_value(some).unwrap(),
1613 json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]})
1614 );
1615 }
1616 }
1617
1618 #[test]
1619 fn test_merge_from_other_config() {
1620 let mut some = document(json!({}));
1621 let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
1622
1623 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1624 let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
1625 assert_eq!(some.config, expected.config);
1626 }
1627
1628 #[test]
1629 fn test_merge_from_some_config() {
1630 let mut some = document(json!({ "config": { "bar": { "type": "bool" } } }));
1631 let mut other = document(json!({}));
1632
1633 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1634 let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
1635 assert_eq!(some.config, expected.config);
1636 }
1637
1638 #[test]
1639 fn test_merge_from_config() {
1640 let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
1641 let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
1642 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1643
1644 assert_eq!(
1645 some,
1646 document(json!({
1647 "config": {
1648 "foo": { "type": "bool" },
1649 "bar": { "type": "bool" },
1650 }
1651 })),
1652 );
1653 }
1654
1655 #[test]
1656 fn test_merge_from_config_dedupe_identical_fields() {
1657 let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
1658 let mut other = document(json!({ "config": { "foo": { "type": "bool" } } }));
1659 some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1660
1661 assert_eq!(some, document(json!({ "config": { "foo": { "type": "bool" } } })));
1662 }
1663
1664 #[test]
1665 fn test_merge_from_config_conflicting_keys() {
1666 let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
1667 let mut other = document(json!({ "config": { "foo": { "type": "uint8" } } }));
1668
1669 assert_matches::assert_matches!(
1670 some.merge_from(&mut other, &path::Path::new("some/path")),
1671 Err(Error::Validate { err, .. })
1672 if err == "Found conflicting entry for config key `foo` in `some/path`."
1673 );
1674 }
1675
1676 #[test]
1677 fn test_canonicalize() {
1678 let mut some = document(json!({
1679 "children": [
1680 { "name": "b_child", "url": "http://foo/b" },
1682 { "name": "a_child", "url": "http://foo/a" },
1683 ],
1684 "environments": [
1685 { "name": "b_env" },
1687 { "name": "a_env" },
1688 ],
1689 "collections": [
1690 { "name": "b_coll", "durability": "transient" },
1692 { "name": "a_coll", "durability": "transient" },
1693 ],
1694 "capabilities": [
1697 { "protocol": ["foo"] },
1699 { "protocol": "bar" },
1700 { "protocol": "arg", "path": "/arg" },
1702 { "service": ["b", "a"] },
1704 { "event_stream": ["b", "a"] },
1706 { "runner": "myrunner" },
1707 { "runner": "mypathrunner1", "path": "/foo" },
1709 { "runner": "mypathrunner2", "path": "/foo" },
1710 ],
1711 "offer": [
1713 { "protocol": "baz", "from": "#a_child", "to": "#c_child" },
1715 { "protocol": ["foo"], "from": "#a_child", "to": "#b_child" },
1717 { "protocol": "bar", "from": "#a_child", "to": "#b_child" },
1718 { "service": ["b", "a"], "from": "#a_child", "to": "#b_child" },
1720 {
1722 "event_stream": ["b", "a"],
1723 "from": "#a_child",
1724 "to": "#b_child",
1725 "scope": ["#b", "#c", "#a"] },
1727 { "runner": [ "myrunner", "a" ], "from": "#a_child", "to": "#b_child" },
1728 { "runner": [ "b" ], "from": "#a_child", "to": "#b_child" },
1729 { "directory": [ "b" ], "from": "#a_child", "to": "#b_child" },
1730 ],
1731 "expose": [
1732 { "protocol": ["foo"], "from": "#a_child" },
1733 { "protocol": "bar", "from": "#a_child" }, { "service": ["b", "a"], "from": "#a_child" },
1736 {
1738 "event_stream": ["b", "a"],
1739 "from": "#a_child",
1740 "scope": ["#b", "#c", "#a"] },
1742 { "runner": [ "myrunner", "a" ], "from": "#a_child" },
1743 { "runner": [ "b" ], "from": "#a_child" },
1744 { "directory": [ "b" ], "from": "#a_child" },
1745 ],
1746 "use": [
1747 { "protocol": ["zazzle"], "path": "/zazbaz" },
1749 { "protocol": ["foo"] },
1751 { "protocol": "bar" },
1752 { "service": ["b", "a"] },
1754 { "event_stream": ["b", "a"], "scope": ["#b", "#a"] },
1756 ],
1757 }));
1758 some.canonicalize();
1759
1760 assert_json_eq!(
1761 some,
1762 document(json!({
1763 "children": [
1764 { "name": "a_child", "url": "http://foo/a" },
1765 { "name": "b_child", "url": "http://foo/b" },
1766 ],
1767 "collections": [
1768 { "name": "a_coll", "durability": "transient" },
1769 { "name": "b_coll", "durability": "transient" },
1770 ],
1771 "environments": [
1772 { "name": "a_env" },
1773 { "name": "b_env" },
1774 ],
1775 "capabilities": [
1776 { "event_stream": ["a", "b"] },
1777 { "protocol": "arg", "path": "/arg" },
1778 { "protocol": ["bar", "foo"] },
1779 { "runner": "mypathrunner1", "path": "/foo" },
1780 { "runner": "mypathrunner2", "path": "/foo" },
1781 { "runner": "myrunner" },
1782 { "service": ["a", "b"] },
1783 ],
1784 "use": [
1785 { "event_stream": ["a", "b"], "scope": ["#a", "#b"] },
1786 { "protocol": ["bar", "foo"] },
1787 { "protocol": "zazzle", "path": "/zazbaz" },
1788 { "service": ["a", "b"] },
1789 ],
1790 "offer": [
1791 { "directory": "b", "from": "#a_child", "to": "#b_child" },
1792 {
1793 "event_stream": ["a", "b"],
1794 "from": "#a_child",
1795 "to": "#b_child",
1796 "scope": ["#a", "#b", "#c"],
1797 },
1798 { "protocol": ["bar", "foo"], "from": "#a_child", "to": "#b_child" },
1799 { "protocol": "baz", "from": "#a_child", "to": "#c_child" },
1800 { "runner": [ "a", "b", "myrunner" ], "from": "#a_child", "to": "#b_child" },
1801 { "service": ["a", "b"], "from": "#a_child", "to": "#b_child" },
1802 ],
1803 "expose": [
1804 { "directory": "b", "from": "#a_child" },
1805 {
1806 "event_stream": ["a", "b"],
1807 "from": "#a_child",
1808 "scope": ["#a", "#b", "#c"],
1809 },
1810 { "protocol": ["bar", "foo"], "from": "#a_child" },
1811 { "runner": [ "a", "b", "myrunner" ], "from": "#a_child" },
1812 { "service": ["a", "b"], "from": "#a_child" },
1813 ],
1814 }))
1815 )
1816 }
1817
1818 #[test]
1819 fn deny_unknown_config_type_fields() {
1820 let input = json!({ "config": { "foo": { "type": "bool", "unknown": "should error" } } });
1821 serde_json5::from_str::<Document>(&input.to_string())
1822 .expect_err("must reject unknown config field attributes");
1823 }
1824
1825 #[test]
1826 fn deny_unknown_config_nested_type_fields() {
1827 let input = json!({
1828 "config": {
1829 "foo": {
1830 "type": "vector",
1831 "max_count": 10,
1832 "element": {
1833 "type": "bool",
1834 "unknown": "should error"
1835 },
1836
1837 }
1838 }
1839 });
1840 serde_json5::from_str::<Document>(&input.to_string())
1841 .expect_err("must reject unknown config field attributes");
1842 }
1843
1844 #[test]
1845 fn test_merge_from_program() {
1846 let mut some = document(json!({ "program": { "binary": "bin/hello_world" } }));
1847 let mut other = document(json!({ "program": { "runner": "elf" } }));
1848 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1849 let expected =
1850 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1851 assert_eq!(some.program, expected.program);
1852 }
1853
1854 #[test]
1855 fn test_merge_from_program_without_runner() {
1856 let mut some =
1857 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1858 let mut other = document(json!({ "program": {} }));
1861 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1862 let expected =
1863 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1864 assert_eq!(some.program, expected.program);
1865 }
1866
1867 #[test]
1868 fn test_merge_from_program_overlapping_environ() {
1869 let mut some = document(json!({ "program": { "environ": ["1"] } }));
1871 let mut other = document(json!({ "program": { "environ": ["2"] } }));
1872 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1873 let expected = document(json!({ "program": { "environ": ["1", "2"] } }));
1874 assert_eq!(some.program, expected.program);
1875 }
1876
1877 #[test]
1878 fn test_merge_from_program_overlapping_runner() {
1879 let mut some =
1881 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1882 let mut other = document(json!({ "program": { "runner": "elf" } }));
1883 some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1884 let expected =
1885 document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1886 assert_eq!(some.program, expected.program);
1887 }
1888
1889 #[test_case(
1890 document(json!({ "program": { "runner": "elf" } })),
1891 document(json!({ "program": { "runner": "fle" } })),
1892 "runner"
1893 ; "when_runner_conflicts"
1894 )]
1895 #[test_case(
1896 document(json!({ "program": { "binary": "bin/hello_world" } })),
1897 document(json!({ "program": { "binary": "bin/hola_mundo" } })),
1898 "binary"
1899 ; "when_binary_conflicts"
1900 )]
1901 #[test_case(
1902 document(json!({ "program": { "args": ["a".to_owned()] } })),
1903 document(json!({ "program": { "args": ["b".to_owned()] } })),
1904 "args"
1905 ; "when_args_conflicts"
1906 )]
1907 fn test_merge_from_program_error(mut some: Document, mut other: Document, field: &str) {
1908 assert_matches::assert_matches!(
1909 some.merge_from(&mut other, &path::Path::new("some/path")),
1910 Err(Error::Validate { err, .. })
1911 if err == format!("manifest include had a conflicting `program.{}`: some/path", field)
1912 );
1913 }
1914
1915 #[test_case(
1916 document(json!({ "facets": { "my.key": "my.value" } })),
1917 document(json!({ "facets": { "other.key": "other.value" } })),
1918 document(json!({ "facets": { "my.key": "my.value", "other.key": "other.value" } }))
1919 ; "two separate keys"
1920 )]
1921 #[test_case(
1922 document(json!({ "facets": { "my.key": "my.value" } })),
1923 document(json!({ "facets": {} })),
1924 document(json!({ "facets": { "my.key": "my.value" } }))
1925 ; "empty other facet"
1926 )]
1927 #[test_case(
1928 document(json!({ "facets": {} })),
1929 document(json!({ "facets": { "other.key": "other.value" } })),
1930 document(json!({ "facets": { "other.key": "other.value" } }))
1931 ; "empty my facet"
1932 )]
1933 #[test_case(
1934 document(json!({ "facets": { "key": { "type": "some_type" } } })),
1935 document(json!({ "facets": { "key": { "runner": "some_runner"} } })),
1936 document(json!({ "facets": { "key": { "type": "some_type", "runner": "some_runner" } } }))
1937 ; "nested facet key"
1938 )]
1939 #[test_case(
1940 document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "type": "new type" }}}})),
1941 document(json!({ "facets": { "key": { "nested_key": { "runner": "some_runner" }} } })),
1942 document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "runner": "some_runner", "type": "new type" }}}}))
1943 ; "double nested facet key"
1944 )]
1945 #[test_case(
1946 document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2"] } } })),
1947 document(json!({ "facets": { "key": { "array_key": ["value_3", "value_4"] } } })),
1948 document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2", "value_3", "value_4"] } } }))
1949 ; "merge array values"
1950 )]
1951 fn test_merge_from_facets(mut my: Document, mut other: Document, expected: Document) {
1952 my.merge_from(&mut other, &Path::new("some/path")).unwrap();
1953 assert_eq!(my.facets, expected.facets);
1954 }
1955
1956 #[test_case(
1957 document(json!({ "facets": { "key": "my.value" }})),
1958 document(json!({ "facets": { "key": "other.value" }})),
1959 "facets.key"
1960 ; "conflict first level keys"
1961 )]
1962 #[test_case(
1963 document(json!({ "facets": { "key": {"type": "cts" }}})),
1964 document(json!({ "facets": { "key": {"type": "system" }}})),
1965 "facets.key.type"
1966 ; "conflict second level keys"
1967 )]
1968 #[test_case(
1969 document(json!({ "facets": { "key": {"type": {"key": "value" }}}})),
1970 document(json!({ "facets": { "key": {"type": "system" }}})),
1971 "facets.key.type"
1972 ; "incompatible self nested type"
1973 )]
1974 #[test_case(
1975 document(json!({ "facets": { "key": {"type": "system" }}})),
1976 document(json!({ "facets": { "key": {"type": {"key": "value" }}}})),
1977 "facets.key.type"
1978 ; "incompatible other nested type"
1979 )]
1980 #[test_case(
1981 document(json!({ "facets": { "key": {"type": {"key": "my.value" }}}})),
1982 document(json!({ "facets": { "key": {"type": {"key": "some.value" }}}})),
1983 "facets.key.type.key"
1984 ; "conflict third level keys"
1985 )]
1986 #[test_case(
1987 document(json!({ "facets": { "key": {"type": [ "value_1" ]}}})),
1988 document(json!({ "facets": { "key": {"type": "value_2" }}})),
1989 "facets.key.type"
1990 ; "incompatible keys"
1991 )]
1992 fn test_merge_from_facet_error(mut my: Document, mut other: Document, field: &str) {
1993 assert_matches::assert_matches!(
1994 my.merge_from(&mut other, &path::Path::new("some/path")),
1995 Err(Error::Validate { err, .. })
1996 if err == format!("manifest include had a conflicting `{}`: some/path", field)
1997 );
1998 }
1999
2000 #[test_case("protocol")]
2001 #[test_case("service")]
2002 #[test_case("event_stream")]
2003 fn test_merge_from_duplicate_use_array(typename: &str) {
2004 let mut my = document(json!({ "use": [{ typename: "a" }]}));
2005 let mut other = document(json!({ "use": [
2006 { typename: ["a", "b"], "availability": "optional"}
2007 ]}));
2008 let result = document(json!({ "use": [
2009 { typename: "a" },
2010 { typename: "b", "availability": "optional" },
2011 ]}));
2012
2013 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2014 assert_eq!(my, result);
2015 }
2016
2017 #[test_case("directory")]
2018 #[test_case("storage")]
2019 fn test_merge_from_duplicate_use_noarray(typename: &str) {
2020 let mut my = document(json!({ "use": [{ typename: "a", "path": "/a"}]}));
2021 let mut other = document(json!({ "use": [
2022 { typename: "a", "path": "/a", "availability": "optional" },
2023 { typename: "b", "path": "/b", "availability": "optional" },
2024 ]}));
2025 let result = document(json!({ "use": [
2026 { typename: "a", "path": "/a" },
2027 { typename: "b", "path": "/b", "availability": "optional" },
2028 ]}));
2029 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2030 assert_eq!(my, result);
2031 }
2032
2033 #[test_case("protocol")]
2034 #[test_case("service")]
2035 #[test_case("event_stream")]
2036 fn test_merge_from_duplicate_capabilities_array(typename: &str) {
2037 let mut my = document(json!({ "capabilities": [{ typename: "a" }]}));
2038 let mut other = document(json!({ "capabilities": [ { typename: ["a", "b"] } ]}));
2039 let result = document(json!({ "capabilities": [ { typename: "a" }, { typename: "b" } ]}));
2040
2041 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2042 assert_eq!(my, result);
2043 }
2044
2045 #[test_case("directory")]
2046 #[test_case("storage")]
2047 #[test_case("runner")]
2048 #[test_case("resolver")]
2049 fn test_merge_from_duplicate_capabilities_noarray(typename: &str) {
2050 let mut my = document(json!({ "capabilities": [{ typename: "a", "path": "/a"}]}));
2051 let mut other = document(json!({ "capabilities": [
2052 { typename: "a", "path": "/a" },
2053 { typename: "b", "path": "/b" },
2054 ]}));
2055 let result = document(json!({ "capabilities": [
2056 { typename: "a", "path": "/a" },
2057 { typename: "b", "path": "/b" },
2058 ]}));
2059 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2060 assert_eq!(my, result);
2061 }
2062
2063 #[test]
2064 fn test_merge_with_empty_names() {
2065 let mut my = document(json!({ "capabilities": [{ "path": "/a"}]}));
2067
2068 let mut other = document(json!({ "capabilities": [
2069 { "directory": "a", "path": "/a" },
2070 { "directory": "b", "path": "/b" },
2071 ]}));
2072 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap_err();
2073 }
2074
2075 #[test_case("protocol")]
2076 #[test_case("service")]
2077 #[test_case("event_stream")]
2078 #[test_case("directory")]
2079 #[test_case("storage")]
2080 #[test_case("runner")]
2081 #[test_case("resolver")]
2082 fn test_merge_from_duplicate_offers(typename: &str) {
2083 let mut my = document(json!({ "offer": [{ typename: "a", "from": "self", "to": "#c" }]}));
2084 let mut other = document(json!({ "offer": [
2085 { typename: ["a", "b"], "from": "self", "to": "#c", "availability": "optional" }
2086 ]}));
2087 let result = document(json!({ "offer": [
2088 { typename: "a", "from": "self", "to": "#c" },
2089 { typename: "b", "from": "self", "to": "#c", "availability": "optional" },
2090 ]}));
2091
2092 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2093 assert_eq!(my, result);
2094 }
2095
2096 #[test_case("protocol")]
2097 #[test_case("service")]
2098 #[test_case("event_stream")]
2099 #[test_case("directory")]
2100 #[test_case("runner")]
2101 #[test_case("resolver")]
2102 fn test_merge_from_duplicate_exposes(typename: &str) {
2103 let mut my = document(json!({ "expose": [{ typename: "a", "from": "self" }]}));
2104 let mut other = document(json!({ "expose": [
2105 { typename: ["a", "b"], "from": "self" }
2106 ]}));
2107 let result = document(json!({ "expose": [
2108 { typename: "a", "from": "self" },
2109 { typename: "b", "from": "self" },
2110 ]}));
2111
2112 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2113 assert_eq!(my, result);
2114 }
2115
2116 #[test_case(
2117 document(json!({ "use": [
2118 { "protocol": "a", "availability": "required" },
2119 { "protocol": "b", "availability": "optional" },
2120 { "protocol": "c", "availability": "transitional" },
2121 { "protocol": "d", "availability": "same_as_target" },
2122 ]})),
2123 document(json!({ "use": [
2124 { "protocol": ["a"], "availability": "required" },
2125 { "protocol": ["b"], "availability": "optional" },
2126 { "protocol": ["c"], "availability": "transitional" },
2127 { "protocol": ["d"], "availability": "same_as_target" },
2128 ]})),
2129 document(json!({ "use": [
2130 { "protocol": "a", "availability": "required" },
2131 { "protocol": "b", "availability": "optional" },
2132 { "protocol": "c", "availability": "transitional" },
2133 { "protocol": "d", "availability": "same_as_target" },
2134 ]}))
2135 ; "merge both same"
2136 )]
2137 #[test_case(
2138 document(json!({ "use": [
2139 { "protocol": "a", "availability": "optional" },
2140 { "protocol": "b", "availability": "transitional" },
2141 { "protocol": "c", "availability": "transitional" },
2142 ]})),
2143 document(json!({ "use": [
2144 { "protocol": ["a", "x"], "availability": "required" },
2145 { "protocol": ["b", "y"], "availability": "optional" },
2146 { "protocol": ["c", "z"], "availability": "required" },
2147 ]})),
2148 document(json!({ "use": [
2149 { "protocol": ["a", "x"], "availability": "required" },
2150 { "protocol": ["b", "y"], "availability": "optional" },
2151 { "protocol": ["c", "z"], "availability": "required" },
2152 ]}))
2153 ; "merge with upgrade"
2154 )]
2155 #[test_case(
2156 document(json!({ "use": [
2157 { "protocol": "a", "availability": "required" },
2158 { "protocol": "b", "availability": "optional" },
2159 { "protocol": "c", "availability": "required" },
2160 ]})),
2161 document(json!({ "use": [
2162 { "protocol": ["a", "x"], "availability": "optional" },
2163 { "protocol": ["b", "y"], "availability": "transitional" },
2164 { "protocol": ["c", "z"], "availability": "transitional" },
2165 ]})),
2166 document(json!({ "use": [
2167 { "protocol": "a", "availability": "required" },
2168 { "protocol": "b", "availability": "optional" },
2169 { "protocol": "c", "availability": "required" },
2170 { "protocol": "x", "availability": "optional" },
2171 { "protocol": "y", "availability": "transitional" },
2172 { "protocol": "z", "availability": "transitional" },
2173 ]}))
2174 ; "merge with downgrade"
2175 )]
2176 #[test_case(
2177 document(json!({ "use": [
2178 { "protocol": "a", "availability": "optional" },
2179 { "protocol": "b", "availability": "transitional" },
2180 { "protocol": "c", "availability": "transitional" },
2181 ]})),
2182 document(json!({ "use": [
2183 { "protocol": ["a", "x"], "availability": "same_as_target" },
2184 { "protocol": ["b", "y"], "availability": "same_as_target" },
2185 { "protocol": ["c", "z"], "availability": "same_as_target" },
2186 ]})),
2187 document(json!({ "use": [
2188 { "protocol": "a", "availability": "optional" },
2189 { "protocol": "b", "availability": "transitional" },
2190 { "protocol": "c", "availability": "transitional" },
2191 { "protocol": ["a", "x"], "availability": "same_as_target" },
2192 { "protocol": ["b", "y"], "availability": "same_as_target" },
2193 { "protocol": ["c", "z"], "availability": "same_as_target" },
2194 ]}))
2195 ; "merge with no replacement"
2196 )]
2197 #[test_case(
2198 document(json!({ "use": [
2199 { "protocol": ["a", "b", "c"], "availability": "optional" },
2200 { "protocol": "d", "availability": "same_as_target" },
2201 { "protocol": ["e", "f"] },
2202 ]})),
2203 document(json!({ "use": [
2204 { "protocol": ["c", "e", "g"] },
2205 { "protocol": ["d", "h"] },
2206 { "protocol": ["f", "i"], "availability": "transitional" },
2207 ]})),
2208 document(json!({ "use": [
2209 { "protocol": ["a", "b"], "availability": "optional" },
2210 { "protocol": "d", "availability": "same_as_target" },
2211 { "protocol": ["e", "f"] },
2212 { "protocol": ["c", "g"] },
2213 { "protocol": ["d", "h"] },
2214 { "protocol": "i", "availability": "transitional" },
2215 ]}))
2216 ; "merge multiple"
2217 )]
2218
2219 fn test_merge_from_duplicate_capability_availability(
2220 mut my: Document,
2221 mut other: Document,
2222 result: Document,
2223 ) {
2224 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2225 assert_eq!(my, result);
2226 }
2227
2228 #[test_case(
2229 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2230 document(json!({ "use": [{ "protocol": ["c", "d"] }]})),
2231 document(json!({ "use": [
2232 { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
2233 ]}))
2234 ; "merge capabilities with disjoint sets"
2235 )]
2236 #[test_case(
2237 document(json!({ "use": [
2238 { "protocol": ["a"] },
2239 { "protocol": "b" },
2240 ]})),
2241 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2242 document(json!({ "use": [
2243 { "protocol": ["a"] }, { "protocol": "b" },
2244 ]}))
2245 ; "merge capabilities with equal set"
2246 )]
2247 #[test_case(
2248 document(json!({ "use": [
2249 { "protocol": ["a", "b"] },
2250 { "protocol": "c" },
2251 ]})),
2252 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2253 document(json!({ "use": [
2254 { "protocol": ["a", "b"] }, { "protocol": "c" },
2255 ]}))
2256 ; "merge capabilities with subset"
2257 )]
2258 #[test_case(
2259 document(json!({ "use": [
2260 { "protocol": ["a", "b"] },
2261 ]})),
2262 document(json!({ "use": [{ "protocol": ["a", "b", "c"] }]})),
2263 document(json!({ "use": [
2264 { "protocol": ["a", "b"] },
2265 { "protocol": "c" },
2266 ]}))
2267 ; "merge capabilities with superset"
2268 )]
2269 #[test_case(
2270 document(json!({ "use": [
2271 { "protocol": ["a", "b"] },
2272 ]})),
2273 document(json!({ "use": [{ "protocol": ["b", "c", "d"] }]})),
2274 document(json!({ "use": [
2275 { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
2276 ]}))
2277 ; "merge capabilities with intersection"
2278 )]
2279 #[test_case(
2280 document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2281 document(json!({ "use": [
2282 { "protocol": ["c", "b", "d"] },
2283 { "protocol": ["e", "d"] },
2284 ]})),
2285 document(json!({ "use": [
2286 {"protocol": ["a", "b"] },
2287 {"protocol": ["c", "d"] },
2288 {"protocol": "e" }]}))
2289 ; "merge capabilities from multiple arrays"
2290 )]
2291 #[test_case(
2292 document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
2293 document(json!({ "use": [{ "service": "foo.bar.Baz", "from": "self"}]})),
2294 document(json!({ "use": [
2295 {"protocol": "foo.bar.Baz", "from": "self"},
2296 {"service": "foo.bar.Baz", "from": "self"}]}))
2297 ; "merge capabilities, types don't match"
2298 )]
2299 #[test_case(
2300 document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
2301 document(json!({ "use": [{ "protocol": "foo.bar.Baz" }]})),
2302 document(json!({ "use": [
2303 {"protocol": "foo.bar.Baz", "from": "self"},
2304 {"protocol": "foo.bar.Baz"}]}))
2305 ; "merge capabilities, fields don't match"
2306 )]
2307
2308 fn test_merge_from_duplicate_capability(
2309 mut my: Document,
2310 mut other: Document,
2311 result: Document,
2312 ) {
2313 my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2314 assert_eq!(my, result);
2315 }
2316
2317 #[test_case(&Right::Connect; "connect right")]
2318 #[test_case(&Right::Enumerate; "enumerate right")]
2319 #[test_case(&Right::Execute; "execute right")]
2320 #[test_case(&Right::GetAttributes; "getattr right")]
2321 #[test_case(&Right::ModifyDirectory; "modifydir right")]
2322 #[test_case(&Right::ReadBytes; "readbytes right")]
2323 #[test_case(&Right::Traverse; "traverse right")]
2324 #[test_case(&Right::UpdateAttributes; "updateattrs right")]
2325 #[test_case(&Right::WriteBytes; "writebytes right")]
2326 #[test_case(&Right::ReadAlias; "r right")]
2327 #[test_case(&Right::WriteAlias; "w right")]
2328 #[test_case(&Right::ExecuteAlias; "x right")]
2329 #[test_case(&Right::ReadWriteAlias; "rw right")]
2330 #[test_case(&Right::ReadExecuteAlias; "rx right")]
2331 #[test_case(&OfferFromRef::Self_; "offer from self")]
2332 #[test_case(&OfferFromRef::Parent; "offer from parent")]
2333 #[test_case(&OfferFromRef::Named(Name::new("child".to_string()).unwrap()); "offer from named")]
2334 #[test_case(
2335 &document(json!({}));
2336 "empty document"
2337 )]
2338 #[test_case(
2339 &document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
2340 "use one from self"
2341 )]
2342 #[test_case(
2343 &document(json!({ "use": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
2344 "use multiple from self"
2345 )]
2346 #[test_case(
2347 &document(json!({
2348 "offer": [{ "protocol": "foo.bar.Baz", "from": "self", "to": "#elements"}],
2349 "collections" :[{"name": "elements", "durability": "transient" }]
2350 }));
2351 "offer from self to collection"
2352 )]
2353 #[test_case(
2354 &document(json!({
2355 "offer": [
2356 { "service": "foo.bar.Baz", "from": "self", "to": "#elements" },
2357 { "service": "some.other.Service", "from": "self", "to": "#elements"},
2358 ],
2359 "collections":[ {"name": "elements", "durability": "transient"} ]}));
2360 "service offers"
2361 )]
2362 #[test_case(
2363 &document(json!({ "expose": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
2364 "expose protocols from self"
2365 )]
2366 #[test_case(
2367 &document(json!({ "expose": [{ "service": ["foo.bar.Baz", "some.other.Service"], "from": "self"}]}));
2368 "expose service from self"
2369 )]
2370 #[test_case(
2371 &document(json!({ "capabilities": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
2372 "capabilities from self"
2373 )]
2374 #[test_case(
2375 &document(json!({ "facets": { "my.key": "my.value" } }));
2376 "facets"
2377 )]
2378 #[test_case(
2379 &document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2380 "elf runner program"
2381 )]
2382 fn serialize_roundtrips<T>(val: &T)
2383 where
2384 T: serde::de::DeserializeOwned + Serialize + PartialEq + std::fmt::Debug,
2385 {
2386 let raw = serde_json::to_string(val).expect("serializing `val` should work");
2387 let parsed: T =
2388 serde_json::from_str(&raw).expect("must be able to parse back serialized value");
2389 assert_eq!(val, &parsed, "parsed value must equal original value");
2390 }
2391}