cml/types/
environment.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 crate::one_or_many::{OneOrMany, option_one_or_many_as_ref};
6use crate::types::common::*;
7use crate::{
8    AnyRef, AsClause, AsClauseContext, CapabilityClause, ContextPathClause, Error, FromClause,
9    FromClauseContext, OfferFromRef, PathClause, merge_spanned_vec,
10};
11pub use cm_types::{
12    Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
13    OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
14};
15use cml_macro::Reference;
16use json_spanned_value::Spanned;
17use reference_doc::ReferenceDoc;
18use serde::{Deserialize, Serialize, de};
19
20use std::fmt;
21use std::path::PathBuf;
22use std::sync::Arc;
23
24/// Example:
25///
26/// ```json5
27/// environments: [
28///     {
29///         name: "test-env",
30///         extends: "realm",
31///         runners: [
32///             {
33///                 runner: "gtest-runner",
34///                 from: "#gtest",
35///             },
36///         ],
37///         resolvers: [
38///             {
39///                 resolver: "full-resolver",
40///                 from: "parent",
41///                 scheme: "fuchsia-pkg",
42///             },
43///         ],
44///     },
45/// ],
46/// ```
47#[derive(Deserialize, Debug, PartialEq, ReferenceDoc, Serialize)]
48#[serde(deny_unknown_fields)]
49#[reference_doc(fields_as = "list", top_level_doc_after_fields)]
50pub struct Environment {
51    /// The name of the environment, which is a string of one or more of the
52    /// following characters: `a-z`, `0-9`, `_`, `.`, `-`. The name identifies this
53    /// environment when used in a [reference](#references).
54    pub name: Name,
55
56    /// How the environment should extend this realm's environment.
57    /// - `realm`: Inherit all properties from this component's environment.
58    /// - `none`: Start with an empty environment, do not inherit anything.
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub extends: Option<EnvironmentExtends>,
61
62    /// The runners registered in the environment. An array of objects
63    /// with the following properties:
64    #[reference_doc(recurse)]
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub runners: Option<Vec<RunnerRegistration>>,
67
68    /// The resolvers registered in the environment. An array of
69    /// objects with the following properties:
70    #[reference_doc(recurse)]
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub resolvers: Option<Vec<ResolverRegistration>>,
73
74    /// Debug protocols available to any component in this environment acquired
75    /// through `use from debug`.
76    #[reference_doc(recurse)]
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub debug: Option<Vec<DebugRegistration>>,
79
80    /// The number of milliseconds to wait, after notifying a component in this environment that it
81    /// should terminate, before forcibly killing it. This field is required if the environment
82    /// extends from `none`.
83    #[serde(rename = "__stop_timeout_ms")]
84    #[reference_doc(json_type = "number", rename = "__stop_timeout_ms")]
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub stop_timeout_ms: Option<StopTimeoutMs>,
87}
88
89impl Environment {
90    pub fn merge_from(&mut self, other: &mut Self) -> Result<(), Error> {
91        if self.extends.is_none() {
92            self.extends = other.extends.take();
93        } else if other.extends.is_some() && other.extends != self.extends {
94            return Err(Error::validate(
95                "cannot merge `environments` that declare conflicting `extends`",
96            ));
97        }
98
99        if self.stop_timeout_ms.is_none() {
100            self.stop_timeout_ms = other.stop_timeout_ms;
101        } else if other.stop_timeout_ms.is_some() && other.stop_timeout_ms != self.stop_timeout_ms {
102            return Err(Error::validate(
103                "cannot merge `environments` that declare conflicting `stop_timeout_ms`",
104            ));
105        }
106
107        // Perform naive vector concatenation and rely on later validation to ensure
108        // no conflicting entries.
109        match &mut self.runners {
110            Some(r) => {
111                if let Some(o) = &mut other.runners {
112                    r.append(o);
113                }
114            }
115            None => self.runners = other.runners.take(),
116        }
117
118        match &mut self.resolvers {
119            Some(r) => {
120                if let Some(o) = &mut other.resolvers {
121                    r.append(o);
122                }
123            }
124            None => self.resolvers = other.resolvers.take(),
125        }
126
127        match &mut self.debug {
128            Some(r) => {
129                if let Some(o) = &mut other.debug {
130                    r.append(o);
131                }
132            }
133            None => self.debug = other.debug.take(),
134        }
135        Ok(())
136    }
137}
138
139/// A reference in an environment.
140#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
141#[reference(expected = "\"#<environment-name>\"")]
142pub enum EnvironmentRef {
143    /// A reference to an environment defined in this component.
144    Named(Name),
145}
146
147#[derive(Deserialize, Debug, PartialEq, Serialize)]
148#[serde(rename_all = "lowercase")]
149pub enum EnvironmentExtends {
150    Realm,
151    None,
152}
153
154/// The stop timeout configured in an environment.
155#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
156pub struct StopTimeoutMs(pub u32);
157
158impl<'de> de::Deserialize<'de> for StopTimeoutMs {
159    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
160    where
161        D: de::Deserializer<'de>,
162    {
163        struct Visitor;
164
165        impl<'de> de::Visitor<'de> for Visitor {
166            type Value = StopTimeoutMs;
167
168            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169                f.write_str("an unsigned 32-bit integer")
170            }
171
172            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
173            where
174                E: de::Error,
175            {
176                if v < 0 || v > i64::from(u32::max_value()) {
177                    return Err(E::invalid_value(
178                        de::Unexpected::Signed(v),
179                        &"an unsigned 32-bit integer",
180                    ));
181                }
182                Ok(StopTimeoutMs(v as u32))
183            }
184
185            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
186            where
187                E: de::Error,
188            {
189                self.visit_i64(value as i64)
190            }
191        }
192
193        deserializer.deserialize_i64(Visitor)
194    }
195}
196
197/// A reference in an environment registration.
198#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
199#[reference(expected = "\"parent\", \"self\", or \"#<child-name>\"")]
200pub enum RegistrationRef {
201    /// A reference to a child.
202    Named(Name),
203    /// A reference to the parent.
204    Parent,
205    /// A reference to this component.
206    Self_,
207}
208
209#[derive(Deserialize, Debug, PartialEq, ReferenceDoc, Serialize)]
210#[serde(deny_unknown_fields)]
211#[reference_doc(fields_as = "list")]
212pub struct RunnerRegistration {
213    /// The [name](#name) of a runner capability, whose source is specified in `from`.
214    pub runner: Name,
215
216    /// The source of the runner capability, one of:
217    /// - `parent`: The component's parent.
218    /// - `self`: This component.
219    /// - `#<child-name>`: A [reference](#references) to a child component
220    ///     instance.
221    pub from: RegistrationRef,
222
223    /// An explicit name for the runner as it will be known in
224    /// this environment. If omitted, defaults to `runner`.
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub r#as: Option<Name>,
227}
228
229#[derive(Deserialize, Debug, PartialEq)]
230#[serde(deny_unknown_fields)]
231pub struct ParsedRunnerRegistration {
232    pub runner: Spanned<Name>,
233    pub from: Spanned<RegistrationRef>,
234    pub r#as: Option<Spanned<Name>>,
235}
236
237impl Hydrate for ParsedRunnerRegistration {
238    type Output = ContextRunnerRegistration;
239
240    fn hydrate(self, file: &Arc<PathBuf>, buffer: &String) -> Result<Self::Output, Error> {
241        let runner = hydrate_simple(self.runner, file, buffer);
242
243        let r#as = hydrate_opt_simple(self.r#as, file, buffer);
244
245        let from = hydrate_simple(self.from, file, buffer);
246
247        Ok(ContextRunnerRegistration { runner, r#as, from })
248    }
249}
250
251#[derive(Debug, PartialEq)]
252pub struct ContextRunnerRegistration {
253    pub runner: ContextSpanned<Name>,
254    pub from: ContextSpanned<RegistrationRef>,
255    pub r#as: Option<ContextSpanned<Name>>,
256}
257
258impl FromClauseContext for ContextRunnerRegistration {
259    fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>> {
260        let origin = self.from.origin.clone();
261        let value = OneOrMany::One(AnyRef::from(&self.from.value));
262
263        ContextSpanned { value, origin }
264    }
265}
266
267impl FromClause for RunnerRegistration {
268    fn from_(&self) -> OneOrMany<AnyRef<'_>> {
269        OneOrMany::One(AnyRef::from(&self.from))
270    }
271}
272
273#[derive(Deserialize, Debug, PartialEq, ReferenceDoc, Serialize)]
274#[serde(deny_unknown_fields)]
275#[reference_doc(fields_as = "list")]
276pub struct ResolverRegistration {
277    /// The [name](#name) of a resolver capability,
278    /// whose source is specified in `from`.
279    pub resolver: Name,
280
281    /// The source of the resolver capability, one of:
282    /// - `parent`: The component's parent.
283    /// - `self`: This component.
284    /// - `#<child-name>`: A [reference](#references) to a child component
285    ///     instance.
286    pub from: RegistrationRef,
287
288    /// The URL scheme for which the resolver should handle
289    /// resolution.
290    pub scheme: cm_types::UrlScheme,
291}
292
293#[derive(Deserialize, Debug, PartialEq)]
294#[serde(deny_unknown_fields)]
295pub struct ParsedResolverRegistration {
296    pub resolver: Spanned<Name>,
297    pub from: Spanned<RegistrationRef>,
298    pub scheme: Spanned<cm_types::UrlScheme>,
299}
300
301impl Hydrate for ParsedResolverRegistration {
302    type Output = ContextResolverRegistration;
303
304    fn hydrate(self, file: &Arc<PathBuf>, buffer: &String) -> Result<Self::Output, Error> {
305        let resolver = hydrate_simple(self.resolver, file, buffer);
306
307        let from = hydrate_simple(self.from, file, buffer);
308        let scheme = hydrate_simple(self.scheme, file, buffer);
309
310        Ok(ContextResolverRegistration { resolver, from, scheme })
311    }
312}
313
314#[derive(Debug, PartialEq, Serialize)]
315pub struct ContextResolverRegistration {
316    pub resolver: ContextSpanned<Name>,
317    pub from: ContextSpanned<RegistrationRef>,
318    pub scheme: ContextSpanned<cm_types::UrlScheme>,
319}
320
321impl FromClause for ResolverRegistration {
322    fn from_(&self) -> OneOrMany<AnyRef<'_>> {
323        OneOrMany::One(AnyRef::from(&self.from))
324    }
325}
326
327impl FromClauseContext for ContextResolverRegistration {
328    fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>> {
329        let origin = self.from.origin.clone();
330        let value = OneOrMany::One(AnyRef::from(&self.from.value));
331
332        ContextSpanned { value, origin }
333    }
334}
335
336#[derive(Deserialize, Debug, Clone, PartialEq, ReferenceDoc, Serialize)]
337#[serde(deny_unknown_fields)]
338#[reference_doc(fields_as = "list")]
339pub struct DebugRegistration {
340    /// The name(s) of the protocol(s) to make available.
341    pub protocol: Option<OneOrMany<Name>>,
342
343    /// The source of the capability(s), one of:
344    /// - `parent`: The component's parent.
345    /// - `self`: This component.
346    /// - `#<child-name>`: A [reference](#references) to a child component
347    ///     instance.
348    pub from: OfferFromRef,
349
350    /// If specified, the name that the capability in `protocol` should be made
351    /// available as to clients. Disallowed if `protocol` is an array.
352    #[serde(skip_serializing_if = "Option::is_none")]
353    pub r#as: Option<Name>,
354}
355
356#[derive(Deserialize, Debug, Clone, PartialEq)]
357pub struct ParsedDebugRegistration {
358    pub protocol: Option<Spanned<OneOrMany<Name>>>,
359    pub from: Spanned<OfferFromRef>,
360    pub r#as: Option<Spanned<Name>>,
361}
362
363impl Hydrate for ParsedDebugRegistration {
364    type Output = ContextDebugRegistration;
365
366    fn hydrate(self, file: &Arc<PathBuf>, buffer: &String) -> Result<Self::Output, Error> {
367        let origin = Origin::synthetic(file.clone().to_path_buf());
368        let protocol = hydrate_opt_simple(self.protocol, file, buffer);
369        let from = hydrate_simple(self.from, file, buffer);
370        let r#as = hydrate_opt_simple(self.r#as, file, buffer);
371
372        Ok(ContextDebugRegistration { origin, protocol, from, r#as })
373    }
374}
375
376#[derive(Debug, Clone, PartialEq)]
377pub struct ContextDebugRegistration {
378    pub origin: Origin,
379    pub protocol: Option<ContextSpanned<OneOrMany<Name>>>,
380    pub from: ContextSpanned<OfferFromRef>,
381    pub r#as: Option<ContextSpanned<Name>>,
382}
383
384impl FromClauseContext for ContextDebugRegistration {
385    fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>> {
386        let origin = self.from.origin.clone();
387        let value = OneOrMany::One(AnyRef::from(&self.from.value));
388
389        ContextSpanned { value, origin }
390    }
391}
392
393impl AsClauseContext for ContextDebugRegistration {
394    fn r#as(&self) -> Option<ContextSpanned<&BorrowedName>> {
395        self.r#as.as_ref().map(|spanned_name| ContextSpanned {
396            value: spanned_name.value.as_ref(),
397            origin: spanned_name.origin.clone(),
398        })
399    }
400}
401
402impl ContextPathClause for ContextDebugRegistration {
403    fn path(&self) -> Option<&ContextSpanned<Path>> {
404        None
405    }
406}
407
408impl ContextCapabilityClause for ContextDebugRegistration {
409    fn service(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
410        None
411    }
412    fn protocol(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
413        option_one_or_many_as_ref_context(&self.protocol)
414    }
415    fn directory(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
416        None
417    }
418    fn storage(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
419        None
420    }
421    fn runner(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
422        None
423    }
424    fn resolver(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
425        None
426    }
427    fn event_stream(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
428        None
429    }
430    fn dictionary(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
431        None
432    }
433    fn config(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
434        None
435    }
436
437    fn decl_type(&self) -> &'static str {
438        "debug"
439    }
440    fn supported(&self) -> &[&'static str] {
441        &["service", "protocol"]
442    }
443    fn are_many_names_allowed(&self) -> bool {
444        ["protocol"].contains(&self.capability_type(None).unwrap())
445    }
446
447    fn set_service(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
448    fn set_protocol(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
449        self.protocol = o;
450    }
451    fn set_directory(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
452    fn set_storage(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
453    fn set_runner(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
454    fn set_resolver(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
455    fn set_event_stream(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
456    fn set_dictionary(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
457    fn set_config(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
458
459    fn origin(&self) -> &Origin {
460        &self.origin
461    }
462
463    fn file_path(&self) -> PathBuf {
464        (*self.origin.file).clone()
465    }
466}
467
468impl AsClause for DebugRegistration {
469    fn r#as(&self) -> Option<&BorrowedName> {
470        self.r#as.as_ref().map(Name::as_ref)
471    }
472}
473
474impl PathClause for DebugRegistration {
475    fn path(&self) -> Option<&Path> {
476        None
477    }
478}
479
480impl FromClause for DebugRegistration {
481    fn from_(&self) -> OneOrMany<AnyRef<'_>> {
482        OneOrMany::One(AnyRef::from(&self.from))
483    }
484}
485
486impl CapabilityClause for DebugRegistration {
487    fn service(&self) -> Option<OneOrMany<&BorrowedName>> {
488        None
489    }
490    fn protocol(&self) -> Option<OneOrMany<&BorrowedName>> {
491        option_one_or_many_as_ref(&self.protocol)
492    }
493    fn directory(&self) -> Option<OneOrMany<&BorrowedName>> {
494        None
495    }
496    fn storage(&self) -> Option<OneOrMany<&BorrowedName>> {
497        None
498    }
499    fn runner(&self) -> Option<OneOrMany<&BorrowedName>> {
500        None
501    }
502    fn resolver(&self) -> Option<OneOrMany<&BorrowedName>> {
503        None
504    }
505    fn event_stream(&self) -> Option<OneOrMany<&BorrowedName>> {
506        None
507    }
508    fn dictionary(&self) -> Option<OneOrMany<&BorrowedName>> {
509        None
510    }
511    fn config(&self) -> Option<OneOrMany<&BorrowedName>> {
512        None
513    }
514
515    fn set_service(&mut self, _o: Option<OneOrMany<Name>>) {}
516    fn set_protocol(&mut self, o: Option<OneOrMany<Name>>) {
517        self.protocol = o;
518    }
519    fn set_directory(&mut self, _o: Option<OneOrMany<Name>>) {}
520    fn set_storage(&mut self, _o: Option<OneOrMany<Name>>) {}
521    fn set_runner(&mut self, _o: Option<OneOrMany<Name>>) {}
522    fn set_resolver(&mut self, _o: Option<OneOrMany<Name>>) {}
523    fn set_event_stream(&mut self, _o: Option<OneOrMany<Name>>) {}
524    fn set_dictionary(&mut self, _o: Option<OneOrMany<Name>>) {}
525    fn set_config(&mut self, _o: Option<OneOrMany<Name>>) {}
526
527    fn availability(&self) -> Option<Availability> {
528        None
529    }
530    fn set_availability(&mut self, _a: Option<Availability>) {}
531
532    fn decl_type(&self) -> &'static str {
533        "debug"
534    }
535    fn supported(&self) -> &[&'static str] {
536        &["service", "protocol"]
537    }
538    fn are_many_names_allowed(&self) -> bool {
539        ["protocol"].contains(&self.capability_type().unwrap())
540    }
541}
542
543#[derive(Deserialize, Debug, PartialEq)]
544#[serde(deny_unknown_fields)]
545pub struct ParsedEnvironment {
546    pub name: Spanned<Name>,
547    pub extends: Option<Spanned<EnvironmentExtends>>,
548    pub runners: Option<Spanned<Vec<Spanned<ParsedRunnerRegistration>>>>,
549    pub resolvers: Option<Spanned<Vec<Spanned<ParsedResolverRegistration>>>>,
550    pub debug: Option<Spanned<Vec<Spanned<ParsedDebugRegistration>>>>,
551    #[serde(rename = "__stop_timeout_ms")]
552    pub stop_timeout_ms: Option<Spanned<StopTimeoutMs>>,
553}
554
555impl Hydrate for ParsedEnvironment {
556    type Output = ContextEnvironment;
557
558    fn hydrate(self, file: &Arc<PathBuf>, buffer: &String) -> Result<Self::Output, Error> {
559        let name = hydrate_simple(self.name, file, buffer);
560
561        let extends = hydrate_opt_simple(self.extends, file, buffer);
562        let stop_timeout_ms = hydrate_opt_simple(self.stop_timeout_ms, file, buffer);
563
564        let runners = hydrate_list(self.runners, file, buffer)?;
565        let resolvers = hydrate_list(self.resolvers, file, buffer)?;
566        let debug = hydrate_list(self.debug, file, buffer)?;
567
568        Ok(ContextEnvironment { name, extends, runners, resolvers, debug, stop_timeout_ms })
569    }
570}
571
572#[derive(Debug, PartialEq)]
573pub struct ContextEnvironment {
574    pub name: ContextSpanned<Name>,
575    pub extends: Option<ContextSpanned<EnvironmentExtends>>,
576    pub runners: Option<Vec<ContextSpanned<ContextRunnerRegistration>>>,
577    pub resolvers: Option<Vec<ContextSpanned<ContextResolverRegistration>>>,
578    pub debug: Option<Vec<ContextSpanned<ContextDebugRegistration>>>,
579    pub stop_timeout_ms: Option<ContextSpanned<StopTimeoutMs>>,
580}
581
582impl ContextEnvironment {
583    pub fn merge_from(&mut self, mut other: Self) -> Result<(), Error> {
584        if let Some(other_extends) = other.extends.take() {
585            if let Some(my_extends) = &self.extends {
586                if my_extends.value != other_extends.value {
587                    return Err(Error::merge(
588                        format!(
589                            "Conflicting 'extends' field in environment '{}': found '{:?}' and '{:?}'",
590                            self.name.value, my_extends.value, other_extends.value
591                        ),
592                        Some(other_extends.origin),
593                    ));
594                }
595            } else {
596                self.extends = Some(other_extends);
597            }
598        }
599
600        if let Some(other_timeout) = other.stop_timeout_ms.take() {
601            if let Some(my_timeout) = &self.stop_timeout_ms {
602                if my_timeout.value != other_timeout.value {
603                    return Err(Error::merge(
604                        format!(
605                            "Conflicting 'stop_timeout_ms' in environment '{}'",
606                            self.name.value
607                        ),
608                        Some(other_timeout.origin),
609                    ));
610                }
611            } else {
612                self.stop_timeout_ms = Some(other_timeout);
613            }
614        }
615
616        merge_spanned_vec!(self, other, runners);
617        merge_spanned_vec!(self, other, resolvers);
618        merge_spanned_vec!(self, other, debug);
619
620        Ok(())
621    }
622}