cml/types/
common.rs

1// Copyright 2025 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
5use std::path::PathBuf;
6use std::sync::Arc;
7
8use crate::{Path, byte_index_to_location};
9use json_spanned_value::Spanned;
10
11use crate::error::{Error, Location};
12use cm_types::BorrowedName;
13
14use crate::OneOrMany;
15
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct Origin {
18    pub file: Arc<PathBuf>,
19    pub location: Location,
20}
21
22#[derive(Debug, Clone, PartialEq)]
23pub struct ContextSpanned<T> {
24    pub value: T,
25    pub origin: Origin,
26}
27
28/// Hydrate is used to translate a json_spanned::Spanned type to a
29/// ContextSpanned type. The ContextSpanned type is used by validation.
30pub trait Hydrate {
31    type Output;
32
33    fn hydrate(self, file: &Arc<PathBuf>, buffer: &String) -> Self::Output;
34}
35
36pub fn hydrate_list<P, C>(
37    raw_list: Option<Spanned<Vec<Spanned<P>>>>,
38    file: &Arc<PathBuf>,
39    buffer: &String,
40) -> Option<Vec<ContextSpanned<C>>>
41where
42    P: Hydrate<Output = C>,
43{
44    raw_list.map(|spanned_vec| {
45        spanned_vec
46            .into_inner()
47            .into_iter()
48            .map(|spanned_item| {
49                let span = spanned_item.span();
50                let location = byte_index_to_location(buffer, span.0);
51                let parsed_item = spanned_item.into_inner();
52
53                let context_item = parsed_item.hydrate(file, buffer);
54
55                ContextSpanned {
56                    value: context_item,
57                    origin: Origin { file: file.clone(), location },
58                }
59            })
60            .collect()
61    })
62}
63
64pub fn hydrate_required<P, C>(
65    spanned: Spanned<P>,
66    file: &Arc<PathBuf>,
67    buffer: &String,
68) -> ContextSpanned<C>
69where
70    P: Hydrate<Output = C>,
71{
72    let span = spanned.span();
73    let location = byte_index_to_location(buffer, span.0);
74    let parsed_value = spanned.into_inner();
75    ContextSpanned {
76        value: parsed_value.hydrate(file, buffer),
77        origin: Origin { file: file.clone(), location },
78    }
79}
80
81pub fn hydrate_simple<T>(
82    spanned: Spanned<T>,
83    file: &Arc<PathBuf>,
84    buffer: &String,
85) -> ContextSpanned<T> {
86    let span = spanned.span();
87    let location = byte_index_to_location(buffer, span.0);
88    ContextSpanned { value: spanned.into_inner(), origin: Origin { file: file.clone(), location } }
89}
90
91pub fn hydrate_opt<P, C>(
92    opt_spanned: Option<Spanned<P>>,
93    file: &Arc<PathBuf>,
94    buffer: &String,
95) -> Option<ContextSpanned<C>>
96where
97    P: Hydrate<Output = C>,
98{
99    opt_spanned.map(|s| hydrate_required(s, file, buffer))
100}
101
102pub fn hydrate_opt_simple<T>(
103    opt_spanned: Option<Spanned<T>>,
104    file: &Arc<PathBuf>,
105    buffer: &String,
106) -> Option<ContextSpanned<T>> {
107    opt_spanned.map(|s| hydrate_simple(s, file, buffer))
108}
109
110pub fn option_one_or_many_as_ref_context<T, S: ?Sized>(
111    o: &Option<ContextSpanned<OneOrMany<T>>>,
112) -> Option<ContextSpanned<OneOrMany<&S>>>
113where
114    T: AsRef<S>,
115{
116    o.as_ref().map(|spanned| ContextSpanned {
117        origin: spanned.origin.clone(),
118        value: match &spanned.value {
119            OneOrMany::One(item) => OneOrMany::One(item.as_ref()),
120            OneOrMany::Many(items) => {
121                OneOrMany::Many(items.iter().map(|item| item.as_ref()).collect())
122            }
123        },
124    })
125}
126
127pub trait ContextPathClause {
128    fn path(&self) -> Option<&ContextSpanned<Path>>;
129}
130
131pub trait ContextCapabilityClause {
132    fn service(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
133    fn protocol(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
134    fn directory(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
135    fn storage(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
136    fn runner(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
137    fn resolver(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
138    fn dictionary(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
139    fn config(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
140    fn event_stream(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
141
142    // /// Returns the name of the capability for display purposes.
143    // /// If `service()` returns `Some`, the capability name must be "service", etc.
144    // ///
145    // /// Returns an error if the capability name is not set, or if there is more than one.
146    fn capability_type(&self, origin: Option<Origin>) -> Result<&'static str, Error> {
147        let mut types = Vec::new();
148        if self.service().is_some() {
149            types.push("service");
150        }
151        if self.protocol().is_some() {
152            types.push("protocol");
153        }
154        if self.directory().is_some() {
155            types.push("directory");
156        }
157        if self.storage().is_some() {
158            types.push("storage");
159        }
160        if self.event_stream().is_some() {
161            types.push("event_stream");
162        }
163        if self.runner().is_some() {
164            types.push("runner");
165        }
166        if self.config().is_some() {
167            types.push("config");
168        }
169        if self.resolver().is_some() {
170            types.push("resolver");
171        }
172        if self.dictionary().is_some() {
173            types.push("dictionary");
174        }
175        match types.len() {
176            0 => {
177                let supported_keywords = self
178                    .supported()
179                    .into_iter()
180                    .map(|k| format!("\"{}\"", k))
181                    .collect::<Vec<_>>()
182                    .join(", ");
183                Err(Error::validate_context(
184                    format!(
185                        "`{}` declaration is missing a capability keyword, one of: {}",
186                        self.decl_type(),
187                        supported_keywords,
188                    ),
189                    origin,
190                ))
191            }
192            1 => Ok(types[0]),
193            _ => Err(Error::validate_context(
194                format!(
195                    "{} declaration has multiple capability types defined: {:?}",
196                    self.decl_type(),
197                    types
198                ),
199                origin,
200            )),
201        }
202    }
203
204    /// Returns the names of the capabilities in this clause.
205    /// If `protocol()` returns `Some(OneOrMany::Many(vec!["a", "b"]))`, this returns!["a", "b"].
206    fn names(&self) -> Vec<&BorrowedName> {
207        let res = vec![
208            self.service(),
209            self.protocol(),
210            self.directory(),
211            self.storage(),
212            self.runner(),
213            self.config(),
214            self.resolver(),
215            self.event_stream(),
216            self.dictionary(),
217        ];
218        res.into_iter()
219            .map(|o| {
220                o.map(|o| o.value.into_iter().collect::<Vec<&BorrowedName>>()).unwrap_or(vec![])
221            })
222            .flatten()
223            .collect()
224    }
225
226    /// Returns true if this capability type allows the ::Many variant of OneOrMany.
227    fn are_many_names_allowed(&self) -> bool;
228
229    fn decl_type(&self) -> &'static str;
230    fn supported(&self) -> &[&'static str];
231}