Skip to main content

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 load;
14pub mod one_or_many;
15pub mod types;
16pub(crate) mod validate;
17
18#[allow(unused)] // A test-only macro is defined outside of a test builds.
19pub mod translate;
20
21use crate::error::Error;
22use cml_macro::{OneOrMany, Reference};
23use json5format::{FormatOptions, PathOption};
24use maplit::{hashmap, hashset};
25use serde::{Deserialize, Serialize, de, ser};
26use std::fmt;
27use std::hash::Hash;
28use std::num::NonZeroU32;
29use std::str::FromStr;
30use std::sync::Arc;
31
32pub use crate::types::capability::{Capability, CapabilityFromRef, ContextCapability};
33pub use crate::types::capability_id::CapabilityId;
34pub use crate::types::child::Child;
35pub use crate::types::collection::Collection;
36use crate::types::common::{ContextCapabilityClause, ContextPathClause, ContextSpanned};
37pub use crate::types::document::{Document, DocumentContext, parse_and_hydrate};
38pub use crate::types::environment::{Environment, ResolverRegistration};
39pub use crate::types::expose::{ContextExpose, Expose};
40pub use crate::types::offer::{Offer, OfferFromRef, OfferToAllCapability, OfferToRef};
41pub use crate::types::program::Program;
42pub use crate::types::r#use::{Use, UseFromRef};
43
44pub use cm_types::{
45    AllowedOffers, Availability, BorrowedName, BoundedName, DeliveryType, DependencyType,
46    Durability, HandleType, Name, NamespacePath, OnTerminate, ParseError, Path, RelativePath,
47    StartupMode, StorageId, Url,
48};
49use error::Location;
50
51pub use crate::one_or_many::OneOrMany;
52pub use crate::translate::{CompileOptions, compile};
53pub use crate::validate::{CapabilityRequirements, MustUseRequirement};
54
55/// Parses a string `buffer` into a [Document]. `file` is used for error reporting.
56pub fn parse_one_document(buffer: &String, file: &std::path::Path) -> Result<Document, Error> {
57    serde_json5::from_str(&buffer).map_err(|e| {
58        let serde_json5::Error::Message { location, msg } = e;
59        let location = location.map(|l| Location { line: l.line, column: l.column });
60        Error::parse(msg, location, Some(file))
61    })
62}
63
64pub fn load_cml_with_context(
65    buffer: &String,
66    file: &std::path::Path,
67) -> Result<DocumentContext, Error> {
68    let file_arc = Arc::from(file);
69    parse_and_hydrate(file_arc, buffer)
70}
71
72/// Generates deserializer for `OneOrMany<Name>`.
73#[derive(OneOrMany, Debug, Clone)]
74#[one_or_many(
75    expected = "a name or nonempty array of names, with unique elements",
76    inner_type = "Name",
77    min_length = 1,
78    unique_items = true
79)]
80pub struct OneOrManyNames;
81
82/// Generates deserializer for `OneOrMany<Path>`.
83#[derive(OneOrMany, Debug, Clone)]
84#[one_or_many(
85    expected = "a path or nonempty array of paths, with unique elements",
86    inner_type = "Path",
87    min_length = 1,
88    unique_items = true
89)]
90pub struct OneOrManyPaths;
91
92/// Generates deserializer for `OneOrMany<EventScope>`.
93#[derive(OneOrMany, Debug, Clone)]
94#[one_or_many(
95    expected = "one or an array of \"#<collection-name>\", or \"#<child-name>\"",
96    inner_type = "EventScope",
97    min_length = 1,
98    unique_items = true
99)]
100pub struct OneOrManyEventScope;
101
102/// A reference in an `offer to` or `exose to`.
103#[derive(Debug, Deserialize, PartialEq, Eq, Hash, Clone, Serialize)]
104#[serde(rename_all = "snake_case")]
105pub enum SourceAvailability {
106    Required,
107    Unknown,
108}
109
110impl Default for SourceAvailability {
111    fn default() -> Self {
112        Self::Required
113    }
114}
115
116impl<T> CanonicalizeContext for Vec<T>
117where
118    T: CanonicalizeContext + ContextCapabilityClause + ContextPathClause + Clone + PartialEq,
119{
120    fn canonicalize_context(&mut self) {
121        let mut to_merge: Vec<(T, Vec<ContextSpanned<Name>>)> = Vec::new();
122        let mut to_keep: Vec<T> = vec![];
123
124        self.iter().for_each(|c| {
125            // Any entry with a `path` set cannot be merged with another.
126            if !c.are_many_names_allowed() || c.path().is_some() {
127                to_keep.push(c.clone());
128                return;
129            }
130
131            let mut names = c.names();
132            let mut copy: T = c.clone();
133
134            let synthetic_name = Name::from_str("a").unwrap();
135            let spanned = ContextSpanned { value: synthetic_name, origin: c.origin().clone() };
136            copy.set_names(vec![spanned]);
137
138            let r = to_merge.iter().position(|(t, _)| t == &copy);
139            match r {
140                Some(i) => to_merge[i].1.append(&mut names),
141                None => to_merge.push((copy, names)),
142            };
143        });
144
145        let mut merged = to_merge
146            .into_iter()
147            .map(|(mut t, mut names)| {
148                names.sort_by(|a, b| a.value.cmp(&b.value));
149
150                t.set_names(names);
151                t
152            })
153            .collect::<Vec<_>>();
154
155        to_keep.append(&mut merged);
156        *self = to_keep;
157
158        self.iter_mut().for_each(|c| c.canonicalize_context());
159
160        self.sort_by(|a, b| {
161            // Sort by capability type string first
162            let a_type = a.capability_type(None).unwrap();
163            let b_type = b.capability_type(None).unwrap();
164
165            a_type.cmp(b_type).then_with(|| {
166                let a_names = a.names();
167                let b_names = b.names();
168
169                // Compare the first name in the list (e.g., "fuchsia.logger.Log") ignoring Origin differences.
170                let a_first_val = &a_names.first().unwrap().value;
171                let b_first_val = &b_names.first().unwrap().value;
172
173                a_first_val.cmp(b_first_val)
174            })
175        });
176    }
177}
178
179/// A relative reference to another object. This is a generic type that can encode any supported
180/// reference subtype. For named references, it holds a reference to the name instead of the name
181/// itself.
182///
183/// Objects of this type are usually derived from conversions of context-specific reference
184/// types that `#[derive(Reference)]`. This type makes it easy to write helper functions that operate on
185/// generic references.
186#[derive(Debug, PartialEq, Eq, Hash, Clone)]
187pub enum AnyRef<'a> {
188    /// A named reference. Parsed as `#name`.
189    Named(&'a BorrowedName),
190    /// A reference to the parent. Parsed as `parent`.
191    Parent,
192    /// A reference to the framework (component manager). Parsed as `framework`.
193    Framework,
194    /// A reference to the debug. Parsed as `debug`.
195    Debug,
196    /// A reference to this component. Parsed as `self`.
197    Self_,
198    /// An intentionally omitted reference.
199    Void,
200    /// A reference to a dictionary. Parsed as a dictionary path.
201    Dictionary(&'a DictionaryRef),
202    /// A reference to a dictionary defined by this component. Parsed as
203    /// `self/<dictionary>`.
204    OwnDictionary(&'a BorrowedName),
205}
206
207/// Format an `AnyRef` as a string.
208impl fmt::Display for AnyRef<'_> {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        match self {
211            Self::Named(name) => write!(f, "#{}", name),
212            Self::Parent => write!(f, "parent"),
213            Self::Framework => write!(f, "framework"),
214            Self::Debug => write!(f, "debug"),
215            Self::Self_ => write!(f, "self"),
216            Self::Void => write!(f, "void"),
217            Self::Dictionary(d) => write!(f, "{}", d),
218            Self::OwnDictionary(name) => write!(f, "self/{}", name),
219        }
220    }
221}
222
223/// A reference to a (possibly nested) dictionary.
224#[derive(Debug, PartialEq, Eq, Hash, Clone)]
225pub struct DictionaryRef {
226    /// Path to the dictionary relative to `root_dictionary`.
227    pub path: RelativePath,
228    pub root: RootDictionaryRef,
229}
230
231impl<'a> From<&'a DictionaryRef> for AnyRef<'a> {
232    fn from(r: &'a DictionaryRef) -> Self {
233        Self::Dictionary(r)
234    }
235}
236
237impl<'a> From<&'a Name> for AnyRef<'a> {
238    fn from(name: &'a Name) -> Self {
239        AnyRef::Named(name.as_ref())
240    }
241}
242
243impl<'a> From<&'a BorrowedName> for AnyRef<'a> {
244    fn from(name: &'a BorrowedName) -> Self {
245        AnyRef::Named(name)
246    }
247}
248
249impl FromStr for DictionaryRef {
250    type Err = ParseError;
251
252    fn from_str(path: &str) -> Result<Self, ParseError> {
253        match path.find('/') {
254            Some(n) => {
255                let root = path[..n].parse().map_err(|_| ParseError::InvalidValue)?;
256                let path = RelativePath::new(&path[n + 1..])?;
257                Ok(Self { root, path })
258            }
259            None => Err(ParseError::InvalidValue),
260        }
261    }
262}
263
264impl fmt::Display for DictionaryRef {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        write!(f, "{}/{}", self.root, self.path)
267    }
268}
269
270impl ser::Serialize for DictionaryRef {
271    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
272    where
273        S: serde::ser::Serializer,
274    {
275        format!("{}", self).serialize(serializer)
276    }
277}
278
279const DICTIONARY_REF_EXPECT_STR: &str = "a path to a dictionary no more \
280    than 4095 characters in length";
281
282impl<'de> de::Deserialize<'de> for DictionaryRef {
283    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
284    where
285        D: de::Deserializer<'de>,
286    {
287        struct Visitor;
288
289        impl<'de> de::Visitor<'de> for Visitor {
290            type Value = DictionaryRef;
291
292            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293                f.write_str(DICTIONARY_REF_EXPECT_STR)
294            }
295
296            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
297            where
298                E: de::Error,
299            {
300                s.parse().map_err(|err| match err {
301                    ParseError::InvalidValue => {
302                        E::invalid_value(de::Unexpected::Str(s), &DICTIONARY_REF_EXPECT_STR)
303                    }
304                    ParseError::TooLong | ParseError::Empty => {
305                        E::invalid_length(s.len(), &DICTIONARY_REF_EXPECT_STR)
306                    }
307                    e => {
308                        panic!("unexpected parse error: {:?}", e);
309                    }
310                })
311            }
312        }
313
314        deserializer.deserialize_string(Visitor)
315    }
316}
317
318/// A reference to a root dictionary.
319#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
320#[reference(expected = "\"parent\", \"self\", \"#<child-name>\"")]
321pub enum RootDictionaryRef {
322    /// A reference to a child.
323    Named(Name),
324    /// A reference to the parent.
325    Parent,
326    /// A reference to this component.
327    Self_,
328}
329
330/// The scope of an event.
331#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference, Ord, PartialOrd)]
332#[reference(expected = "\"#<collection-name>\", \"#<child-name>\", or none")]
333pub enum EventScope {
334    /// A reference to a child or a collection.
335    Named(Name),
336}
337
338#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
339#[serde(rename_all = "snake_case")]
340pub enum ConfigType {
341    Bool,
342    Uint8,
343    Uint16,
344    Uint32,
345    Uint64,
346    Int8,
347    Int16,
348    Int32,
349    Int64,
350    String,
351    Vector,
352}
353
354impl From<&cm_rust::ConfigValueType> for ConfigType {
355    fn from(value: &cm_rust::ConfigValueType) -> Self {
356        match value {
357            cm_rust::ConfigValueType::Bool => ConfigType::Bool,
358            cm_rust::ConfigValueType::Uint8 => ConfigType::Uint8,
359            cm_rust::ConfigValueType::Int8 => ConfigType::Int8,
360            cm_rust::ConfigValueType::Uint16 => ConfigType::Uint16,
361            cm_rust::ConfigValueType::Int16 => ConfigType::Int16,
362            cm_rust::ConfigValueType::Uint32 => ConfigType::Uint32,
363            cm_rust::ConfigValueType::Int32 => ConfigType::Int32,
364            cm_rust::ConfigValueType::Uint64 => ConfigType::Uint64,
365            cm_rust::ConfigValueType::Int64 => ConfigType::Int64,
366            cm_rust::ConfigValueType::String { .. } => ConfigType::String,
367            cm_rust::ConfigValueType::Vector { .. } => ConfigType::Vector,
368        }
369    }
370}
371
372#[derive(Clone, Deserialize, Debug, PartialEq, Serialize)]
373#[serde(tag = "type", deny_unknown_fields, rename_all = "lowercase")]
374pub enum ConfigNestedValueType {
375    Bool {},
376    Uint8 {},
377    Uint16 {},
378    Uint32 {},
379    Uint64 {},
380    Int8 {},
381    Int16 {},
382    Int32 {},
383    Int64 {},
384    String { max_size: NonZeroU32 },
385}
386
387impl ConfigNestedValueType {
388    /// Update the hasher by digesting the ConfigVectorElementType enum value
389    pub fn update_digest(&self, hasher: &mut impl sha2::Digest) {
390        let val = match self {
391            ConfigNestedValueType::Bool {} => 0u8,
392            ConfigNestedValueType::Uint8 {} => 1u8,
393            ConfigNestedValueType::Uint16 {} => 2u8,
394            ConfigNestedValueType::Uint32 {} => 3u8,
395            ConfigNestedValueType::Uint64 {} => 4u8,
396            ConfigNestedValueType::Int8 {} => 5u8,
397            ConfigNestedValueType::Int16 {} => 6u8,
398            ConfigNestedValueType::Int32 {} => 7u8,
399            ConfigNestedValueType::Int64 {} => 8u8,
400            ConfigNestedValueType::String { max_size } => {
401                hasher.update(max_size.get().to_le_bytes());
402                9u8
403            }
404        };
405        hasher.update([val])
406    }
407}
408
409impl From<ConfigNestedValueType> for cm_rust::ConfigNestedValueType {
410    fn from(value: ConfigNestedValueType) -> Self {
411        match value {
412            ConfigNestedValueType::Bool {} => cm_rust::ConfigNestedValueType::Bool,
413            ConfigNestedValueType::Uint8 {} => cm_rust::ConfigNestedValueType::Uint8,
414            ConfigNestedValueType::Uint16 {} => cm_rust::ConfigNestedValueType::Uint16,
415            ConfigNestedValueType::Uint32 {} => cm_rust::ConfigNestedValueType::Uint32,
416            ConfigNestedValueType::Uint64 {} => cm_rust::ConfigNestedValueType::Uint64,
417            ConfigNestedValueType::Int8 {} => cm_rust::ConfigNestedValueType::Int8,
418            ConfigNestedValueType::Int16 {} => cm_rust::ConfigNestedValueType::Int16,
419            ConfigNestedValueType::Int32 {} => cm_rust::ConfigNestedValueType::Int32,
420            ConfigNestedValueType::Int64 {} => cm_rust::ConfigNestedValueType::Int64,
421            ConfigNestedValueType::String { max_size } => {
422                cm_rust::ConfigNestedValueType::String { max_size: max_size.into() }
423            }
424        }
425    }
426}
427
428impl TryFrom<&cm_rust::ConfigNestedValueType> for ConfigNestedValueType {
429    type Error = ();
430    fn try_from(nested: &cm_rust::ConfigNestedValueType) -> Result<Self, ()> {
431        Ok(match nested {
432            cm_rust::ConfigNestedValueType::Bool => ConfigNestedValueType::Bool {},
433            cm_rust::ConfigNestedValueType::Uint8 => ConfigNestedValueType::Uint8 {},
434            cm_rust::ConfigNestedValueType::Int8 => ConfigNestedValueType::Int8 {},
435            cm_rust::ConfigNestedValueType::Uint16 => ConfigNestedValueType::Uint16 {},
436            cm_rust::ConfigNestedValueType::Int16 => ConfigNestedValueType::Int16 {},
437            cm_rust::ConfigNestedValueType::Uint32 => ConfigNestedValueType::Uint32 {},
438            cm_rust::ConfigNestedValueType::Int32 => ConfigNestedValueType::Int32 {},
439            cm_rust::ConfigNestedValueType::Uint64 => ConfigNestedValueType::Uint64 {},
440            cm_rust::ConfigNestedValueType::Int64 => ConfigNestedValueType::Int64 {},
441            cm_rust::ConfigNestedValueType::String { max_size } => {
442                ConfigNestedValueType::String { max_size: NonZeroU32::new(*max_size).ok_or(())? }
443            }
444        })
445    }
446}
447
448#[derive(Clone, Hash, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize)]
449pub struct ConfigKey(String);
450
451impl ConfigKey {
452    pub fn as_str(&self) -> &str {
453        self.0.as_str()
454    }
455}
456
457impl std::fmt::Display for ConfigKey {
458    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
459        write!(f, "{}", self.0)
460    }
461}
462
463impl FromStr for ConfigKey {
464    type Err = ParseError;
465
466    fn from_str(s: &str) -> Result<Self, ParseError> {
467        let length = s.len();
468        if length == 0 {
469            return Err(ParseError::Empty);
470        }
471        if length > 64 {
472            return Err(ParseError::TooLong);
473        }
474
475        // identifiers must start with a letter
476        let first_is_letter = s.chars().next().expect("non-empty string").is_ascii_lowercase();
477        // can contain letters, numbers, and underscores
478        let contains_invalid_chars =
479            s.chars().any(|c| !(c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_'));
480        // cannot end with an underscore
481        let last_is_underscore = s.chars().next_back().expect("non-empty string") == '_';
482
483        if !first_is_letter || contains_invalid_chars || last_is_underscore {
484            return Err(ParseError::InvalidValue);
485        }
486
487        Ok(Self(s.to_string()))
488    }
489}
490
491impl<'de> de::Deserialize<'de> for ConfigKey {
492    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
493    where
494        D: de::Deserializer<'de>,
495    {
496        struct Visitor;
497
498        impl<'de> de::Visitor<'de> for Visitor {
499            type Value = ConfigKey;
500
501            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
502                f.write_str(
503                    "a non-empty string no more than 64 characters in length, which must \
504                    start with a letter, can contain letters, numbers, and underscores, \
505                    but cannot end with an underscore",
506                )
507            }
508
509            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
510            where
511                E: de::Error,
512            {
513                s.parse().map_err(|err| match err {
514                    ParseError::InvalidValue => E::invalid_value(
515                        de::Unexpected::Str(s),
516                        &"a name which must start with a letter, can contain letters, \
517                        numbers, and underscores, but cannot end with an underscore",
518                    ),
519                    ParseError::TooLong | ParseError::Empty => E::invalid_length(
520                        s.len(),
521                        &"a non-empty name no more than 64 characters in length",
522                    ),
523                    e => {
524                        panic!("unexpected parse error: {:?}", e);
525                    }
526                })
527            }
528        }
529        deserializer.deserialize_string(Visitor)
530    }
531}
532
533#[derive(Clone, Deserialize, Debug, PartialEq, Serialize)]
534#[serde(deny_unknown_fields, rename_all = "lowercase")]
535pub enum ConfigRuntimeSource {
536    Parent,
537}
538
539#[derive(Clone, Deserialize, Debug, PartialEq, Serialize)]
540#[serde(tag = "type", deny_unknown_fields, rename_all = "lowercase")]
541pub enum ConfigValueType {
542    Bool {
543        #[serde(skip_serializing_if = "Option::is_none")]
544        mutability: Option<Vec<ConfigRuntimeSource>>,
545    },
546    Uint8 {
547        #[serde(skip_serializing_if = "Option::is_none")]
548        mutability: Option<Vec<ConfigRuntimeSource>>,
549    },
550    Uint16 {
551        #[serde(skip_serializing_if = "Option::is_none")]
552        mutability: Option<Vec<ConfigRuntimeSource>>,
553    },
554    Uint32 {
555        #[serde(skip_serializing_if = "Option::is_none")]
556        mutability: Option<Vec<ConfigRuntimeSource>>,
557    },
558    Uint64 {
559        #[serde(skip_serializing_if = "Option::is_none")]
560        mutability: Option<Vec<ConfigRuntimeSource>>,
561    },
562    Int8 {
563        #[serde(skip_serializing_if = "Option::is_none")]
564        mutability: Option<Vec<ConfigRuntimeSource>>,
565    },
566    Int16 {
567        #[serde(skip_serializing_if = "Option::is_none")]
568        mutability: Option<Vec<ConfigRuntimeSource>>,
569    },
570    Int32 {
571        #[serde(skip_serializing_if = "Option::is_none")]
572        mutability: Option<Vec<ConfigRuntimeSource>>,
573    },
574    Int64 {
575        #[serde(skip_serializing_if = "Option::is_none")]
576        mutability: Option<Vec<ConfigRuntimeSource>>,
577    },
578    String {
579        max_size: NonZeroU32,
580        #[serde(skip_serializing_if = "Option::is_none")]
581        mutability: Option<Vec<ConfigRuntimeSource>>,
582    },
583    Vector {
584        max_count: NonZeroU32,
585        element: ConfigNestedValueType,
586        #[serde(skip_serializing_if = "Option::is_none")]
587        mutability: Option<Vec<ConfigRuntimeSource>>,
588    },
589}
590
591impl ConfigValueType {
592    /// Update the hasher by digesting the ConfigValueType enum value
593    pub fn update_digest(&self, hasher: &mut impl sha2::Digest) {
594        let val = match self {
595            ConfigValueType::Bool { .. } => 0u8,
596            ConfigValueType::Uint8 { .. } => 1u8,
597            ConfigValueType::Uint16 { .. } => 2u8,
598            ConfigValueType::Uint32 { .. } => 3u8,
599            ConfigValueType::Uint64 { .. } => 4u8,
600            ConfigValueType::Int8 { .. } => 5u8,
601            ConfigValueType::Int16 { .. } => 6u8,
602            ConfigValueType::Int32 { .. } => 7u8,
603            ConfigValueType::Int64 { .. } => 8u8,
604            ConfigValueType::String { max_size, .. } => {
605                hasher.update(max_size.get().to_le_bytes());
606                9u8
607            }
608            ConfigValueType::Vector { max_count, element, .. } => {
609                hasher.update(max_count.get().to_le_bytes());
610                element.update_digest(hasher);
611                10u8
612            }
613        };
614        hasher.update([val])
615    }
616}
617
618impl From<ConfigValueType> for cm_rust::ConfigValueType {
619    fn from(value: ConfigValueType) -> Self {
620        match value {
621            ConfigValueType::Bool { .. } => cm_rust::ConfigValueType::Bool,
622            ConfigValueType::Uint8 { .. } => cm_rust::ConfigValueType::Uint8,
623            ConfigValueType::Uint16 { .. } => cm_rust::ConfigValueType::Uint16,
624            ConfigValueType::Uint32 { .. } => cm_rust::ConfigValueType::Uint32,
625            ConfigValueType::Uint64 { .. } => cm_rust::ConfigValueType::Uint64,
626            ConfigValueType::Int8 { .. } => cm_rust::ConfigValueType::Int8,
627            ConfigValueType::Int16 { .. } => cm_rust::ConfigValueType::Int16,
628            ConfigValueType::Int32 { .. } => cm_rust::ConfigValueType::Int32,
629            ConfigValueType::Int64 { .. } => cm_rust::ConfigValueType::Int64,
630            ConfigValueType::String { max_size, .. } => {
631                cm_rust::ConfigValueType::String { max_size: max_size.into() }
632            }
633            ConfigValueType::Vector { max_count, element, .. } => {
634                cm_rust::ConfigValueType::Vector {
635                    max_count: max_count.into(),
636                    nested_type: element.into(),
637                }
638            }
639        }
640    }
641}
642
643pub trait FromClause {
644    fn from_(&self) -> OneOrMany<AnyRef<'_>>;
645}
646
647pub trait FromClauseContext {
648    fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>>;
649}
650
651pub trait CapabilityClause: Clone + PartialEq + std::fmt::Debug {
652    fn service(&self) -> Option<OneOrMany<&BorrowedName>>;
653    fn protocol(&self) -> Option<OneOrMany<&BorrowedName>>;
654    fn directory(&self) -> Option<OneOrMany<&BorrowedName>>;
655    fn storage(&self) -> Option<OneOrMany<&BorrowedName>>;
656    fn runner(&self) -> Option<OneOrMany<&BorrowedName>>;
657    fn resolver(&self) -> Option<OneOrMany<&BorrowedName>>;
658    fn event_stream(&self) -> Option<OneOrMany<&BorrowedName>>;
659    fn dictionary(&self) -> Option<OneOrMany<&BorrowedName>>;
660    fn config(&self) -> Option<OneOrMany<&BorrowedName>>;
661    fn set_service(&mut self, o: Option<OneOrMany<Name>>);
662    fn set_protocol(&mut self, o: Option<OneOrMany<Name>>);
663    fn set_directory(&mut self, o: Option<OneOrMany<Name>>);
664    fn set_storage(&mut self, o: Option<OneOrMany<Name>>);
665    fn set_runner(&mut self, o: Option<OneOrMany<Name>>);
666    fn set_resolver(&mut self, o: Option<OneOrMany<Name>>);
667    fn set_event_stream(&mut self, o: Option<OneOrMany<Name>>);
668    fn set_dictionary(&mut self, o: Option<OneOrMany<Name>>);
669    fn set_config(&mut self, o: Option<OneOrMany<Name>>);
670
671    fn availability(&self) -> Option<Availability>;
672    fn set_availability(&mut self, a: Option<Availability>);
673
674    /// Returns the name of the capability for display purposes.
675    /// If `service()` returns `Some`, the capability name must be "service", etc.
676    ///
677    /// Returns an error if the capability name is not set, or if there is more than one.
678    fn capability_type(&self) -> Result<&'static str, Error> {
679        let mut types = Vec::new();
680        if self.service().is_some() {
681            types.push("service");
682        }
683        if self.protocol().is_some() {
684            types.push("protocol");
685        }
686        if self.directory().is_some() {
687            types.push("directory");
688        }
689        if self.storage().is_some() {
690            types.push("storage");
691        }
692        if self.event_stream().is_some() {
693            types.push("event_stream");
694        }
695        if self.runner().is_some() {
696            types.push("runner");
697        }
698        if self.config().is_some() {
699            types.push("config");
700        }
701        if self.resolver().is_some() {
702            types.push("resolver");
703        }
704        if self.dictionary().is_some() {
705            types.push("dictionary");
706        }
707        match types.len() {
708            0 => {
709                let supported_keywords = self
710                    .supported()
711                    .iter()
712                    .map(|k| format!("\"{}\"", k))
713                    .collect::<Vec<_>>()
714                    .join(", ");
715                Err(Error::validate(format!(
716                    "`{}` declaration is missing a capability keyword, one of: {}",
717                    self.decl_type(),
718                    supported_keywords,
719                )))
720            }
721            1 => Ok(types[0]),
722            _ => Err(Error::validate(format!(
723                "{} declaration has multiple capability types defined: {:?}",
724                self.decl_type(),
725                types
726            ))),
727        }
728    }
729
730    /// Returns true if this capability type allows the ::Many variant of OneOrMany.
731    fn are_many_names_allowed(&self) -> bool;
732
733    fn decl_type(&self) -> &'static str;
734    fn supported(&self) -> &[&'static str];
735
736    /// Returns the names of the capabilities in this clause.
737    /// If `protocol()` returns `Some(OneOrMany::Many(vec!["a", "b"]))`, this returns!["a", "b"].
738    fn names(&self) -> Vec<&BorrowedName> {
739        let res = vec![
740            self.service(),
741            self.protocol(),
742            self.directory(),
743            self.storage(),
744            self.runner(),
745            self.config(),
746            self.resolver(),
747            self.event_stream(),
748            self.dictionary(),
749        ];
750        res.into_iter()
751            .map(|o| o.map(|o| o.into_iter().collect::<Vec<&BorrowedName>>()).unwrap_or(vec![]))
752            .flatten()
753            .collect()
754    }
755
756    fn set_names(&mut self, names: Vec<Name>) {
757        let names = match names.len() {
758            0 => None,
759            1 => Some(OneOrMany::One(names.first().unwrap().clone())),
760            _ => Some(OneOrMany::Many(names)),
761        };
762
763        let cap_type = self.capability_type().unwrap();
764        if cap_type == "protocol" {
765            self.set_protocol(names);
766        } else if cap_type == "service" {
767            self.set_service(names);
768        } else if cap_type == "directory" {
769            self.set_directory(names);
770        } else if cap_type == "storage" {
771            self.set_storage(names);
772        } else if cap_type == "runner" {
773            self.set_runner(names);
774        } else if cap_type == "resolver" {
775            self.set_resolver(names);
776        } else if cap_type == "event_stream" {
777            self.set_event_stream(names);
778        } else if cap_type == "dictionary" {
779            self.set_dictionary(names);
780        } else if cap_type == "config" {
781            self.set_config(names);
782        } else {
783            panic!("Unknown capability type {}", cap_type);
784        }
785    }
786}
787
788trait CanonicalizeContext {
789    fn canonicalize_context(&mut self);
790}
791
792pub trait AsClauseContext {
793    fn r#as(&self) -> Option<ContextSpanned<&BorrowedName>>;
794}
795
796pub fn alias_or_name_context<'a>(
797    alias: Option<ContextSpanned<&'a BorrowedName>>,
798    name: &'a BorrowedName,
799    origin: Arc<std::path::Path>,
800) -> ContextSpanned<&'a BorrowedName> {
801    alias.unwrap_or(ContextSpanned { value: name, origin })
802}
803
804pub fn alias_or_path<'a>(alias: Option<&'a Path>, path: &'a Path) -> &'a Path {
805    alias.unwrap_or(path)
806}
807
808pub fn format_cml(buffer: &str, file: Option<&std::path::Path>) -> Result<Vec<u8>, Error> {
809    let general_order = PathOption::PropertyNameOrder(vec![
810        "name",
811        "url",
812        "startup",
813        "environment",
814        "config",
815        "dictionary",
816        "durability",
817        "service",
818        "protocol",
819        "directory",
820        "storage",
821        "runner",
822        "resolver",
823        "event",
824        "event_stream",
825        "from",
826        "as",
827        "to",
828        "rights",
829        "path",
830        "subdir",
831        "filter",
832        "dependency",
833        "extends",
834        "runners",
835        "resolvers",
836        "debug",
837    ]);
838    let options = FormatOptions {
839        collapse_containers_of_one: true,
840        sort_array_items: true, // but use options_by_path to turn this off for program args
841        options_by_path: hashmap! {
842            "/*" => hashset! {
843                PathOption::PropertyNameOrder(vec![
844                    "name",
845                    "composite_name",
846                    "include",
847                    "program",
848                    "children",
849                    "collections",
850                    "capabilities",
851                    "use",
852                    "offer",
853                    "offers",
854                    "expose",
855                    "environments",
856                    "facets",
857                    "config",
858                    "metadata_mappings",
859                ])
860            },
861            "/*/program" => hashset! {
862                PathOption::CollapseContainersOfOne(false),
863                PathOption::PropertyNameOrder(vec![
864                    "runner",
865                    "binary",
866                    "args",
867                ]),
868            },
869            "/*/program/*" => hashset! {
870                PathOption::SortArrayItems(false),
871            },
872            "/*/*/*" => hashset! {
873                general_order.clone()
874            },
875            "/*/*/*/*/*" => hashset! {
876                general_order
877            },
878        },
879        ..Default::default()
880    };
881
882    json5format::format(buffer, file.map(|f| f.to_string_lossy().to_string()), Some(options))
883        .map_err(|e| Error::json5(e, file))
884}
885
886#[cfg(test)]
887mod tests {
888    use super::*;
889    use crate::types::document;
890    use crate::types::environment::RunnerRegistration;
891    use assert_matches::assert_matches;
892    use serde_json::Value;
893    use std::path::Path;
894
895    // Exercise reference parsing tests on `OfferFromRef` because it contains every reference
896    // subtype.
897
898    #[test]
899    fn test_parse_named_reference() {
900        assert_matches!("#some-child".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "some-child");
901        assert_matches!("#A".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "A");
902        assert_matches!("#7".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "7");
903        assert_matches!("#_".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "_");
904
905        assert_matches!("#-".parse::<OfferFromRef>(), Err(_));
906        assert_matches!("#.".parse::<OfferFromRef>(), Err(_));
907        assert_matches!("#".parse::<OfferFromRef>(), Err(_));
908        assert_matches!("some-child".parse::<OfferFromRef>(), Err(_));
909    }
910
911    #[test]
912    fn test_parse_reference_test() {
913        assert_matches!("parent".parse::<OfferFromRef>(), Ok(OfferFromRef::Parent));
914        assert_matches!("framework".parse::<OfferFromRef>(), Ok(OfferFromRef::Framework));
915        assert_matches!("self".parse::<OfferFromRef>(), Ok(OfferFromRef::Self_));
916        assert_matches!("#child".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "child");
917
918        assert_matches!("invalid".parse::<OfferFromRef>(), Err(_));
919        assert_matches!("#invalid-child^".parse::<OfferFromRef>(), Err(_));
920    }
921
922    fn json_value_from_str(json: &str, filename: &Path) -> Result<Value, Error> {
923        serde_json::from_str(json).map_err(|e| {
924            Error::parse(
925                format!("Couldn't read input as JSON: {}", e),
926                Some(Location { line: e.line(), column: e.column() }),
927                Some(filename),
928            )
929        })
930    }
931
932    fn parse_as_ref(input: &str) -> Result<OfferFromRef, Error> {
933        serde_json::from_value::<OfferFromRef>(json_value_from_str(input, &Path::new("test.cml"))?)
934            .map_err(|e| Error::parse(format!("{}", e), None, None))
935    }
936
937    #[test]
938    fn test_deserialize_ref() -> Result<(), Error> {
939        assert_matches!(parse_as_ref("\"self\""), Ok(OfferFromRef::Self_));
940        assert_matches!(parse_as_ref("\"parent\""), Ok(OfferFromRef::Parent));
941        assert_matches!(parse_as_ref("\"#child\""), Ok(OfferFromRef::Named(name)) if name == "child");
942
943        assert_matches!(parse_as_ref(r#""invalid""#), Err(_));
944
945        Ok(())
946    }
947
948    #[test]
949    fn test_deny_unknown_fields() {
950        assert_matches!(serde_json5::from_str::<Document>("{ unknown: \"\" }"), Err(_));
951        assert_matches!(serde_json5::from_str::<Environment>("{ unknown: \"\" }"), Err(_));
952        assert_matches!(serde_json5::from_str::<RunnerRegistration>("{ unknown: \"\" }"), Err(_));
953        assert_matches!(serde_json5::from_str::<ResolverRegistration>("{ unknown: \"\" }"), Err(_));
954        assert_matches!(serde_json5::from_str::<Use>("{ unknown: \"\" }"), Err(_));
955        assert_matches!(serde_json5::from_str::<Expose>("{ unknown: \"\" }"), Err(_));
956        assert_matches!(serde_json5::from_str::<Offer>("{ unknown: \"\" }"), Err(_));
957        assert_matches!(serde_json5::from_str::<Capability>("{ unknown: \"\" }"), Err(_));
958        assert_matches!(serde_json5::from_str::<Child>("{ unknown: \"\" }"), Err(_));
959        assert_matches!(serde_json5::from_str::<Collection>("{ unknown: \"\" }"), Err(_));
960    }
961
962    #[test]
963    fn test_context_pipeline_denies_unknown_fields() {
964        let dummy_path = std::sync::Arc::from(std::path::Path::new("test.cml"));
965        let bad_json = "{ unknown : \"\" }".to_string();
966
967        let result = document::parse_and_hydrate(dummy_path, &bad_json);
968
969        assert!(
970            result.is_err(),
971            "parse should fail because the underlying Document rejected unknown fields"
972        );
973    }
974
975    #[test]
976    fn test_format_cml_top_level_name() {
977        let input = r#"{
978    use: [
979        {
980            protocol: "fuchsia.logger.LogSink",
981        },
982    ],
983    program: {
984        runner: "elf",
985    },
986    composite_name: "my_composite",
987    name: "my_component",
988}"#;
989        let formatted = format_cml(input, None).expect("failed to format");
990        let formatted_str = std::str::from_utf8(&formatted).unwrap();
991        let expected = r#"{
992    name: "my_component",
993    composite_name: "my_composite",
994    program: {
995        runner: "elf",
996    },
997    use: [
998        { protocol: "fuchsia.logger.LogSink" },
999    ],
1000}
1001"#;
1002        assert_eq!(formatted_str, expected);
1003    }
1004}