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