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