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