Skip to main content

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::hash::{Hash, Hasher};
6use std::path::PathBuf;
7use std::sync::Arc;
8
9use crate::Path;
10
11use crate::error::Error;
12use crate::{CanonicalizeContext, OneOrMany};
13use cm_types::{Availability, BorrowedName, Name};
14use serde::Serialize;
15
16#[derive(Debug, Clone, PartialOrd, Ord, Serialize)]
17#[serde(transparent)]
18pub struct ContextSpanned<T> {
19    pub value: T,
20
21    #[serde(skip)]
22    pub origin: Arc<PathBuf>,
23}
24
25impl<T: PartialEq> PartialEq for ContextSpanned<T> {
26    fn eq(&self, other: &Self) -> bool {
27        self.value == other.value
28    }
29}
30
31impl<T: Eq> Eq for ContextSpanned<T> {}
32
33impl<T: Hash> Hash for ContextSpanned<T> {
34    fn hash<H: Hasher>(&self, state: &mut H) {
35        self.value.hash(state);
36    }
37}
38
39impl<T> ContextSpanned<T> {
40    pub fn map<U, F>(self, f: F) -> ContextSpanned<U>
41    where
42        F: FnOnce(T) -> U,
43    {
44        ContextSpanned { value: f(self.value), origin: self.origin }
45    }
46
47    pub fn new_synthetic(value: T, file: PathBuf) -> Self {
48        Self { value, origin: Arc::new(file) }
49    }
50
51    pub fn maybe_synthetic(val: Option<T>, file: PathBuf) -> Option<Self> {
52        val.map(|v| Self::new_synthetic(v, file))
53    }
54}
55
56impl<T: CanonicalizeContext> CanonicalizeContext for ContextSpanned<T> {
57    fn canonicalize_context(&mut self) {
58        self.value.canonicalize_context();
59    }
60}
61
62impl<T: ContextPathClause> ContextPathClause for ContextSpanned<T> {
63    fn path(&self) -> Option<&ContextSpanned<Path>> {
64        self.value.path()
65    }
66}
67
68/// Helper to wrap programmatic values in a ContextSpanned wrapper.
69pub fn synthetic_span<T>(value: T) -> ContextSpanned<T> {
70    ContextSpanned { value, origin: Arc::new(PathBuf::from("programmatic_manifest.cml")) }
71}
72
73/// Hydrate is used to translate a type to a
74/// Result<ContextSpanned> type. The ContextSpanned type is used by validation.
75///
76/// It is possible to error when merging if a field is defined as multiple,
77/// incompatible data structures.
78pub trait Hydrate {
79    type Output;
80
81    fn hydrate(self, file: &Arc<PathBuf>) -> Result<Self::Output, Error>;
82}
83
84pub fn hydrate_list<P, C>(
85    raw_list: Option<Vec<P>>,
86    file: &Arc<PathBuf>,
87) -> Result<Option<Vec<ContextSpanned<C>>>, Error>
88where
89    P: Hydrate<Output = C>,
90{
91    raw_list
92        .map(|vec| {
93            vec.into_iter()
94                .map(|item| {
95                    let context_item_result = item.hydrate(file);
96                    context_item_result
97                        .map(|c_value| ContextSpanned { value: c_value, origin: file.clone() })
98                })
99                .collect::<Result<Vec<ContextSpanned<C>>, Error>>()
100        })
101        .transpose()
102}
103
104pub fn hydrate_required<P, C>(
105    parsed_value: P,
106    file: &Arc<PathBuf>,
107) -> Result<ContextSpanned<C>, Error>
108where
109    P: Hydrate<Output = C>,
110{
111    let context_value = parsed_value.hydrate(file)?;
112
113    Ok(ContextSpanned { value: context_value, origin: file.clone() })
114}
115
116pub fn hydrate_simple<T>(value: T, file: &Arc<PathBuf>) -> ContextSpanned<T> {
117    ContextSpanned { value, origin: file.clone() }
118}
119
120pub fn hydrate_opt<P, C>(
121    opt: Option<P>,
122    file: &Arc<PathBuf>,
123) -> Result<Option<ContextSpanned<C>>, Error>
124where
125    P: Hydrate<Output = C>,
126{
127    opt.map(|s| hydrate_required(s, file)).transpose()
128}
129
130pub fn hydrate_opt_simple<T>(
131    opt_spanned: Option<T>,
132    file: &Arc<PathBuf>,
133) -> Option<ContextSpanned<T>> {
134    opt_spanned.map(|s| hydrate_simple(s, file))
135}
136
137pub fn option_one_or_many_as_ref_context<T, S: ?Sized>(
138    o: &Option<ContextSpanned<OneOrMany<T>>>,
139) -> Option<ContextSpanned<OneOrMany<&S>>>
140where
141    T: AsRef<S>,
142{
143    o.as_ref().map(|spanned| ContextSpanned {
144        origin: spanned.origin.clone(),
145        value: match &spanned.value {
146            OneOrMany::One(item) => OneOrMany::One(item.as_ref()),
147            OneOrMany::Many(items) => {
148                OneOrMany::Many(items.iter().map(|item| item.as_ref()).collect())
149            }
150        },
151    })
152}
153
154pub trait ContextPathClause {
155    fn path(&self) -> Option<&ContextSpanned<Path>>;
156}
157
158pub trait ContextCapabilityClause: Clone + PartialEq + std::fmt::Debug {
159    fn service(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
160    fn protocol(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
161    fn directory(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
162    fn storage(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
163    fn runner(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
164    fn resolver(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
165    fn dictionary(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
166    fn config(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
167    fn event_stream(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>>;
168    fn origin(&self) -> &Arc<PathBuf>;
169    fn availability(&self) -> Option<ContextSpanned<Availability>>;
170
171    fn set_availability(&mut self, a: Option<ContextSpanned<Availability>>);
172    fn set_service(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>);
173    fn set_protocol(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>);
174    fn set_directory(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>);
175    fn set_storage(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>);
176    fn set_runner(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>);
177    fn set_resolver(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>);
178    fn set_event_stream(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>);
179    fn set_dictionary(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>);
180    fn set_config(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>);
181
182    // /// Returns the name of the capability for display purposes.
183    // /// If `service()` returns `Some`, the capability name must be "service", etc.
184    // ///
185    // /// Returns an error if the capability name is not set, or if there is more than one.
186    fn capability_type(&self, origin: Option<Arc<PathBuf>>) -> Result<&'static str, Error> {
187        let mut types = Vec::new();
188        if self.service().is_some() {
189            types.push("service");
190        }
191        if self.protocol().is_some() {
192            types.push("protocol");
193        }
194        if self.directory().is_some() {
195            types.push("directory");
196        }
197        if self.storage().is_some() {
198            types.push("storage");
199        }
200        if self.event_stream().is_some() {
201            types.push("event_stream");
202        }
203        if self.runner().is_some() {
204            types.push("runner");
205        }
206        if self.config().is_some() {
207            types.push("config");
208        }
209        if self.resolver().is_some() {
210            types.push("resolver");
211        }
212        if self.dictionary().is_some() {
213            types.push("dictionary");
214        }
215        match types.len() {
216            0 => {
217                let supported_keywords = self
218                    .supported()
219                    .iter()
220                    .map(|k| format!("\"{}\"", k))
221                    .collect::<Vec<_>>()
222                    .join(", ");
223                Err(Error::validate_context(
224                    format!(
225                        "`{}` declaration is missing a capability keyword, one of: {}",
226                        self.decl_type(),
227                        supported_keywords,
228                    ),
229                    origin,
230                ))
231            }
232            1 => Ok(types[0]),
233            _ => Err(Error::validate_context(
234                format!(
235                    "{} declaration has multiple capability types defined: {:?}",
236                    self.decl_type(),
237                    types
238                ),
239                origin,
240            )),
241        }
242    }
243
244    /// Returns the names of the capabilities in this clause, wrapped in ContextSpanned.
245    /// This allows the caller to know the file source of every individual name.
246    fn names(&self) -> Vec<ContextSpanned<Name>> {
247        let extract = |field: Option<&ContextSpanned<OneOrMany<&BorrowedName>>>| -> Vec<ContextSpanned<Name>> {
248        match field {
249                    Some(wrapper) => match &wrapper.value {
250                        // n is &&BorrowedName. We deref once to get &BorrowedName,
251                        // then .to_owned() converts it to Name.
252                        OneOrMany::One(n) => vec![ContextSpanned {
253                            value: (*n).to_owned(),
254                            origin: wrapper.origin.clone(),
255                        }],
256                        OneOrMany::Many(names) => names
257                            .iter()
258                            .map(|n| ContextSpanned {
259                                value: (*n).to_owned(),
260                                origin: wrapper.origin.clone(),
261                            })
262                            .collect(),
263                    },
264                    None => vec![],
265                }
266            };
267        // Collect names from all possible fields
268        let mut res = Vec::new();
269        res.extend(extract(self.service().as_ref()));
270        res.extend(extract(self.protocol().as_ref()));
271        res.extend(extract(self.directory().as_ref()));
272        res.extend(extract(self.storage().as_ref()));
273        res.extend(extract(self.runner().as_ref()));
274        res.extend(extract(self.config().as_ref()));
275        res.extend(extract(self.resolver().as_ref()));
276        res.extend(extract(self.event_stream().as_ref()));
277        res.extend(extract(self.dictionary().as_ref()));
278        res
279    }
280
281    /// Sets the names for this capability, preserving origin information.
282    fn set_names(&mut self, names: Vec<ContextSpanned<Name>>) {
283        let cap_type = self.capability_type(None).expect("Cannot set names on empty capability");
284
285        let mut update_field = |wrapped_val: Option<ContextSpanned<OneOrMany<Name>>>| match cap_type
286        {
287            "protocol" => self.set_protocol(wrapped_val),
288            "service" => self.set_service(wrapped_val),
289            "directory" => self.set_directory(wrapped_val),
290            "storage" => self.set_storage(wrapped_val),
291            "runner" => self.set_runner(wrapped_val),
292            "resolver" => self.set_resolver(wrapped_val),
293            "event_stream" => self.set_event_stream(wrapped_val),
294            "dictionary" => self.set_dictionary(wrapped_val),
295            "config" => self.set_config(wrapped_val),
296            _ => panic!("Unknown capability type {}", cap_type),
297        };
298
299        if names.is_empty() {
300            update_field(None);
301            return;
302        }
303
304        let first_origin = names[0].origin.clone();
305
306        let raw_names: Vec<Name> = names.into_iter().map(|n| n.value).collect();
307        let one_or_many = if raw_names.len() == 1 {
308            OneOrMany::One(raw_names.into_iter().next().unwrap())
309        } else {
310            OneOrMany::Many(raw_names)
311        };
312
313        let wrapped = Some(ContextSpanned { value: one_or_many, origin: first_origin });
314
315        update_field(wrapped);
316    }
317
318    /// Returns true if this capability type allows the ::Many variant of OneOrMany.
319    fn are_many_names_allowed(&self) -> bool;
320
321    fn decl_type(&self) -> &'static str;
322    fn supported(&self) -> &[&'static str];
323}
324
325impl<T: ContextCapabilityClause> ContextCapabilityClause for ContextSpanned<T> {
326    fn service(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
327        self.value.service()
328    }
329    fn protocol(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
330        self.value.protocol()
331    }
332    fn directory(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
333        self.value.directory()
334    }
335    fn storage(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
336        self.value.storage()
337    }
338    fn runner(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
339        self.value.runner()
340    }
341    fn resolver(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
342        self.value.resolver()
343    }
344    fn dictionary(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
345        self.value.dictionary()
346    }
347    fn config(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
348        self.value.config()
349    }
350    fn event_stream(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
351        self.value.event_stream()
352    }
353
354    fn origin(&self) -> &Arc<PathBuf> {
355        &self.origin
356    }
357
358    fn set_service(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
359        self.value.set_service(o)
360    }
361    fn set_protocol(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
362        self.value.set_protocol(o)
363    }
364    fn set_directory(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
365        self.value.set_directory(o)
366    }
367    fn set_storage(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
368        self.value.set_storage(o)
369    }
370    fn set_runner(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
371        self.value.set_runner(o)
372    }
373    fn set_resolver(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
374        self.value.set_resolver(o)
375    }
376    fn set_event_stream(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
377        self.value.set_event_stream(o)
378    }
379    fn set_dictionary(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
380        self.value.set_dictionary(o)
381    }
382    fn set_config(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
383        self.value.set_config(o)
384    }
385
386    fn are_many_names_allowed(&self) -> bool {
387        self.value.are_many_names_allowed()
388    }
389    fn decl_type(&self) -> &'static str {
390        self.value.decl_type()
391    }
392    fn supported(&self) -> &[&'static str] {
393        self.value.supported()
394    }
395
396    fn availability(&self) -> Option<ContextSpanned<Availability>> {
397        self.value.availability()
398    }
399    fn set_availability(&mut self, a: Option<ContextSpanned<Availability>>) {
400        self.value.set_availability(a)
401    }
402}
403
404#[macro_export]
405macro_rules! merge_spanned_vec {
406    ($self:expr, $other:expr, $field:ident) => {
407        if let Some(other_vec) = $other.$field.take() {
408            if let Some(self_vec) = $self.$field.as_mut() {
409                for item in other_vec {
410                    if !self_vec.contains(&item) {
411                        self_vec.push(item);
412                    }
413                }
414            } else {
415                $self.$field = Some(other_vec);
416            }
417        }
418    };
419}