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