cml/
lib.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! A library of common utilities used by `cmc` and related tools.
6//! To manually regenerate reference documentation from doc comments in
7//! this file, see the instructions at:
8//!
9//!   tools/lib/reference_doc/macro/derive-reference-doc-tests/src/test_data/README.md
10
11pub mod error;
12pub mod features;
13pub mod one_or_many;
14pub mod types;
15pub(crate) mod validate;
16
17#[allow(unused)] // A test-only macro is defined outside of a test builds.
18pub 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
56/// Parses a string `buffer` into a [Document]. `file` is used for error reporting.
57pub 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
65/// Parses a string `buffer` into a [SpannedDocument]. `file` is used for error reporting.
66pub 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
76/// Parses a string `buffer` into a vector of [Document]. `file` is used for error reporting.
77/// Supports JSON encoded as an array of Document JSON objects.
78pub 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/// Generates deserializer for `OneOrMany<Name>`.
117#[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/// Generates deserializer for `OneOrMany<Path>`.
127#[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/// Generates deserializer for `OneOrMany<EventScope>`.
137#[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/// A reference in an `offer to`.
147#[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        // Collapse like-entries into one. Like entries are those that are equal in all fields
166        // but their capability names. Accomplish this by collecting all the names into a vector
167        // keyed by an instance of T with its names removed.
168        let mut to_merge: Vec<(T, Vec<Name>)> = vec![];
169        let mut to_keep: Vec<T> = vec![];
170        self.iter().for_each(|c| {
171            // Any entry with a `path` set cannot be merged with another.
172            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()]); // The name here is arbitrary.
179            let r = to_merge.iter().position(|(t, _)| t == &copy);
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            // Sort by capability type, then by the name of the first entry for
198            // that type.
199            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/// A relative reference to another object. This is a generic type that can encode any supported
213/// reference subtype. For named references, it holds a reference to the name instead of the name
214/// itself.
215///
216/// Objects of this type are usually derived from conversions of context-specific reference
217/// types that `#[derive(Reference)]`. This type makes it easy to write helper functions that operate on
218/// generic references.
219#[derive(Debug, PartialEq, Eq, Hash, Clone)]
220pub enum AnyRef<'a> {
221    /// A named reference. Parsed as `#name`.
222    Named(&'a BorrowedName),
223    /// A reference to the parent. Parsed as `parent`.
224    Parent,
225    /// A reference to the framework (component manager). Parsed as `framework`.
226    Framework,
227    /// A reference to the debug. Parsed as `debug`.
228    Debug,
229    /// A reference to this component. Parsed as `self`.
230    Self_,
231    /// An intentionally omitted reference.
232    Void,
233    /// A reference to a dictionary. Parsed as a dictionary path.
234    Dictionary(&'a DictionaryRef),
235    /// A reference to a dictionary defined by this component. Parsed as
236    /// `self/<dictionary>`.
237    OwnDictionary(&'a BorrowedName),
238}
239
240/// Format an `AnyRef` as a string.
241impl 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/// A reference to a (possibly nested) dictionary.
257#[derive(Debug, PartialEq, Eq, Hash, Clone)]
258pub struct DictionaryRef {
259    /// Path to the dictionary relative to `root_dictionary`.
260    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/// A reference to a root dictionary.
340#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
341#[reference(expected = "\"parent\", \"self\", \"#<child-name>\"")]
342pub enum RootDictionaryRef {
343    /// A reference to a child.
344    Named(Name),
345    /// A reference to the parent.
346    Parent,
347    /// A reference to this component.
348    Self_,
349}
350
351/// The scope of an event.
352#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference, Ord, PartialOrd)]
353#[reference(expected = "\"#<collection-name>\", \"#<child-name>\", or none")]
354pub enum EventScope {
355    /// A reference to a child or a collection.
356    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    /// Update the hasher by digesting the ConfigVectorElementType enum value
410    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        // identifiers must start with a letter
497        let first_is_letter = s.chars().next().expect("non-empty string").is_ascii_lowercase();
498        // can contain letters, numbers, and underscores
499        let contains_invalid_chars =
500            s.chars().any(|c| !(c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_'));
501        // cannot end with an underscore
502        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    /// Update the hasher by digesting the ConfigValueType enum value
603    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    /// Returns the name of the capability for display purposes.
681    /// If `service()` returns `Some`, the capability name must be "service", etc.
682    ///
683    /// Returns an error if the capability name is not set, or if there is more than one.
684    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    /// Returns true if this capability type allows the ::Many variant of OneOrMany.
737    fn are_many_names_allowed(&self) -> bool;
738
739    fn decl_type(&self) -> &'static str;
740    fn supported(&self) -> &[&'static str];
741
742    /// Returns the names of the capabilities in this clause.
743    /// If `protocol()` returns `Some(OneOrMany::Many(vec!["a", "b"]))`, this returns!["a", "b"].
744    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    /// Returns the name of the capability for display purposes.
806    /// If `service()` returns `Some`, the capability name must be "service", etc.
807    ///
808    /// Returns an error if the capability name is not set, or if there is more than one.
809    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, // but use options_by_path to turn this off for program args
969        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    // Clone is needed so the iterator can be moved forward.
1039    // This doesn't actually allocate memory or copy a string, as only the reference
1040    // held by the OfferToAllCapability<'a> is copied.
1041    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
1053/// Returns `Ok(true)` if desugaring the `offer_to_all` using `name` duplicates
1054/// `specific_offer`. Returns `Ok(false)` if not a duplicate.
1055///
1056/// Returns Err if there is a validation error.
1057pub 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    // Only protocols and dictionaries may be offered to all
1063    assert!(offer_to_all.protocol.is_some() || offer_to_all.dictionary.is_some());
1064
1065    // If none of the pairs of the cross products of the two offer's protocols
1066    // match, then the offer is certainly not a duplicate
1067    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    // Since the capability ID's match, the underlying protocol must also match
1094    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    // Exercise reference parsing tests on `OfferFromRef` because it contains every reference
1149    // subtype.
1150
1151    #[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    // TODO: Use Default::default() instead
1323
1324    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        // service
1379        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        // protocol
1439        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        // directory
1480        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        // storage
1504        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        // runner
1528        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        // dictionary
1534        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        // "as" aliasing.
1575        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        // Error case.
1585        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        // It's ok if the values match.
1803        {
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                // Will be sorted by name
1889                { "name": "b_child", "url": "http://foo/b" },
1890                { "name": "a_child", "url": "http://foo/a" },
1891            ],
1892            "environments": [
1893                // Will be sorted by name
1894                { "name": "b_env" },
1895                { "name": "a_env" },
1896            ],
1897            "collections": [
1898                // Will be sorted by name
1899                { "name": "b_coll", "durability": "transient" },
1900                { "name": "a_coll", "durability": "transient" },
1901            ],
1902            // Will have entries sorted by capability type, then
1903            // by capability name (using the first entry in Many cases).
1904            "capabilities": [
1905                // Will be merged with "bar"
1906                { "protocol": ["foo"] },
1907                { "protocol": "bar" },
1908                // Will not be merged, but will be sorted before "bar"
1909                { "protocol": "arg", "path": "/arg" },
1910                // Will have list of names sorted
1911                { "service": ["b", "a"] },
1912                // Will have list of names sorted
1913                { "event_stream": ["b", "a"] },
1914                { "runner": "myrunner" },
1915                // The following two will *not* be merged, because they have a `path`.
1916                { "runner": "mypathrunner1", "path": "/foo" },
1917                { "runner": "mypathrunner2", "path": "/foo" },
1918            ],
1919            // Same rules as for "capabilities".
1920            "offer": [
1921                // Will be sorted after "bar"
1922                { "protocol": "baz", "from": "#a_child", "to": "#c_child"  },
1923                // The following two entries will be merged
1924                { "protocol": ["foo"], "from": "#a_child", "to": "#b_child"  },
1925                { "protocol": "bar", "from": "#a_child", "to": "#b_child"  },
1926                // Will have list of names sorted
1927                { "service": ["b", "a"], "from": "#a_child", "to": "#b_child"  },
1928                // Will have list of names sorted
1929                {
1930                    "event_stream": ["b", "a"],
1931                    "from": "#a_child",
1932                    "to": "#b_child",
1933                    "scope": ["#b", "#c", "#a"]  // Also gets sorted
1934                },
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" },  // Will appear before protocol: foo
1942                // Will have list of names sorted
1943                { "service": ["b", "a"], "from": "#a_child" },
1944                // Will have list of names sorted
1945                {
1946                    "event_stream": ["b", "a"],
1947                    "from": "#a_child",
1948                    "scope": ["#b", "#c", "#a"]  // Also gets sorted
1949                },
1950                { "runner": [ "myrunner", "a" ], "from": "#a_child" },
1951                { "runner": [ "b" ], "from": "#a_child" },
1952                { "directory": [ "b" ], "from": "#a_child" },
1953            ],
1954            "use": [
1955                // Will be sorted after "baz"
1956                { "protocol": ["zazzle"], "path": "/zazbaz" },
1957                // These will be merged
1958                { "protocol": ["foo"] },
1959                { "protocol": "bar" },
1960                // Will have list of names sorted
1961                { "service": ["b", "a"] },
1962                // Will have list of names sorted
1963                { "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        // https://fxbug.dev/42160240: merging with a document that doesn't have a runner doesn't override the
2067        // runner that we already have assigned.
2068        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        // It's ok to merge `program.environ` by concatenating the arrays together.
2078        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        // It's ok to merge `program.runner = "elf"` with `program.runner = "elf"`.
2088        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        // different protocols
2112        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        // different targets
2128        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        // target has alias
2146        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        // This document is an error because there is no capability name.
2363        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}