cml/types/
document.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 indexmap::IndexMap;
6use itertools::Itertools;
7
8use crate::types::capability::{ContextCapability, ParsedCapability};
9use crate::types::child::{ContextChild, ParsedChild};
10use crate::types::collection::{ContextCollection, ParsedCollection};
11use crate::types::common::*;
12use crate::types::environment::{ContextEnvironment, ParsedEnvironment};
13use crate::types::expose::{ContextExpose, ParsedExpose};
14use crate::types::offer::{ContextOffer, ParsedOffer};
15use crate::types::program::{ContextProgram, ParsedProgram};
16use crate::types::r#use::{ContextUse, ParsedUse};
17use crate::{
18    Canonicalize, Capability, CapabilityClause, CapabilityFromRef, Child, Collection, ConfigKey,
19    ConfigValueType, Environment, Error, Expose, Location, Offer, Program, Use, merge_spanned_vec,
20};
21
22pub use cm_types::{
23    Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
24    OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
25};
26use json_spanned_value::Spanned;
27use reference_doc::ReferenceDoc;
28use serde::{Deserialize, Serialize};
29use serde_json::{Map, Value};
30
31use std::collections::{BTreeMap, HashMap, HashSet};
32use std::path::PathBuf;
33use std::sync::Arc;
34use std::{cmp, path};
35
36/// # Component manifest (`.cml`) reference
37///
38/// A `.cml` file contains a single json5 object literal with the keys below.
39///
40/// Where string values are expected, a list of valid values is generally documented.
41/// The following string value types are reused and must follow specific rules.
42///
43/// The `.cml` file is compiled into a FIDL wire format (`.cm`) file.
44///
45/// ## String types
46///
47/// ### Names {#names}
48///
49/// Both capabilities and a component's children are named. A name string may
50/// consist of one or more of the following characters: `A-Z`, `a-z`, `0-9`,
51/// `_`, `.`, `-`. It must not exceed 255 characters in length and may not start
52/// with `.` or `-`.
53///
54/// ### Paths {#paths}
55///
56/// Paths are sequences of [names](#names) delimited by the `/` character. A path
57/// must not exceed 4095 characters in length. Throughout the document,
58///
59/// - Relative paths cannot start with the `/` character.
60/// - Namespace and outgoing directory paths must start with the `/` character.
61///
62/// ### References {#references}
63///
64/// A reference string takes the form of `#<name>`, where `<name>` refers to the name of a child:
65///
66/// - A [static child instance][doc-static-children] whose name is
67///     `<name>`, or
68/// - A [collection][doc-collections] whose name is `<name>`.
69///
70/// [doc-static-children]: /docs/concepts/components/v2/realms.md#static-children
71/// [doc-collections]: /docs/concepts/components/v2/realms.md#collections
72/// [doc-protocol]: /docs/concepts/components/v2/capabilities/protocol.md
73/// [doc-dictionaries]: /reference/fidl/fuchsia.component.decl#Dictionary
74/// [doc-directory]: /docs/concepts/components/v2/capabilities/directory.md
75/// [doc-storage]: /docs/concepts/components/v2/capabilities/storage.md
76/// [doc-resolvers]: /docs/concepts/components/v2/capabilities/resolver.md
77/// [doc-runners]: /docs/concepts/components/v2/capabilities/runner.md
78/// [doc-event]: /docs/concepts/components/v2/capabilities/event.md
79/// [doc-service]: /docs/concepts/components/v2/capabilities/service.md
80/// [doc-directory-rights]: /docs/concepts/components/v2/capabilities/directory.md#directory-capability-rights
81///
82/// ## Top-level keys {#document}
83#[derive(ReferenceDoc, Deserialize, Debug, Default, PartialEq, Serialize)]
84#[serde(deny_unknown_fields)]
85pub struct Document {
86    /// The optional `include` property describes zero or more other component manifest
87    /// files to be merged into this component manifest. For example:
88    ///
89    /// ```json5
90    /// include: [ "syslog/client.shard.cml" ]
91    /// ```
92    ///
93    /// In the example given above, the component manifest is including contents from a
94    /// manifest shard provided by the `syslog` library, thus ensuring that the
95    /// component functions correctly at runtime if it attempts to write to `syslog`. By
96    /// convention such files are called "manifest shards" and end with `.shard.cml`.
97    ///
98    /// Include paths prepended with `//` are relative to the source root of the Fuchsia
99    /// checkout. However, include paths not prepended with `//`, as in the example
100    /// above, are resolved from Fuchsia SDK libraries (`//sdk/lib`) that export
101    /// component manifest shards.
102    ///
103    /// For reference, inside the Fuchsia checkout these two include paths are
104    /// equivalent:
105    ///
106    /// * `syslog/client.shard.cml`
107    /// * `//sdk/lib/syslog/client.shard.cml`
108    ///
109    /// You can review the outcome of merging any and all includes into a component
110    /// manifest file by invoking the following command:
111    ///
112    /// Note: The `fx` command below is for developers working in a Fuchsia source
113    /// checkout environment.
114    ///
115    /// ```sh
116    /// fx cmc include {{ "<var>" }}cml_file{{ "</var>" }} --includeroot $FUCHSIA_DIR --includepath $FUCHSIA_DIR/sdk/lib
117    /// ```
118    ///
119    /// Includes can cope with duplicate [`use`], [`offer`], [`expose`], or [`capabilities`]
120    /// declarations referencing the same capability, as long as the properties are the same. For
121    /// example:
122    ///
123    /// ```json5
124    /// // my_component.cml
125    /// include: [ "syslog.client.shard.cml" ]
126    /// use: [
127    ///     {
128    ///         protocol: [
129    ///             "fuchsia.posix.socket.Provider",
130    ///             "fuchsia.logger.LogSink",
131    ///         ],
132    ///     },
133    /// ],
134    ///
135    /// // syslog.client.shard.cml
136    /// use: [
137    ///     { protocol: "fuchsia.logger.LogSink" },
138    /// ],
139    /// ```
140    ///
141    /// In this example, the contents of the merged file will be the same as my_component.cml --
142    /// `fuchsia.logger.LogSink` is deduped.
143    ///
144    /// However, this would fail to compile:
145    ///
146    /// ```json5
147    /// // my_component.cml
148    /// include: [ "syslog.client.shard.cml" ]
149    /// use: [
150    ///     {
151    ///         protocol: "fuchsia.logger.LogSink",
152    ///         // properties for fuchsia.logger.LogSink don't match
153    ///         from: "#archivist",
154    ///     },
155    /// ],
156    ///
157    /// // syslog.client.shard.cml
158    /// use: [
159    ///     { protocol: "fuchsia.logger.LogSink" },
160    /// ],
161    /// ```
162    ///
163    /// An exception to this constraint is the `availability` property. If two routing declarations
164    /// are identical, and one availability is stronger than the other, the availability will be
165    /// "promoted" to the stronger value (if `availability` is missing, it defaults to `required`).
166    /// For example:
167    ///
168    /// ```json5
169    /// // my_component.cml
170    /// include: [ "syslog.client.shard.cml" ]
171    /// use: [
172    ///     {
173    ///         protocol: [
174    ///             "fuchsia.posix.socket.Provider",
175    ///             "fuchsia.logger.LogSink",
176    ///         ],
177    ///         availability: "optional",
178    ///     },
179    /// ],
180    ///
181    /// // syslog.client.shard.cml
182    /// use: [
183    ///     {
184    ///         protocol: "fuchsia.logger.LogSink
185    ///         availability: "required",  // This is the default
186    ///     },
187    /// ],
188    /// ```
189    ///
190    /// Becomes:
191    ///
192    /// ```json5
193    /// use: [
194    ///     {
195    ///         protocol: "fuchsia.posix.socket.Provider",
196    ///         availability: "optional",
197    ///     },
198    ///     {
199    ///         protocol: "fuchsia.logger.LogSink",
200    ///         availability: "required",
201    ///     },
202    /// ],
203    /// ```
204    ///
205    /// Includes are transitive, meaning that shards can have their own includes.
206    ///
207    /// Include paths can have diamond dependencies. For instance this is valid:
208    /// A includes B, A includes C, B includes D, C includes D.
209    /// In this case A will transitively include B, C, D.
210    ///
211    /// Include paths cannot have cycles. For instance this is invalid:
212    /// A includes B, B includes A.
213    /// A cycle such as the above will result in a compile-time error.
214    ///
215    /// [`use`]: #use
216    /// [`offer`]: #offer
217    /// [`expose`]: #expose
218    /// [`capabilities`]: #capabilities
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub include: Option<Vec<String>>,
221
222    /// Components that are executable include a `program` section. The `program`
223    /// section must set the `runner` property to select a [runner][doc-runners] to run
224    /// the component. The format of the rest of the `program` section is determined by
225    /// that particular runner.
226    ///
227    /// # ELF runners {#elf-runners}
228    ///
229    /// If the component uses the ELF runner, `program` must include the following
230    /// properties, at a minimum:
231    ///
232    /// - `runner`: must be set to `"elf"`
233    /// - `binary`: Package-relative path to the executable binary
234    /// - `args` _(optional)_: List of arguments
235    ///
236    /// Example:
237    ///
238    /// ```json5
239    /// program: {
240    ///     runner: "elf",
241    ///     binary: "bin/hippo",
242    ///     args: [ "Hello", "hippos!" ],
243    /// },
244    /// ```
245    ///
246    /// For a complete list of properties, see: [ELF Runner](/docs/concepts/components/v2/elf_runner.md)
247    ///
248    /// # Other runners {#other-runners}
249    ///
250    /// If a component uses a custom runner, values inside the `program` stanza other
251    /// than `runner` are specific to the runner. The runner receives the arguments as a
252    /// dictionary of key and value pairs. Refer to the specific runner being used to
253    /// determine what keys it expects to receive, and how it interprets them.
254    ///
255    /// [doc-runners]: /docs/concepts/components/v2/capabilities/runner.md
256    #[reference_doc(json_type = "object")]
257    #[serde(skip_serializing_if = "Option::is_none")]
258    pub program: Option<Program>,
259
260    /// The `children` section declares child component instances as described in
261    /// [Child component instances][doc-children].
262    ///
263    /// [doc-children]: /docs/concepts/components/v2/realms.md#child-component-instances
264    #[reference_doc(recurse)]
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub children: Option<Vec<Child>>,
267
268    /// The `collections` section declares collections as described in
269    /// [Component collections][doc-collections].
270    #[reference_doc(recurse)]
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub collections: Option<Vec<Collection>>,
273
274    /// The `environments` section declares environments as described in
275    /// [Environments][doc-environments].
276    ///
277    /// [doc-environments]: /docs/concepts/components/v2/environments.md
278    #[reference_doc(recurse)]
279    #[serde(skip_serializing_if = "Option::is_none")]
280    pub environments: Option<Vec<Environment>>,
281
282    /// The `capabilities` section defines capabilities that are provided by this component.
283    /// Capabilities that are [offered](#offer) or [exposed](#expose) from `self` must be declared
284    /// here.
285    ///
286    /// # Capability fields
287    ///
288    /// This supports the following capability keys. Exactly one of these must be set:
289    ///
290    /// - `protocol`: (_optional `string or array of strings`_)
291    /// - `service`: (_optional `string or array of strings`_)
292    /// - `directory`: (_optional `string`_)
293    /// - `storage`: (_optional `string`_)
294    /// - `runner`: (_optional `string`_)
295    /// - `resolver`: (_optional `string`_)
296    /// - `event_stream`: (_optional `string or array of strings`_)
297    /// - `dictionary`: (_optional `string`_)
298    /// - `config`: (_optional `string`_)
299    ///
300    /// # Additional fields
301    ///
302    /// This supports the following additional fields:
303    /// [glossary.outgoing directory]: /docs/glossary/README.md#outgoing-directory
304    #[reference_doc(recurse)]
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub capabilities: Option<Vec<Capability>>,
307
308    /// For executable components, declares capabilities that this
309    /// component requires in its [namespace][glossary.namespace] at runtime.
310    /// Capabilities are routed from the `parent` unless otherwise specified,
311    /// and each capability must have a valid route through all components between
312    /// this component and the capability's source.
313    ///
314    /// # Capability fields
315    ///
316    /// This supports the following capability keys. Exactly one of these must be set:
317    ///
318    /// - `service`: (_optional `string or array of strings`_)
319    /// - `directory`: (_optional `string`_)
320    /// - `protocol`: (_optional `string or array of strings`_)
321    /// - `dictionary`: (_optional `string`_)
322    /// - `storage`: (_optional `string`_)
323    /// - `event_stream`: (_optional `string or array of strings`_)
324    /// - `runner`: (_optional `string`_)
325    /// - `config`: (_optional `string`_)
326    ///
327    /// # Additional fields
328    ///
329    /// This supports the following additional fields:
330    /// [glossary.namespace]: /docs/glossary/README.md#namespace
331    #[reference_doc(recurse)]
332    #[serde(skip_serializing_if = "Option::is_none")]
333    pub r#use: Option<Vec<Use>>,
334
335    /// Declares the capabilities that are made available to the parent component or to the
336    /// framework. It is valid to `expose` from `self` or from a child component.
337    ///
338    /// # Capability fields
339    ///
340    /// This supports the following capability keys. Exactly one of these must be set:
341    ///
342    /// - `service`: (_optional `string or array of strings`_)
343    /// - `protocol`: (_optional `string or array of strings`_)
344    /// - `directory`: (_optional `string`_)
345    /// - `runner`: (_optional `string`_)
346    /// - `resolver`: (_optional `string`_)
347    /// - `dictionary`: (_optional `string`_)
348    /// - `config`: (_optional `string`_)
349    ///
350    /// # Additional fields
351    ///
352    /// This supports the following additional fields:
353    #[reference_doc(recurse)]
354    #[serde(skip_serializing_if = "Option::is_none")]
355    pub expose: Option<Vec<Expose>>,
356
357    /// Declares the capabilities that are made available to a [child component][doc-children]
358    /// instance or a [child collection][doc-collections].
359    ///
360    /// # Capability fields
361    ///
362    /// This supports the following capability keys. Exactly one of these must be set:
363    ///
364    /// - `protocol`: (_optional `string or array of strings`_)
365    /// - `service`: (_optional `string or array of strings`_)
366    /// - `directory`: (_optional `string`_)
367    /// - `storage`: (_optional `string`_)
368    /// - `runner`: (_optional `string`_)
369    /// - `resolver`: (_optional `string`_)
370    /// - `event_stream`: (_optional `string or array of strings`_)
371    /// - `dictionary`: (_optional `string`_)
372    /// - `config`: (_optional `string`_)
373    ///
374    /// # Additional fields
375    ///
376    /// This supports the following additional fields:
377    #[reference_doc(recurse)]
378    #[serde(skip_serializing_if = "Option::is_none")]
379    pub offer: Option<Vec<Offer>>,
380
381    /// Contains metadata that components may interpret for their own purposes. The component
382    /// framework enforces no schema for this section, but third parties may expect their facets to
383    /// adhere to a particular schema.
384    #[serde(skip_serializing_if = "Option::is_none")]
385    pub facets: Option<IndexMap<String, Value>>,
386
387    /// The configuration schema as defined by a component. Each key represents a single field
388    /// in the schema.
389    ///
390    /// Configuration fields are JSON objects and must define a `type` which can be one of the
391    /// following strings:
392    /// `bool`, `uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`,
393    /// `string`, `vector`
394    ///
395    /// Example:
396    ///
397    /// ```json5
398    /// config: {
399    ///     debug_mode: {
400    ///         type: "bool"
401    ///     },
402    /// }
403    /// ```
404    ///
405    /// Fields are resolved from a component's package by default. To be able to change the values
406    /// at runtime a `mutability` specifier is required.
407    ///
408    /// Example:
409    ///
410    /// ```json5
411    /// config: {
412    ///     verbose: {
413    ///         type: "bool",
414    ///         mutability: [ "parent" ],
415    ///     },
416    /// },
417    /// ```
418    ///
419    /// Currently `"parent"` is the only mutability specifier supported.
420    ///
421    /// Strings must define the `max_size` property as a non-zero integer.
422    ///
423    /// Example:
424    ///
425    /// ```json5
426    /// config: {
427    ///     verbosity: {
428    ///         type: "string",
429    ///         max_size: 20,
430    ///     }
431    /// }
432    /// ```
433    ///
434    /// Vectors must set the `max_count` property as a non-zero integer. Vectors must also set the
435    /// `element` property as a JSON object which describes the element being contained in the
436    /// vector. Vectors can contain booleans, integers, and strings but cannot contain other
437    /// vectors.
438    ///
439    /// Example:
440    ///
441    /// ```json5
442    /// config: {
443    ///     tags: {
444    ///         type: "vector",
445    ///         max_count: 20,
446    ///         element: {
447    ///             type: "string",
448    ///             max_size: 50,
449    ///         }
450    ///     }
451    /// }
452    /// ```
453    #[reference_doc(json_type = "object")]
454    #[serde(skip_serializing_if = "Option::is_none")]
455    // NB: Unlike other maps the order of these fields matters for the ABI of generated config
456    // libraries. Rather than insertion order, we explicitly sort the fields here to dissuade
457    // developers from taking a dependency on the source ordering in their manifest. In the future
458    // this will hopefully make it easier to pursue layout size optimizations.
459    pub config: Option<BTreeMap<ConfigKey, ConfigValueType>>,
460}
461
462impl Document {
463    pub fn merge_from(
464        &mut self,
465        other: &mut Document,
466        include_path: &path::Path,
467    ) -> Result<(), Error> {
468        // Flatten the mergeable fields that may contain a
469        // list of capabilities in one clause.
470        merge_from_capability_field(&mut self.r#use, &mut other.r#use)?;
471        merge_from_capability_field(&mut self.expose, &mut other.expose)?;
472        merge_from_capability_field(&mut self.offer, &mut other.offer)?;
473        merge_from_capability_field(&mut self.capabilities, &mut other.capabilities)?;
474        merge_from_other_field(&mut self.include, &mut other.include);
475        merge_from_other_field(&mut self.children, &mut other.children);
476        merge_from_other_field(&mut self.collections, &mut other.collections);
477        self.merge_environment(other, include_path)?;
478        self.merge_program(other, include_path)?;
479        self.merge_facets(other, include_path)?;
480        self.merge_config(other, include_path)?;
481
482        Ok(())
483    }
484
485    pub fn canonicalize(&mut self) {
486        // Don't sort `include` - the order there matters.
487        if let Some(children) = &mut self.children {
488            children.sort_by(|a, b| a.name.cmp(&b.name));
489        }
490        if let Some(collections) = &mut self.collections {
491            collections.sort_by(|a, b| a.name.cmp(&b.name));
492        }
493        if let Some(environments) = &mut self.environments {
494            environments.sort_by(|a, b| a.name.cmp(&b.name));
495        }
496        if let Some(capabilities) = &mut self.capabilities {
497            capabilities.canonicalize();
498        }
499        if let Some(offers) = &mut self.offer {
500            offers.canonicalize();
501        }
502        if let Some(expose) = &mut self.expose {
503            expose.canonicalize();
504        }
505        if let Some(r#use) = &mut self.r#use {
506            r#use.canonicalize();
507        }
508    }
509
510    fn merge_program(
511        &mut self,
512        other: &mut Document,
513        include_path: &path::Path,
514    ) -> Result<(), Error> {
515        if let None = other.program {
516            return Ok(());
517        }
518        if let None = self.program {
519            self.program = Some(Program::default());
520        }
521        let my_program = self.program.as_mut().unwrap();
522        let other_program = other.program.as_mut().unwrap();
523        if let Some(other_runner) = other_program.runner.take() {
524            my_program.runner = match &my_program.runner {
525                Some(runner) if *runner != other_runner => {
526                    return Err(Error::validate(format!(
527                        "manifest include had a conflicting `program.runner`: {}",
528                        include_path.display()
529                    )));
530                }
531                _ => Some(other_runner),
532            }
533        }
534
535        Self::merge_maps_with_options(
536            &mut my_program.info,
537            &other_program.info,
538            "program",
539            include_path,
540            Some(vec!["environ", "features"]),
541        )
542    }
543
544    fn merge_environment(
545        &mut self,
546        other: &mut Document,
547        _include_path: &path::Path,
548    ) -> Result<(), Error> {
549        if let None = other.environments {
550            return Ok(());
551        }
552        if let None = self.environments {
553            self.environments = Some(vec![]);
554        }
555
556        let my_environments = self.environments.as_mut().unwrap();
557        let other_environments = other.environments.as_mut().unwrap();
558        my_environments.sort_by(|x, y| x.name.cmp(&y.name));
559        other_environments.sort_by(|x, y| x.name.cmp(&y.name));
560
561        let all_environments =
562            my_environments.into_iter().merge_by(other_environments, |x, y| x.name <= y.name);
563        let groups = all_environments.chunk_by(|e| e.name.clone());
564
565        let mut merged_environments = vec![];
566        for (name, group) in groups.into_iter() {
567            let mut merged_environment = Environment {
568                name: name.clone(),
569                extends: None,
570                runners: None,
571                resolvers: None,
572                debug: None,
573                stop_timeout_ms: None,
574            };
575            for e in group {
576                merged_environment.merge_from(e)?;
577            }
578            merged_environments.push(merged_environment);
579        }
580
581        self.environments = Some(merged_environments);
582        Ok(())
583    }
584
585    fn merge_maps<'s, Source, Dest>(
586        self_map: &mut Dest,
587        include_map: Source,
588        outer_key: &str,
589        include_path: &path::Path,
590    ) -> Result<(), Error>
591    where
592        Source: IntoIterator<Item = (&'s String, &'s Value)>,
593        Dest: ValueMap,
594    {
595        Self::merge_maps_with_options(self_map, include_map, outer_key, include_path, None)
596    }
597
598    /// If `allow_array_concatenation_keys` is None, all arrays present in both
599    /// `self_map` and `include_map` will be concatenated in the result. If it
600    /// is set to Some(vec), only those keys specified will allow concatenation,
601    /// with any others returning an error.
602    fn merge_maps_with_options<'s, Source, Dest>(
603        self_map: &mut Dest,
604        include_map: Source,
605        outer_key: &str,
606        include_path: &path::Path,
607        allow_array_concatenation_keys: Option<Vec<&str>>,
608    ) -> Result<(), Error>
609    where
610        Source: IntoIterator<Item = (&'s String, &'s Value)>,
611        Dest: ValueMap,
612    {
613        for (key, value) in include_map {
614            match self_map.get_mut(key) {
615                None => {
616                    // Key not present in self map, insert it from include map.
617                    self_map.insert(key.clone(), value.clone());
618                }
619                // Self and include maps share the same key
620                Some(Value::Object(self_nested_map)) => match value {
621                    // The include value is an object and can be recursively merged
622                    Value::Object(include_nested_map) => {
623                        let combined_key = format!("{}.{}", outer_key, key);
624
625                        // Recursively merge maps
626                        Self::merge_maps(
627                            self_nested_map,
628                            include_nested_map,
629                            &combined_key,
630                            include_path,
631                        )?;
632                    }
633                    _ => {
634                        // Cannot merge object and non-object
635                        return Err(Error::validate(format!(
636                            "manifest include had a conflicting `{}.{}`: {}",
637                            outer_key,
638                            key,
639                            include_path.display()
640                        )));
641                    }
642                },
643                Some(Value::Array(self_nested_vec)) => match value {
644                    // The include value is an array and can be merged, unless
645                    // `allow_array_concatenation_keys` is used and the key is not included.
646                    Value::Array(include_nested_vec) => {
647                        if let Some(allowed_keys) = &allow_array_concatenation_keys {
648                            if !allowed_keys.contains(&key.as_str()) {
649                                // This key wasn't present in `allow_array_concatenation_keys` and so
650                                // merging is disallowed.
651                                return Err(Error::validate(format!(
652                                    "manifest include had a conflicting `{}.{}`: {}",
653                                    outer_key,
654                                    key,
655                                    include_path.display()
656                                )));
657                            }
658                        }
659                        let mut new_values = include_nested_vec.clone();
660                        self_nested_vec.append(&mut new_values);
661                    }
662                    _ => {
663                        // Cannot merge array and non-array
664                        return Err(Error::validate(format!(
665                            "manifest include had a conflicting `{}.{}`: {}",
666                            outer_key,
667                            key,
668                            include_path.display()
669                        )));
670                    }
671                },
672                _ => {
673                    // Cannot merge object and non-object
674                    return Err(Error::validate(format!(
675                        "manifest include had a conflicting `{}.{}`: {}",
676                        outer_key,
677                        key,
678                        include_path.display()
679                    )));
680                }
681            }
682        }
683        Ok(())
684    }
685
686    fn merge_facets(
687        &mut self,
688        other: &mut Document,
689        include_path: &path::Path,
690    ) -> Result<(), Error> {
691        if let None = other.facets {
692            return Ok(());
693        }
694        if let None = self.facets {
695            self.facets = Some(Default::default());
696        }
697        let my_facets = self.facets.as_mut().unwrap();
698        let other_facets = other.facets.as_ref().unwrap();
699
700        Self::merge_maps(my_facets, other_facets, "facets", include_path)
701    }
702
703    fn merge_config(
704        &mut self,
705        other: &mut Document,
706        include_path: &path::Path,
707    ) -> Result<(), Error> {
708        if let Some(other_config) = other.config.as_mut() {
709            if let Some(self_config) = self.config.as_mut() {
710                for (key, field) in other_config {
711                    match self_config.entry(key.clone()) {
712                        std::collections::btree_map::Entry::Vacant(v) => {
713                            v.insert(field.clone());
714                        }
715                        std::collections::btree_map::Entry::Occupied(o) => {
716                            if o.get() != field {
717                                let msg = format!(
718                                    "Found conflicting entry for config key `{key}` in `{}`.",
719                                    include_path.display()
720                                );
721                                return Err(Error::validate(&msg));
722                            }
723                        }
724                    }
725                }
726            } else {
727                self.config.replace(std::mem::take(other_config));
728            }
729        }
730        Ok(())
731    }
732
733    pub fn includes(&self) -> Vec<String> {
734        self.include.clone().unwrap_or_default()
735    }
736
737    pub fn all_children_names(&self) -> Vec<&BorrowedName> {
738        if let Some(children) = self.children.as_ref() {
739            children.iter().map(|c| c.name.as_ref()).collect()
740        } else {
741            vec![]
742        }
743    }
744
745    pub fn all_collection_names(&self) -> Vec<&BorrowedName> {
746        if let Some(collections) = self.collections.as_ref() {
747            collections.iter().map(|c| c.name.as_ref()).collect()
748        } else {
749            vec![]
750        }
751    }
752
753    pub fn all_storage_names(&self) -> Vec<&BorrowedName> {
754        if let Some(capabilities) = self.capabilities.as_ref() {
755            capabilities.iter().filter_map(|c| c.storage.as_ref().map(|n| n.as_ref())).collect()
756        } else {
757            vec![]
758        }
759    }
760
761    pub fn all_storage_with_sources<'a>(
762        &'a self,
763    ) -> HashMap<&'a BorrowedName, &'a CapabilityFromRef> {
764        if let Some(capabilities) = self.capabilities.as_ref() {
765            capabilities
766                .iter()
767                .filter_map(|c| match (c.storage.as_ref().map(Name::as_ref), c.from.as_ref()) {
768                    (Some(s), Some(f)) => Some((s, f)),
769                    _ => None,
770                })
771                .collect()
772        } else {
773            HashMap::new()
774        }
775    }
776
777    pub fn all_service_names(&self) -> Vec<&BorrowedName> {
778        self.capabilities
779            .as_ref()
780            .map(|c| {
781                c.iter()
782                    .filter_map(|c| c.service.as_ref().map(|o| o.as_ref()))
783                    .map(|p| p.into_iter())
784                    .flatten()
785                    .collect()
786            })
787            .unwrap_or_else(|| vec![])
788    }
789
790    pub fn all_protocol_names(&self) -> Vec<&BorrowedName> {
791        self.capabilities
792            .as_ref()
793            .map(|c| {
794                c.iter()
795                    .filter_map(|c| c.protocol.as_ref().map(|o| o.as_ref()))
796                    .map(|p| p.into_iter())
797                    .flatten()
798                    .collect()
799            })
800            .unwrap_or_else(|| vec![])
801    }
802
803    pub fn all_directory_names(&self) -> Vec<&BorrowedName> {
804        self.capabilities
805            .as_ref()
806            .map(|c| c.iter().filter_map(|c| c.directory.as_ref().map(Name::as_ref)).collect())
807            .unwrap_or_else(|| vec![])
808    }
809
810    pub fn all_runner_names(&self) -> Vec<&BorrowedName> {
811        self.capabilities
812            .as_ref()
813            .map(|c| c.iter().filter_map(|c| c.runner.as_ref().map(Name::as_ref)).collect())
814            .unwrap_or_else(|| vec![])
815    }
816
817    pub fn all_resolver_names(&self) -> Vec<&BorrowedName> {
818        self.capabilities
819            .as_ref()
820            .map(|c| c.iter().filter_map(|c| c.resolver.as_ref().map(Name::as_ref)).collect())
821            .unwrap_or_else(|| vec![])
822    }
823
824    pub fn all_dictionary_names(&self) -> Vec<&BorrowedName> {
825        if let Some(capabilities) = self.capabilities.as_ref() {
826            capabilities.iter().filter_map(|c| c.dictionary.as_ref().map(Name::as_ref)).collect()
827        } else {
828            vec![]
829        }
830    }
831
832    pub fn all_dictionaries<'a>(&'a self) -> HashMap<&'a BorrowedName, &'a Capability> {
833        if let Some(capabilities) = self.capabilities.as_ref() {
834            capabilities
835                .iter()
836                .filter_map(|c| match c.dictionary.as_ref().map(Name::as_ref) {
837                    Some(s) => Some((s, c)),
838                    _ => None,
839                })
840                .collect()
841        } else {
842            HashMap::new()
843        }
844    }
845
846    pub fn all_config_names(&self) -> Vec<&BorrowedName> {
847        self.capabilities
848            .as_ref()
849            .map(|c| c.iter().filter_map(|c| c.config.as_ref().map(Name::as_ref)).collect())
850            .unwrap_or_else(|| vec![])
851    }
852
853    pub fn all_environment_names(&self) -> Vec<&BorrowedName> {
854        self.environments
855            .as_ref()
856            .map(|c| c.iter().map(|s| s.name.as_ref()).collect())
857            .unwrap_or_else(|| vec![])
858    }
859
860    pub fn all_capability_names(&self) -> HashSet<&BorrowedName> {
861        self.capabilities
862            .as_ref()
863            .map(|c| {
864                c.iter().fold(HashSet::new(), |mut acc, capability| {
865                    acc.extend(capability.names());
866                    acc
867                })
868            })
869            .unwrap_or_default()
870    }
871}
872
873/// Merges `us` into `other` according to the rules documented for [`include`].
874/// [`include`]: #include
875fn merge_from_capability_field<T: CapabilityClause>(
876    us: &mut Option<Vec<T>>,
877    other: &mut Option<Vec<T>>,
878) -> Result<(), Error> {
879    // Empty entries are an error, and merging removes empty entries so we first need to check
880    // for them.
881    for entry in us.iter().flatten().chain(other.iter().flatten()) {
882        if entry.names().is_empty() {
883            return Err(Error::Validate {
884                err: format!("{}: Missing type name: {:#?}", entry.decl_type(), entry),
885                filename: None,
886            });
887        }
888    }
889
890    if let Some(all_ours) = us.as_mut() {
891        if let Some(all_theirs) = other.take() {
892            for mut theirs in all_theirs {
893                for ours in &mut *all_ours {
894                    compute_diff(ours, &mut theirs);
895                }
896                all_ours.push(theirs);
897            }
898        }
899        // Post-filter step: remove empty entries.
900        all_ours.retain(|ours| !ours.names().is_empty())
901    } else if let Some(theirs) = other.take() {
902        us.replace(theirs);
903    }
904    Ok(())
905}
906
907/// Merges `us` into `other` according to the rules documented for [`include`].
908/// [`include`]: #include
909fn merge_from_other_field<T: std::cmp::PartialEq>(
910    us: &mut Option<Vec<T>>,
911    other: &mut Option<Vec<T>>,
912) {
913    if let Some(ours) = us {
914        if let Some(theirs) = other.take() {
915            // Add their elements, ignoring dupes with ours
916            for t in theirs {
917                if !ours.contains(&t) {
918                    ours.push(t);
919                }
920            }
921        }
922    } else if let Some(theirs) = other.take() {
923        us.replace(theirs);
924    }
925}
926
927/// Subtracts the capabilities in `ours` from `theirs` if the declarations match in their type and
928/// other fields, resulting in the removal of duplicates between `ours` and `theirs`. Stores the
929/// result in `theirs`.
930///
931/// Inexact matches on `availability` are allowed if there is a partial order between them. The
932/// stronger availability is chosen.
933fn compute_diff<T: CapabilityClause>(ours: &mut T, theirs: &mut T) {
934    // Return early if one is empty.
935    if ours.names().is_empty() || theirs.names().is_empty() {
936        return;
937    }
938
939    // Return early if the types don't match.
940    if ours.capability_type().unwrap() != theirs.capability_type().unwrap() {
941        return;
942    }
943
944    // Check if the non-capability fields match before proceeding.
945    let mut ours_partial = ours.clone();
946    let mut theirs_partial = theirs.clone();
947    for e in [&mut ours_partial, &mut theirs_partial] {
948        e.set_names(Vec::new());
949        // Availability is allowed to differ (see merge algorithm below)
950        e.set_availability(None);
951    }
952    if ours_partial != theirs_partial {
953        // The fields other than `availability` do not match, nothing to remove.
954        return;
955    }
956
957    // Compare the availabilities.
958    let Some(avail_cmp) = ours
959        .availability()
960        .unwrap_or_default()
961        .partial_cmp(&theirs.availability().unwrap_or_default())
962    else {
963        // The availabilities are incompatible (no partial order).
964        return;
965    };
966
967    let mut our_names: Vec<Name> = ours.names().into_iter().map(Into::into).collect();
968    let mut their_names: Vec<Name> = theirs.names().into_iter().map(Into::into).collect();
969
970    let mut our_entries_to_remove = HashSet::new();
971    let mut their_entries_to_remove = HashSet::new();
972    for e in &their_names {
973        if !our_names.contains(e) {
974            // Not a duplicate, so keep.
975            continue;
976        }
977        match avail_cmp {
978            cmp::Ordering::Less => {
979                // Their availability is stronger, meaning theirs should take
980                // priority. Keep `e` in theirs, and remove it from ours.
981                our_entries_to_remove.insert(e.clone());
982            }
983            cmp::Ordering::Greater => {
984                // Our availability is stronger, meaning ours should take
985                // priority. Remove `e` from theirs.
986                their_entries_to_remove.insert(e.clone());
987            }
988            cmp::Ordering::Equal => {
989                // The availabilities are equal, so `e` is a duplicate.
990                their_entries_to_remove.insert(e.clone());
991            }
992        }
993    }
994    our_names.retain(|e| !our_entries_to_remove.contains(e));
995    their_names.retain(|e| !their_entries_to_remove.contains(e));
996
997    ours.set_names(our_names);
998    theirs.set_names(their_names);
999}
1000
1001/// Trait that allows us to merge `serde_json::Map`s into `indexmap::IndexMap`s and vice versa.
1002trait ValueMap {
1003    fn get_mut(&mut self, key: &str) -> Option<&mut Value>;
1004    fn insert(&mut self, key: String, val: Value);
1005}
1006
1007impl ValueMap for Map<String, Value> {
1008    fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
1009        self.get_mut(key)
1010    }
1011
1012    fn insert(&mut self, key: String, val: Value) {
1013        self.insert(key, val);
1014    }
1015}
1016
1017impl ValueMap for IndexMap<String, Value> {
1018    fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
1019        self.get_mut(key)
1020    }
1021
1022    fn insert(&mut self, key: String, val: Value) {
1023        self.insert(key, val);
1024    }
1025}
1026
1027/// # Component manifest (`.cml`) reference
1028///
1029/// A `.cml` file contains a single spanned json5 object literal with the keys below.
1030#[derive(Deserialize, Debug, Default, PartialEq)]
1031#[serde(deny_unknown_fields)]
1032pub struct ParsedDocument {
1033    pub program: Option<Spanned<ParsedProgram>>,
1034    pub children: Option<Spanned<Vec<Spanned<ParsedChild>>>>,
1035    pub collections: Option<Spanned<Vec<Spanned<ParsedCollection>>>>,
1036    pub environments: Option<Spanned<Vec<Spanned<ParsedEnvironment>>>>,
1037    pub capabilities: Option<Spanned<Vec<Spanned<ParsedCapability>>>>,
1038    pub r#use: Option<Spanned<Vec<Spanned<ParsedUse>>>>,
1039    pub expose: Option<Spanned<Vec<Spanned<ParsedExpose>>>>,
1040    pub offer: Option<Spanned<Vec<Spanned<ParsedOffer>>>>,
1041    pub facets: Option<IndexMap<String, Spanned<Value>>>,
1042    pub config: Option<BTreeMap<ConfigKey, Spanned<ConfigValueType>>>,
1043}
1044
1045#[derive(Debug, Default)]
1046pub struct DocumentContext {
1047    pub program: Option<ContextSpanned<ContextProgram>>,
1048    pub children: Option<Vec<ContextSpanned<ContextChild>>>,
1049    pub collections: Option<Vec<ContextSpanned<ContextCollection>>>,
1050    pub environments: Option<Vec<ContextSpanned<ContextEnvironment>>>,
1051    pub capabilities: Option<Vec<ContextSpanned<ContextCapability>>>,
1052    pub r#use: Option<Vec<ContextSpanned<ContextUse>>>,
1053    pub expose: Option<Vec<ContextSpanned<ContextExpose>>>,
1054    pub offer: Option<Vec<ContextSpanned<ContextOffer>>>,
1055    pub facets: Option<IndexMap<String, ContextSpanned<Value>>>,
1056    pub config: Option<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>,
1057}
1058
1059impl DocumentContext {
1060    pub fn merge_from(
1061        &mut self,
1062        mut other: DocumentContext,
1063        include_path: &path::Path,
1064    ) -> Result<(), Error> {
1065        self.merge_program(&mut other, include_path)?;
1066        merge_spanned_vec!(self, other, children);
1067        merge_spanned_vec!(self, other, collections);
1068        self.merge_environment(&mut other)?;
1069        merge_spanned_vec!(self, other, capabilities);
1070        merge_spanned_vec!(self, other, r#use);
1071        merge_spanned_vec!(self, other, expose);
1072        merge_spanned_vec!(self, other, offer);
1073        self.merge_facets(&mut other, include_path)?;
1074        self.merge_config(&mut other)?;
1075        Ok(())
1076    }
1077
1078    pub fn all_storage_with_sources<'a>(
1079        &'a self,
1080    ) -> HashMap<&'a BorrowedName, &'a CapabilityFromRef> {
1081        if let Some(capabilities) = self.capabilities.as_ref() {
1082            capabilities
1083                .iter()
1084                .filter_map(|cap_wrapper| {
1085                    let c = &cap_wrapper.value;
1086
1087                    let storage_span_opt = c.storage.as_ref();
1088                    let source_span_opt = c.from.as_ref();
1089
1090                    match (storage_span_opt, source_span_opt) {
1091                        (Some(s_span), Some(f_span)) => {
1092                            let name_ref: &BorrowedName = s_span.value.as_ref();
1093                            let source_ref: &CapabilityFromRef = &f_span.value;
1094
1095                            Some((name_ref, source_ref))
1096                        }
1097                        _ => None,
1098                    }
1099                })
1100                .collect()
1101        } else {
1102            HashMap::new()
1103        }
1104    }
1105
1106    pub fn all_capability_names(&self) -> HashSet<&BorrowedName> {
1107        self.capabilities
1108            .as_ref()
1109            .map(|c| {
1110                c.iter().fold(HashSet::new(), |mut acc, capability_wrapper| {
1111                    let capability = &capability_wrapper.value;
1112                    acc.extend(capability.names());
1113                    acc
1114                })
1115            })
1116            .unwrap_or_default()
1117    }
1118
1119    pub fn all_collection_names(&self) -> HashSet<&BorrowedName> {
1120        if let Some(collections) = self.collections.as_ref() {
1121            collections.iter().map(|c| c.value.name.value.as_ref()).collect()
1122        } else {
1123            HashSet::new()
1124        }
1125    }
1126
1127    pub fn all_config_names(&self) -> HashSet<&BorrowedName> {
1128        self.capabilities
1129            .as_ref()
1130            .map(|caps| {
1131                caps.iter()
1132                    .filter_map(|cap_wrapper| {
1133                        let cap = &cap_wrapper.value;
1134
1135                        cap.config.as_ref().map(|spanned_key| spanned_key.value.as_ref())
1136                    })
1137                    .collect()
1138            })
1139            .unwrap_or_default()
1140    }
1141
1142    pub fn all_children_names(&self) -> HashSet<&BorrowedName> {
1143        if let Some(children) = self.children.as_ref() {
1144            children.iter().map(|c| c.value.name.value.as_ref()).collect()
1145        } else {
1146            HashSet::new()
1147        }
1148    }
1149
1150    pub fn all_dictionaries<'a>(&'a self) -> HashMap<&'a BorrowedName, &'a ContextCapability> {
1151        if let Some(capabilities) = self.capabilities.as_ref() {
1152            capabilities
1153                .iter()
1154                .filter_map(|cap_wrapper| {
1155                    let cap = &cap_wrapper.value;
1156                    let dict_span_opt = cap.dictionary.as_ref();
1157
1158                    dict_span_opt.and_then(|dict_span| {
1159                        let name_value = &dict_span.value;
1160                        let borrowed_name: &BorrowedName = name_value.as_ref();
1161                        Some((borrowed_name, cap))
1162                    })
1163                })
1164                .collect()
1165        } else {
1166            HashMap::new()
1167        }
1168    }
1169
1170    fn merge_program(
1171        &mut self,
1172        other: &mut DocumentContext,
1173        include_path: &path::Path,
1174    ) -> Result<(), Error> {
1175        if let None = other.program {
1176            return Ok(());
1177        }
1178        if let None = self.program {
1179            let synthetic_origin = Origin {
1180                file: Arc::new(include_path.to_path_buf()),
1181                location: Location { line: 0, column: 0 },
1182            };
1183
1184            self.program =
1185                Some(ContextSpanned { value: ContextProgram::default(), origin: synthetic_origin });
1186        }
1187        let my_program = &mut self.program.as_mut().unwrap().value;
1188        let other_program = &mut other.program.as_mut().unwrap().value;
1189        if let Some(other_runner_wrapper) = other_program.runner.take() {
1190            if let Some(my_runner_wrapper) = my_program.runner.as_ref() {
1191                if my_runner_wrapper.value != other_runner_wrapper.value {
1192                    return Err(Error::merge(
1193                        format!(
1194                            "Manifest include had a conflicting `program.runner`: parent='{}', include='{}'",
1195                            my_runner_wrapper.value, other_runner_wrapper.value
1196                        ),
1197                        Some(other_runner_wrapper.origin),
1198                    ));
1199                }
1200            } else {
1201                my_program.runner = Some(other_runner_wrapper);
1202            }
1203        }
1204        Self::merge_maps_unified(
1205            &mut my_program.info,
1206            &other_program.info,
1207            "program",
1208            include_path,
1209            None,
1210            Some(&vec!["environ", "features"]),
1211        )
1212    }
1213
1214    fn merge_environment(&mut self, other: &mut DocumentContext) -> Result<(), Error> {
1215        if other.environments.is_none() {
1216            return Ok(());
1217        }
1218        if self.environments.is_none() {
1219            self.environments = Some(vec![]);
1220        }
1221
1222        let merged_results = {
1223            let my_environments = self.environments.as_mut().unwrap();
1224            let other_environments = other.environments.as_mut().unwrap();
1225
1226            my_environments.sort_by(|x, y| x.value.name.value.cmp(&y.value.name.value));
1227            other_environments.sort_by(|x, y| x.value.name.value.cmp(&y.value.name.value));
1228
1229            let all_environments =
1230                my_environments.drain(..).merge_by(other_environments.drain(..), |x, y| {
1231                    x.value.name.value <= y.value.name.value
1232                });
1233
1234            let groups = all_environments.chunk_by(|e| e.value.name.value.clone());
1235
1236            let mut results = vec![];
1237            for (_name_value, group) in &groups {
1238                let mut group_iter = group.into_iter();
1239                let first_wrapper = group_iter.next().expect("chunk cannot be empty");
1240                let first_origin = first_wrapper.origin.clone();
1241                let mut merged_inner = first_wrapper.value;
1242
1243                for subsequent in group_iter {
1244                    merged_inner.merge_from(subsequent.value)?;
1245                }
1246
1247                results.push(ContextSpanned { value: merged_inner, origin: first_origin });
1248            }
1249            results
1250        };
1251
1252        self.environments = Some(merged_results);
1253        Ok(())
1254    }
1255
1256    fn merge_facets(
1257        &mut self,
1258        other: &mut DocumentContext,
1259        include_path: &path::Path,
1260    ) -> Result<(), Error> {
1261        if let None = other.facets {
1262            return Ok(());
1263        }
1264        if let None = self.facets {
1265            self.facets = Some(Default::default());
1266        }
1267        let other_facets = other.facets.as_ref().unwrap();
1268
1269        for (key, include_spanned) in other_facets {
1270            let entry_origin = Some(&include_spanned.origin);
1271            let my_facets = self.facets.as_mut().unwrap();
1272
1273            if !my_facets.contains_key(key) {
1274                my_facets.insert(key.clone(), include_spanned.clone());
1275            } else {
1276                let self_spanned = my_facets.get_mut(key).unwrap();
1277                Self::merge_maps_unified(
1278                    self_spanned.value.as_object_mut().unwrap(),
1279                    include_spanned.value.as_object().unwrap(),
1280                    &format!("facets.{}", key),
1281                    include_path,
1282                    entry_origin,
1283                    Some(&vec![]),
1284                )?;
1285            }
1286        }
1287        Ok(())
1288    }
1289
1290    fn merge_config(&mut self, other: &mut DocumentContext) -> Result<(), Error> {
1291        if other.config.is_none() {
1292            return Ok(());
1293        }
1294        if self.config.is_none() {
1295            self.config = Some(BTreeMap::new());
1296        }
1297
1298        let my_config = self.config.as_mut().unwrap();
1299        let other_config = other.config.as_ref().unwrap();
1300
1301        for (key, other_spanned) in other_config {
1302            if let Some(my_spanned) = my_config.get(key) {
1303                if my_spanned.value != other_spanned.value {
1304                    return Err(Error::merge(
1305                        format!("Conflicting configuration key found: '{}'", key),
1306                        Some(other_spanned.origin.clone()),
1307                    ));
1308                }
1309            } else {
1310                my_config.insert(key.clone(), other_spanned.clone());
1311            }
1312        }
1313        Ok(())
1314    }
1315
1316    fn merge_maps_unified<'s, Source, Dest>(
1317        self_map: &mut Dest,
1318        include_map: Source,
1319        outer_key: &str,
1320        include_path: &path::Path,
1321        origin: Option<&Origin>,
1322        allow_array_concatenation_keys: Option<&Vec<&str>>,
1323    ) -> Result<(), Error>
1324    where
1325        Source: IntoIterator<Item = (&'s String, &'s serde_json::Value)>,
1326        Dest: ValueMap,
1327    {
1328        for (key, include_val) in include_map {
1329            match self_map.get_mut(key) {
1330                None => {
1331                    self_map.insert(key.clone(), include_val.clone());
1332                }
1333                Some(self_val) => match (self_val, include_val) {
1334                    (serde_json::Value::Object(s_inner), serde_json::Value::Object(i_inner)) => {
1335                        let combined_key = format!("{}.{}", outer_key, key);
1336                        Self::merge_maps_unified(
1337                            s_inner,
1338                            i_inner,
1339                            &combined_key,
1340                            include_path,
1341                            origin,
1342                            allow_array_concatenation_keys,
1343                        )?;
1344                    }
1345                    (serde_json::Value::Array(s_arr), serde_json::Value::Array(i_arr)) => {
1346                        let is_allowed = allow_array_concatenation_keys
1347                            .map_or(true, |keys| keys.contains(&key.as_str()));
1348
1349                        if is_allowed {
1350                            s_arr.extend(i_arr.clone());
1351                        } else if s_arr != i_arr {
1352                            return Err(Error::merge(
1353                                format!(
1354                                    "Conflicting array values for field \"{}.{}\"",
1355                                    outer_key, key
1356                                ),
1357                                origin.cloned(),
1358                            ));
1359                        }
1360                    }
1361                    (v1, v2) if v1 == v2 => {}
1362                    _ => {
1363                        return Err(Error::merge(
1364                            format!(
1365                                "Manifest include '{}' had a conflicting value for field \"{}.{}\"",
1366                                include_path.display(),
1367                                outer_key,
1368                                key
1369                            ),
1370                            origin.cloned(),
1371                        ));
1372                    }
1373                },
1374            }
1375        }
1376        Ok(())
1377    }
1378}
1379
1380pub fn convert_parsed_to_document(
1381    parsed_doc: ParsedDocument,
1382    file_arc: Arc<PathBuf>,
1383    buffer: &String,
1384) -> Result<DocumentContext, Error> {
1385    let facets = parsed_doc.facets.map(|raw_facets| {
1386        raw_facets
1387            .into_iter()
1388            .map(|(key, spanned_val)| {
1389                let context_val = hydrate_simple(spanned_val, &file_arc, buffer);
1390                (key, context_val)
1391            })
1392            .collect::<IndexMap<String, ContextSpanned<serde_json::Value>>>()
1393    });
1394
1395    let config = parsed_doc.config.map(|raw_config| {
1396        raw_config
1397            .into_iter()
1398            .map(|(key, spanned_val)| {
1399                let context_val = hydrate_simple(spanned_val, &file_arc, buffer);
1400                (key, context_val)
1401            })
1402            .collect::<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>()
1403    });
1404
1405    Ok(DocumentContext {
1406        program: hydrate_opt(parsed_doc.program, &file_arc, buffer)?,
1407        children: hydrate_list(parsed_doc.children, &file_arc, buffer)?,
1408        collections: hydrate_list(parsed_doc.collections, &file_arc, buffer)?,
1409        environments: hydrate_list(parsed_doc.environments, &file_arc, buffer)?,
1410        capabilities: hydrate_list(parsed_doc.capabilities, &file_arc, buffer)?,
1411        r#use: hydrate_list(parsed_doc.r#use, &file_arc, buffer)?,
1412        expose: hydrate_list(parsed_doc.expose, &file_arc, buffer)?,
1413        offer: hydrate_list(parsed_doc.offer, &file_arc, buffer)?,
1414        facets,
1415        config,
1416    })
1417}
1418
1419#[cfg(test)]
1420mod tests {
1421    use super::*;
1422    use crate::OneOrMany;
1423    use crate::types::document::Document;
1424    use crate::types::offer::OfferFromRef;
1425    use crate::types::right::Right;
1426    use difference::Changeset;
1427    use serde_json::{json, to_string_pretty, to_value};
1428    use std::path;
1429    use std::path::Path;
1430    use test_case::test_case;
1431
1432    fn document(contents: serde_json::Value) -> Document {
1433        serde_json5::from_str::<Document>(&contents.to_string()).unwrap()
1434    }
1435
1436    macro_rules! assert_json_eq {
1437        ($a:expr, $e:expr) => {{
1438            if $a != $e {
1439                let expected = to_string_pretty(&$e).unwrap();
1440                let actual = to_string_pretty(&$a).unwrap();
1441                assert_eq!(
1442                    $a,
1443                    $e,
1444                    "JSON actual != expected. Diffs:\n\n{}",
1445                    Changeset::new(&actual, &expected, "\n")
1446                );
1447            }
1448        }};
1449    }
1450
1451    #[test]
1452    fn test_includes() {
1453        assert_eq!(document(json!({})).includes(), Vec::<String>::new());
1454        assert_eq!(document(json!({ "include": []})).includes(), Vec::<String>::new());
1455        assert_eq!(
1456            document(json!({ "include": [ "foo.cml", "bar.cml" ]})).includes(),
1457            vec!["foo.cml", "bar.cml"]
1458        );
1459    }
1460
1461    #[test]
1462    fn test_merge_same_section() {
1463        let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1464        let mut other = document(json!({ "use": [{ "protocol": "bar" }] }));
1465        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1466        let uses = some.r#use.as_ref().unwrap();
1467        assert_eq!(uses.len(), 2);
1468        assert_eq!(
1469            uses[0].protocol.as_ref().unwrap(),
1470            &OneOrMany::One("foo".parse::<Name>().unwrap())
1471        );
1472        assert_eq!(
1473            uses[1].protocol.as_ref().unwrap(),
1474            &OneOrMany::One("bar".parse::<Name>().unwrap())
1475        );
1476    }
1477
1478    #[test]
1479    fn test_merge_upgraded_availability() {
1480        let mut some =
1481            document(json!({ "use": [{ "protocol": "foo", "availability": "optional" }] }));
1482        let mut other1 = document(json!({ "use": [{ "protocol": "foo" }] }));
1483        let mut other2 =
1484            document(json!({ "use": [{ "protocol": "foo", "availability": "transitional" }] }));
1485        let mut other3 =
1486            document(json!({ "use": [{ "protocol": "foo", "availability": "same_as_target" }] }));
1487        some.merge_from(&mut other1, &Path::new("some/path")).unwrap();
1488        some.merge_from(&mut other2, &Path::new("some/path")).unwrap();
1489        some.merge_from(&mut other3, &Path::new("some/path")).unwrap();
1490        let uses = some.r#use.as_ref().unwrap();
1491        assert_eq!(uses.len(), 2);
1492        assert_eq!(
1493            uses[0].protocol.as_ref().unwrap(),
1494            &OneOrMany::One("foo".parse::<Name>().unwrap())
1495        );
1496        assert!(uses[0].availability.is_none());
1497        assert_eq!(
1498            uses[1].protocol.as_ref().unwrap(),
1499            &OneOrMany::One("foo".parse::<Name>().unwrap())
1500        );
1501        assert_eq!(uses[1].availability.as_ref().unwrap(), &Availability::SameAsTarget,);
1502    }
1503
1504    #[test]
1505    fn test_merge_different_sections() {
1506        let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1507        let mut other = document(json!({ "expose": [{ "protocol": "bar", "from": "self" }] }));
1508        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1509        let uses = some.r#use.as_ref().unwrap();
1510        let exposes = some.expose.as_ref().unwrap();
1511        assert_eq!(uses.len(), 1);
1512        assert_eq!(exposes.len(), 1);
1513        assert_eq!(
1514            uses[0].protocol.as_ref().unwrap(),
1515            &OneOrMany::One("foo".parse::<Name>().unwrap())
1516        );
1517        assert_eq!(
1518            exposes[0].protocol.as_ref().unwrap(),
1519            &OneOrMany::One("bar".parse::<Name>().unwrap())
1520        );
1521    }
1522
1523    #[test]
1524    fn test_merge_environments() {
1525        let mut some = document(json!({ "environments": [
1526            {
1527                "name": "one",
1528                "extends": "realm",
1529            },
1530            {
1531                "name": "two",
1532                "extends": "none",
1533                "runners": [
1534                    {
1535                        "runner": "r1",
1536                        "from": "#c1",
1537                    },
1538                    {
1539                        "runner": "r2",
1540                        "from": "#c2",
1541                    },
1542                ],
1543                "resolvers": [
1544                    {
1545                        "resolver": "res1",
1546                        "from": "#c1",
1547                        "scheme": "foo",
1548                    },
1549                ],
1550                "debug": [
1551                    {
1552                        "protocol": "baz",
1553                        "from": "#c2"
1554                    }
1555                ]
1556            },
1557        ]}));
1558        let mut other = document(json!({ "environments": [
1559            {
1560                "name": "two",
1561                "__stop_timeout_ms": 100,
1562                "runners": [
1563                    {
1564                        "runner": "r3",
1565                        "from": "#c3",
1566                    },
1567                ],
1568                "resolvers": [
1569                    {
1570                        "resolver": "res2",
1571                        "from": "#c1",
1572                        "scheme": "bar",
1573                    },
1574                ],
1575                "debug": [
1576                    {
1577                        "protocol": "faz",
1578                        "from": "#c2"
1579                    }
1580                ]
1581            },
1582            {
1583                "name": "three",
1584                "__stop_timeout_ms": 1000,
1585            },
1586        ]}));
1587        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1588        assert_eq!(
1589            to_value(some).unwrap(),
1590            json!({"environments": [
1591                {
1592                    "name": "one",
1593                    "extends": "realm",
1594                },
1595                {
1596                    "name": "three",
1597                    "__stop_timeout_ms": 1000,
1598                },
1599                {
1600                    "name": "two",
1601                    "extends": "none",
1602                    "__stop_timeout_ms": 100,
1603                    "runners": [
1604                        {
1605                            "runner": "r1",
1606                            "from": "#c1",
1607                        },
1608                        {
1609                            "runner": "r2",
1610                            "from": "#c2",
1611                        },
1612                        {
1613                            "runner": "r3",
1614                            "from": "#c3",
1615                        },
1616                    ],
1617                    "resolvers": [
1618                        {
1619                            "resolver": "res1",
1620                            "from": "#c1",
1621                            "scheme": "foo",
1622                        },
1623                        {
1624                            "resolver": "res2",
1625                            "from": "#c1",
1626                            "scheme": "bar",
1627                        },
1628                    ],
1629                    "debug": [
1630                        {
1631                            "protocol": "baz",
1632                            "from": "#c2"
1633                        },
1634                        {
1635                            "protocol": "faz",
1636                            "from": "#c2"
1637                        }
1638                    ]
1639                },
1640            ]})
1641        );
1642    }
1643
1644    #[test]
1645    fn test_merge_environments_errors() {
1646        {
1647            let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
1648            let mut other = document(json!({"environments": [{"name": "one", "extends": "none"}]}));
1649            assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
1650        }
1651        {
1652            let mut some =
1653                document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
1654            let mut other =
1655                document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 20}]}));
1656            assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
1657        }
1658
1659        // It's ok if the values match.
1660        {
1661            let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
1662            let mut other =
1663                document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
1664            some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1665            assert_eq!(
1666                to_value(some).unwrap(),
1667                json!({"environments": [{"name": "one", "extends": "realm"}]})
1668            );
1669        }
1670        {
1671            let mut some =
1672                document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
1673            let mut other =
1674                document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
1675            some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1676            assert_eq!(
1677                to_value(some).unwrap(),
1678                json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]})
1679            );
1680        }
1681    }
1682
1683    #[test]
1684    fn test_merge_from_other_config() {
1685        let mut some = document(json!({}));
1686        let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
1687
1688        some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1689        let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
1690        assert_eq!(some.config, expected.config);
1691    }
1692
1693    #[test]
1694    fn test_merge_from_some_config() {
1695        let mut some = document(json!({ "config": { "bar": { "type": "bool" } } }));
1696        let mut other = document(json!({}));
1697
1698        some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1699        let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
1700        assert_eq!(some.config, expected.config);
1701    }
1702
1703    #[test]
1704    fn test_merge_from_config() {
1705        let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
1706        let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
1707        some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1708
1709        assert_eq!(
1710            some,
1711            document(json!({
1712                "config": {
1713                    "foo": { "type": "bool" },
1714                    "bar": { "type": "bool" },
1715                }
1716            })),
1717        );
1718    }
1719
1720    #[test]
1721    fn test_merge_from_config_dedupe_identical_fields() {
1722        let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
1723        let mut other = document(json!({ "config": { "foo": { "type": "bool" } } }));
1724        some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
1725
1726        assert_eq!(some, document(json!({ "config": { "foo": { "type": "bool" } } })));
1727    }
1728
1729    #[test]
1730    fn test_merge_from_config_conflicting_keys() {
1731        let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
1732        let mut other = document(json!({ "config": { "foo": { "type": "uint8" } } }));
1733
1734        assert_matches::assert_matches!(
1735            some.merge_from(&mut other, &path::Path::new("some/path")),
1736            Err(Error::Validate { err, .. })
1737                if err == "Found conflicting entry for config key `foo` in `some/path`."
1738        );
1739    }
1740
1741    #[test]
1742    fn test_canonicalize() {
1743        let mut some = document(json!({
1744            "children": [
1745                // Will be sorted by name
1746                { "name": "b_child", "url": "http://foo/b" },
1747                { "name": "a_child", "url": "http://foo/a" },
1748            ],
1749            "environments": [
1750                // Will be sorted by name
1751                { "name": "b_env" },
1752                { "name": "a_env" },
1753            ],
1754            "collections": [
1755                // Will be sorted by name
1756                { "name": "b_coll", "durability": "transient" },
1757                { "name": "a_coll", "durability": "transient" },
1758            ],
1759            // Will have entries sorted by capability type, then
1760            // by capability name (using the first entry in Many cases).
1761            "capabilities": [
1762                // Will be merged with "bar"
1763                { "protocol": ["foo"] },
1764                { "protocol": "bar" },
1765                // Will not be merged, but will be sorted before "bar"
1766                { "protocol": "arg", "path": "/arg" },
1767                // Will have list of names sorted
1768                { "service": ["b", "a"] },
1769                // Will have list of names sorted
1770                { "event_stream": ["b", "a"] },
1771                { "runner": "myrunner" },
1772                // The following two will *not* be merged, because they have a `path`.
1773                { "runner": "mypathrunner1", "path": "/foo" },
1774                { "runner": "mypathrunner2", "path": "/foo" },
1775            ],
1776            // Same rules as for "capabilities".
1777            "offer": [
1778                // Will be sorted after "bar"
1779                { "protocol": "baz", "from": "#a_child", "to": "#c_child"  },
1780                // The following two entries will be merged
1781                { "protocol": ["foo"], "from": "#a_child", "to": "#b_child"  },
1782                { "protocol": "bar", "from": "#a_child", "to": "#b_child"  },
1783                // Will have list of names sorted
1784                { "service": ["b", "a"], "from": "#a_child", "to": "#b_child"  },
1785                // Will have list of names sorted
1786                {
1787                    "event_stream": ["b", "a"],
1788                    "from": "#a_child",
1789                    "to": "#b_child",
1790                    "scope": ["#b", "#c", "#a"]  // Also gets sorted
1791                },
1792                { "runner": [ "myrunner", "a" ], "from": "#a_child", "to": "#b_child"  },
1793                { "runner": [ "b" ], "from": "#a_child", "to": "#b_child"  },
1794                { "directory": [ "b" ], "from": "#a_child", "to": "#b_child"  },
1795            ],
1796            "expose": [
1797                { "protocol": ["foo"], "from": "#a_child" },
1798                { "protocol": "bar", "from": "#a_child" },  // Will appear before protocol: foo
1799                // Will have list of names sorted
1800                { "service": ["b", "a"], "from": "#a_child" },
1801                // Will have list of names sorted
1802                {
1803                    "event_stream": ["b", "a"],
1804                    "from": "#a_child",
1805                    "scope": ["#b", "#c", "#a"]  // Also gets sorted
1806                },
1807                { "runner": [ "myrunner", "a" ], "from": "#a_child" },
1808                { "runner": [ "b" ], "from": "#a_child" },
1809                { "directory": [ "b" ], "from": "#a_child" },
1810            ],
1811            "use": [
1812                // Will be sorted after "baz"
1813                { "protocol": ["zazzle"], "path": "/zazbaz" },
1814                // These will be merged
1815                { "protocol": ["foo"] },
1816                { "protocol": "bar" },
1817                // Will have list of names sorted
1818                { "service": ["b", "a"] },
1819                // Will have list of names sorted
1820                { "event_stream": ["b", "a"], "scope": ["#b", "#a"] },
1821            ],
1822        }));
1823        some.canonicalize();
1824
1825        assert_json_eq!(
1826            some,
1827            document(json!({
1828                "children": [
1829                    { "name": "a_child", "url": "http://foo/a" },
1830                    { "name": "b_child", "url": "http://foo/b" },
1831                ],
1832                "collections": [
1833                    { "name": "a_coll", "durability": "transient" },
1834                    { "name": "b_coll", "durability": "transient" },
1835                ],
1836                "environments": [
1837                    { "name": "a_env" },
1838                    { "name": "b_env" },
1839                ],
1840                "capabilities": [
1841                    { "event_stream": ["a", "b"] },
1842                    { "protocol": "arg", "path": "/arg" },
1843                    { "protocol": ["bar", "foo"] },
1844                    { "runner": "mypathrunner1", "path": "/foo" },
1845                    { "runner": "mypathrunner2", "path": "/foo" },
1846                    { "runner": "myrunner" },
1847                    { "service": ["a", "b"] },
1848                ],
1849                "use": [
1850                    { "event_stream": ["a", "b"], "scope": ["#a", "#b"] },
1851                    { "protocol": ["bar", "foo"] },
1852                    { "protocol": "zazzle", "path": "/zazbaz" },
1853                    { "service": ["a", "b"] },
1854                ],
1855                "offer": [
1856                    { "directory": "b", "from": "#a_child", "to": "#b_child" },
1857                    {
1858                        "event_stream": ["a", "b"],
1859                        "from": "#a_child",
1860                        "to": "#b_child",
1861                        "scope": ["#a", "#b", "#c"],
1862                    },
1863                    { "protocol": ["bar", "foo"], "from": "#a_child", "to": "#b_child" },
1864                    { "protocol": "baz", "from": "#a_child", "to": "#c_child"  },
1865                    { "runner": [ "a", "b", "myrunner" ], "from": "#a_child", "to": "#b_child" },
1866                    { "service": ["a", "b"], "from": "#a_child", "to": "#b_child" },
1867                ],
1868                "expose": [
1869                    { "directory": "b", "from": "#a_child" },
1870                    {
1871                        "event_stream": ["a", "b"],
1872                        "from": "#a_child",
1873                        "scope": ["#a", "#b", "#c"],
1874                    },
1875                    { "protocol": ["bar", "foo"], "from": "#a_child" },
1876                    { "runner": [ "a", "b", "myrunner" ], "from": "#a_child" },
1877                    { "service": ["a", "b"], "from": "#a_child" },
1878                ],
1879            }))
1880        )
1881    }
1882
1883    #[test]
1884    fn deny_unknown_config_type_fields() {
1885        let input = json!({ "config": { "foo": { "type": "bool", "unknown": "should error" } } });
1886        serde_json5::from_str::<Document>(&input.to_string())
1887            .expect_err("must reject unknown config field attributes");
1888    }
1889
1890    #[test]
1891    fn deny_unknown_config_nested_type_fields() {
1892        let input = json!({
1893            "config": {
1894                "foo": {
1895                    "type": "vector",
1896                    "max_count": 10,
1897                    "element": {
1898                        "type": "bool",
1899                        "unknown": "should error"
1900                    },
1901
1902                }
1903            }
1904        });
1905        serde_json5::from_str::<Document>(&input.to_string())
1906            .expect_err("must reject unknown config field attributes");
1907    }
1908
1909    #[test]
1910    fn test_merge_from_program() {
1911        let mut some = document(json!({ "program": { "binary": "bin/hello_world" } }));
1912        let mut other = document(json!({ "program": { "runner": "elf" } }));
1913        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1914        let expected =
1915            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1916        assert_eq!(some.program, expected.program);
1917    }
1918
1919    #[test]
1920    fn test_merge_from_program_without_runner() {
1921        let mut some =
1922            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1923        // https://fxbug.dev/42160240: merging with a document that doesn't have a runner doesn't override the
1924        // runner that we already have assigned.
1925        let mut other = document(json!({ "program": {} }));
1926        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1927        let expected =
1928            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1929        assert_eq!(some.program, expected.program);
1930    }
1931
1932    #[test]
1933    fn test_merge_from_program_overlapping_environ() {
1934        // It's ok to merge `program.environ` by concatenating the arrays together.
1935        let mut some = document(json!({ "program": { "environ": ["1"] } }));
1936        let mut other = document(json!({ "program": { "environ": ["2"] } }));
1937        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1938        let expected = document(json!({ "program": { "environ": ["1", "2"] } }));
1939        assert_eq!(some.program, expected.program);
1940    }
1941
1942    #[test]
1943    fn test_merge_from_program_overlapping_runner() {
1944        // It's ok to merge `program.runner = "elf"` with `program.runner = "elf"`.
1945        let mut some =
1946            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1947        let mut other = document(json!({ "program": { "runner": "elf" } }));
1948        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1949        let expected =
1950            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
1951        assert_eq!(some.program, expected.program);
1952    }
1953
1954    #[test_case(
1955        document(json!({ "program": { "runner": "elf" } })),
1956        document(json!({ "program": { "runner": "fle" } })),
1957        "runner"
1958        ; "when_runner_conflicts"
1959    )]
1960    #[test_case(
1961        document(json!({ "program": { "binary": "bin/hello_world" } })),
1962        document(json!({ "program": { "binary": "bin/hola_mundo" } })),
1963        "binary"
1964        ; "when_binary_conflicts"
1965    )]
1966    #[test_case(
1967        document(json!({ "program": { "args": ["a".to_owned()] } })),
1968        document(json!({ "program": { "args": ["b".to_owned()] } })),
1969        "args"
1970        ; "when_args_conflicts"
1971    )]
1972    fn test_merge_from_program_error(mut some: Document, mut other: Document, field: &str) {
1973        assert_matches::assert_matches!(
1974            some.merge_from(&mut other, &path::Path::new("some/path")),
1975            Err(Error::Validate {  err, .. })
1976                if err == format!("manifest include had a conflicting `program.{}`: some/path", field)
1977        );
1978    }
1979
1980    #[test_case(
1981        document(json!({ "facets": { "my.key": "my.value" } })),
1982        document(json!({ "facets": { "other.key": "other.value" } })),
1983        document(json!({ "facets": { "my.key": "my.value",  "other.key": "other.value" } }))
1984        ; "two separate keys"
1985    )]
1986    #[test_case(
1987        document(json!({ "facets": { "my.key": "my.value" } })),
1988        document(json!({ "facets": {} })),
1989        document(json!({ "facets": { "my.key": "my.value" } }))
1990        ; "empty other facet"
1991    )]
1992    #[test_case(
1993        document(json!({ "facets": {} })),
1994        document(json!({ "facets": { "other.key": "other.value" } })),
1995        document(json!({ "facets": { "other.key": "other.value" } }))
1996        ; "empty my facet"
1997    )]
1998    #[test_case(
1999        document(json!({ "facets": { "key": { "type": "some_type" } } })),
2000        document(json!({ "facets": { "key": { "runner": "some_runner"} } })),
2001        document(json!({ "facets": { "key": { "type": "some_type", "runner": "some_runner" } } }))
2002        ; "nested facet key"
2003    )]
2004    #[test_case(
2005        document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "type": "new type" }}}})),
2006        document(json!({ "facets": { "key": { "nested_key": { "runner": "some_runner" }} } })),
2007        document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "runner": "some_runner", "type": "new type" }}}}))
2008        ; "double nested facet key"
2009    )]
2010    #[test_case(
2011        document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2"] } } })),
2012        document(json!({ "facets": { "key": { "array_key": ["value_3", "value_4"] } } })),
2013        document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2", "value_3", "value_4"] } } }))
2014        ; "merge array values"
2015    )]
2016    fn test_merge_from_facets(mut my: Document, mut other: Document, expected: Document) {
2017        my.merge_from(&mut other, &Path::new("some/path")).unwrap();
2018        assert_eq!(my.facets, expected.facets);
2019    }
2020
2021    #[test_case(
2022        document(json!({ "facets": { "key": "my.value" }})),
2023        document(json!({ "facets": { "key": "other.value" }})),
2024        "facets.key"
2025        ; "conflict first level keys"
2026    )]
2027    #[test_case(
2028        document(json!({ "facets": { "key":  {"type": "cts" }}})),
2029        document(json!({ "facets": { "key":  {"type": "system" }}})),
2030        "facets.key.type"
2031        ; "conflict second level keys"
2032    )]
2033    #[test_case(
2034        document(json!({ "facets": { "key":  {"type": {"key": "value" }}}})),
2035        document(json!({ "facets": { "key":  {"type": "system" }}})),
2036        "facets.key.type"
2037        ; "incompatible self nested type"
2038    )]
2039    #[test_case(
2040        document(json!({ "facets": { "key":  {"type": "system" }}})),
2041        document(json!({ "facets": { "key":  {"type":  {"key": "value" }}}})),
2042        "facets.key.type"
2043        ; "incompatible other nested type"
2044    )]
2045    #[test_case(
2046        document(json!({ "facets": { "key":  {"type": {"key": "my.value" }}}})),
2047        document(json!({ "facets": { "key":  {"type":  {"key": "some.value" }}}})),
2048        "facets.key.type.key"
2049        ; "conflict third level keys"
2050    )]
2051    #[test_case(
2052        document(json!({ "facets": { "key":  {"type": [ "value_1" ]}}})),
2053        document(json!({ "facets": { "key":  {"type":  "value_2" }}})),
2054        "facets.key.type"
2055        ; "incompatible keys"
2056    )]
2057    fn test_merge_from_facet_error(mut my: Document, mut other: Document, field: &str) {
2058        assert_matches::assert_matches!(
2059            my.merge_from(&mut other, &path::Path::new("some/path")),
2060            Err(Error::Validate {  err, .. })
2061                if err == format!("manifest include had a conflicting `{}`: some/path", field)
2062        );
2063    }
2064
2065    #[test_case("protocol")]
2066    #[test_case("service")]
2067    #[test_case("event_stream")]
2068    fn test_merge_from_duplicate_use_array(typename: &str) {
2069        let mut my = document(json!({ "use": [{ typename: "a" }]}));
2070        let mut other = document(json!({ "use": [
2071            { typename: ["a", "b"], "availability": "optional"}
2072        ]}));
2073        let result = document(json!({ "use": [
2074            { typename: "a" },
2075            { typename: "b", "availability": "optional" },
2076        ]}));
2077
2078        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2079        assert_eq!(my, result);
2080    }
2081
2082    #[test_case("directory")]
2083    #[test_case("storage")]
2084    fn test_merge_from_duplicate_use_noarray(typename: &str) {
2085        let mut my = document(json!({ "use": [{ typename: "a", "path": "/a"}]}));
2086        let mut other = document(json!({ "use": [
2087            { typename: "a", "path": "/a", "availability": "optional" },
2088            { typename: "b", "path": "/b", "availability": "optional" },
2089        ]}));
2090        let result = document(json!({ "use": [
2091            { typename: "a", "path": "/a" },
2092            { typename: "b", "path": "/b", "availability": "optional" },
2093        ]}));
2094        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2095        assert_eq!(my, result);
2096    }
2097
2098    #[test_case("protocol")]
2099    #[test_case("service")]
2100    #[test_case("event_stream")]
2101    fn test_merge_from_duplicate_capabilities_array(typename: &str) {
2102        let mut my = document(json!({ "capabilities": [{ typename: "a" }]}));
2103        let mut other = document(json!({ "capabilities": [ { typename: ["a", "b"] } ]}));
2104        let result = document(json!({ "capabilities": [ { typename: "a" }, { typename: "b" } ]}));
2105
2106        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2107        assert_eq!(my, result);
2108    }
2109
2110    #[test_case("directory")]
2111    #[test_case("storage")]
2112    #[test_case("runner")]
2113    #[test_case("resolver")]
2114    fn test_merge_from_duplicate_capabilities_noarray(typename: &str) {
2115        let mut my = document(json!({ "capabilities": [{ typename: "a", "path": "/a"}]}));
2116        let mut other = document(json!({ "capabilities": [
2117            { typename: "a", "path": "/a" },
2118            { typename: "b", "path": "/b" },
2119        ]}));
2120        let result = document(json!({ "capabilities": [
2121            { typename: "a", "path": "/a" },
2122            { typename: "b", "path": "/b" },
2123        ]}));
2124        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2125        assert_eq!(my, result);
2126    }
2127
2128    #[test]
2129    fn test_merge_with_empty_names() {
2130        // This document is an error because there is no capability name.
2131        let mut my = document(json!({ "capabilities": [{ "path": "/a"}]}));
2132
2133        let mut other = document(json!({ "capabilities": [
2134            { "directory": "a", "path": "/a" },
2135            { "directory": "b", "path": "/b" },
2136        ]}));
2137        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap_err();
2138    }
2139
2140    #[test_case("protocol")]
2141    #[test_case("service")]
2142    #[test_case("event_stream")]
2143    #[test_case("directory")]
2144    #[test_case("storage")]
2145    #[test_case("runner")]
2146    #[test_case("resolver")]
2147    fn test_merge_from_duplicate_offers(typename: &str) {
2148        let mut my = document(json!({ "offer": [{ typename: "a", "from": "self", "to": "#c" }]}));
2149        let mut other = document(json!({ "offer": [
2150            { typename: ["a", "b"], "from": "self", "to": "#c", "availability": "optional" }
2151        ]}));
2152        let result = document(json!({ "offer": [
2153            { typename: "a", "from": "self", "to": "#c" },
2154            { typename: "b", "from": "self", "to": "#c", "availability": "optional" },
2155        ]}));
2156
2157        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2158        assert_eq!(my, result);
2159    }
2160
2161    #[test_case("protocol")]
2162    #[test_case("service")]
2163    #[test_case("event_stream")]
2164    #[test_case("directory")]
2165    #[test_case("runner")]
2166    #[test_case("resolver")]
2167    fn test_merge_from_duplicate_exposes(typename: &str) {
2168        let mut my = document(json!({ "expose": [{ typename: "a", "from": "self" }]}));
2169        let mut other = document(json!({ "expose": [
2170            { typename: ["a", "b"], "from": "self" }
2171        ]}));
2172        let result = document(json!({ "expose": [
2173            { typename: "a", "from": "self" },
2174            { typename: "b", "from": "self" },
2175        ]}));
2176
2177        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2178        assert_eq!(my, result);
2179    }
2180
2181    #[test_case(
2182        document(json!({ "use": [
2183            { "protocol": "a", "availability": "required" },
2184            { "protocol": "b", "availability": "optional" },
2185            { "protocol": "c", "availability": "transitional" },
2186            { "protocol": "d", "availability": "same_as_target" },
2187        ]})),
2188        document(json!({ "use": [
2189            { "protocol": ["a"], "availability": "required" },
2190            { "protocol": ["b"], "availability": "optional" },
2191            { "protocol": ["c"], "availability": "transitional" },
2192            { "protocol": ["d"], "availability": "same_as_target" },
2193        ]})),
2194        document(json!({ "use": [
2195            { "protocol": "a", "availability": "required" },
2196            { "protocol": "b", "availability": "optional" },
2197            { "protocol": "c", "availability": "transitional" },
2198            { "protocol": "d", "availability": "same_as_target" },
2199        ]}))
2200        ; "merge both same"
2201    )]
2202    #[test_case(
2203        document(json!({ "use": [
2204            { "protocol": "a", "availability": "optional" },
2205            { "protocol": "b", "availability": "transitional" },
2206            { "protocol": "c", "availability": "transitional" },
2207        ]})),
2208        document(json!({ "use": [
2209            { "protocol": ["a", "x"], "availability": "required" },
2210            { "protocol": ["b", "y"], "availability": "optional" },
2211            { "protocol": ["c", "z"], "availability": "required" },
2212        ]})),
2213        document(json!({ "use": [
2214            { "protocol": ["a", "x"], "availability": "required" },
2215            { "protocol": ["b", "y"], "availability": "optional" },
2216            { "protocol": ["c", "z"], "availability": "required" },
2217        ]}))
2218        ; "merge with upgrade"
2219    )]
2220    #[test_case(
2221        document(json!({ "use": [
2222            { "protocol": "a", "availability": "required" },
2223            { "protocol": "b", "availability": "optional" },
2224            { "protocol": "c", "availability": "required" },
2225        ]})),
2226        document(json!({ "use": [
2227            { "protocol": ["a", "x"], "availability": "optional" },
2228            { "protocol": ["b", "y"], "availability": "transitional" },
2229            { "protocol": ["c", "z"], "availability": "transitional" },
2230        ]})),
2231        document(json!({ "use": [
2232            { "protocol": "a", "availability": "required" },
2233            { "protocol": "b", "availability": "optional" },
2234            { "protocol": "c", "availability": "required" },
2235            { "protocol": "x", "availability": "optional" },
2236            { "protocol": "y", "availability": "transitional" },
2237            { "protocol": "z", "availability": "transitional" },
2238        ]}))
2239        ; "merge with downgrade"
2240    )]
2241    #[test_case(
2242        document(json!({ "use": [
2243            { "protocol": "a", "availability": "optional" },
2244            { "protocol": "b", "availability": "transitional" },
2245            { "protocol": "c", "availability": "transitional" },
2246        ]})),
2247        document(json!({ "use": [
2248            { "protocol": ["a", "x"], "availability": "same_as_target" },
2249            { "protocol": ["b", "y"], "availability": "same_as_target" },
2250            { "protocol": ["c", "z"], "availability": "same_as_target" },
2251        ]})),
2252        document(json!({ "use": [
2253            { "protocol": "a", "availability": "optional" },
2254            { "protocol": "b", "availability": "transitional" },
2255            { "protocol": "c", "availability": "transitional" },
2256            { "protocol": ["a", "x"], "availability": "same_as_target" },
2257            { "protocol": ["b", "y"], "availability": "same_as_target" },
2258            { "protocol": ["c", "z"], "availability": "same_as_target" },
2259        ]}))
2260        ; "merge with no replacement"
2261    )]
2262    #[test_case(
2263        document(json!({ "use": [
2264            { "protocol": ["a", "b", "c"], "availability": "optional" },
2265            { "protocol": "d", "availability": "same_as_target" },
2266            { "protocol": ["e", "f"] },
2267        ]})),
2268        document(json!({ "use": [
2269            { "protocol": ["c", "e", "g"] },
2270            { "protocol": ["d", "h"] },
2271            { "protocol": ["f", "i"], "availability": "transitional" },
2272        ]})),
2273        document(json!({ "use": [
2274            { "protocol": ["a", "b"], "availability": "optional" },
2275            { "protocol": "d", "availability": "same_as_target" },
2276            { "protocol": ["e", "f"] },
2277            { "protocol": ["c", "g"] },
2278            { "protocol": ["d", "h"] },
2279            { "protocol": "i", "availability": "transitional" },
2280        ]}))
2281        ; "merge multiple"
2282    )]
2283
2284    fn test_merge_from_duplicate_capability_availability(
2285        mut my: Document,
2286        mut other: Document,
2287        result: Document,
2288    ) {
2289        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2290        assert_eq!(my, result);
2291    }
2292
2293    #[test_case(
2294        document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2295        document(json!({ "use": [{ "protocol": ["c", "d"] }]})),
2296        document(json!({ "use": [
2297            { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
2298        ]}))
2299        ; "merge capabilities with disjoint sets"
2300    )]
2301    #[test_case(
2302        document(json!({ "use": [
2303            { "protocol": ["a"] },
2304            { "protocol": "b" },
2305        ]})),
2306        document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2307        document(json!({ "use": [
2308            { "protocol": ["a"] }, { "protocol": "b" },
2309        ]}))
2310        ; "merge capabilities with equal set"
2311    )]
2312    #[test_case(
2313        document(json!({ "use": [
2314            { "protocol": ["a", "b"] },
2315            { "protocol": "c" },
2316        ]})),
2317        document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2318        document(json!({ "use": [
2319            { "protocol": ["a", "b"] }, { "protocol": "c" },
2320        ]}))
2321        ; "merge capabilities with subset"
2322    )]
2323    #[test_case(
2324        document(json!({ "use": [
2325            { "protocol": ["a", "b"] },
2326        ]})),
2327        document(json!({ "use": [{ "protocol": ["a", "b", "c"] }]})),
2328        document(json!({ "use": [
2329            { "protocol": ["a", "b"] },
2330            { "protocol": "c" },
2331        ]}))
2332        ; "merge capabilities with superset"
2333    )]
2334    #[test_case(
2335        document(json!({ "use": [
2336            { "protocol": ["a", "b"] },
2337        ]})),
2338        document(json!({ "use": [{ "protocol": ["b", "c", "d"] }]})),
2339        document(json!({ "use": [
2340            { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
2341        ]}))
2342        ; "merge capabilities with intersection"
2343    )]
2344    #[test_case(
2345        document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
2346        document(json!({ "use": [
2347            { "protocol": ["c", "b", "d"] },
2348            { "protocol": ["e", "d"] },
2349        ]})),
2350        document(json!({ "use": [
2351            {"protocol": ["a", "b"] },
2352            {"protocol": ["c", "d"] },
2353            {"protocol": "e" }]}))
2354        ; "merge capabilities from multiple arrays"
2355    )]
2356    #[test_case(
2357        document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
2358        document(json!({ "use": [{ "service": "foo.bar.Baz", "from": "self"}]})),
2359        document(json!({ "use": [
2360            {"protocol": "foo.bar.Baz", "from": "self"},
2361            {"service": "foo.bar.Baz", "from": "self"}]}))
2362        ; "merge capabilities, types don't match"
2363    )]
2364    #[test_case(
2365        document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
2366        document(json!({ "use": [{ "protocol": "foo.bar.Baz" }]})),
2367        document(json!({ "use": [
2368            {"protocol": "foo.bar.Baz", "from": "self"},
2369            {"protocol": "foo.bar.Baz"}]}))
2370        ; "merge capabilities, fields don't match"
2371    )]
2372
2373    fn test_merge_from_duplicate_capability(
2374        mut my: Document,
2375        mut other: Document,
2376        result: Document,
2377    ) {
2378        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2379        assert_eq!(my, result);
2380    }
2381
2382    #[test_case(&Right::Connect; "connect right")]
2383    #[test_case(&Right::Enumerate; "enumerate right")]
2384    #[test_case(&Right::Execute; "execute right")]
2385    #[test_case(&Right::GetAttributes; "getattr right")]
2386    #[test_case(&Right::ModifyDirectory; "modifydir right")]
2387    #[test_case(&Right::ReadBytes; "readbytes right")]
2388    #[test_case(&Right::Traverse; "traverse right")]
2389    #[test_case(&Right::UpdateAttributes; "updateattrs right")]
2390    #[test_case(&Right::WriteBytes; "writebytes right")]
2391    #[test_case(&Right::ReadAlias; "r right")]
2392    #[test_case(&Right::WriteAlias; "w right")]
2393    #[test_case(&Right::ExecuteAlias; "x right")]
2394    #[test_case(&Right::ReadWriteAlias; "rw right")]
2395    #[test_case(&Right::ReadExecuteAlias; "rx right")]
2396    #[test_case(&OfferFromRef::Self_; "offer from self")]
2397    #[test_case(&OfferFromRef::Parent; "offer from parent")]
2398    #[test_case(&OfferFromRef::Named(Name::new("child".to_string()).unwrap()); "offer from named")]
2399    #[test_case(
2400        &document(json!({}));
2401        "empty document"
2402    )]
2403    #[test_case(
2404        &document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
2405        "use one from self"
2406    )]
2407    #[test_case(
2408        &document(json!({ "use": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
2409        "use multiple from self"
2410    )]
2411    #[test_case(
2412        &document(json!({
2413            "offer": [{ "protocol": "foo.bar.Baz", "from": "self", "to": "#elements"}],
2414            "collections" :[{"name": "elements", "durability": "transient" }]
2415        }));
2416        "offer from self to collection"
2417    )]
2418    #[test_case(
2419        &document(json!({
2420            "offer": [
2421                { "service": "foo.bar.Baz", "from": "self", "to": "#elements" },
2422                { "service": "some.other.Service", "from": "self", "to": "#elements"},
2423            ],
2424            "collections":[ {"name": "elements", "durability": "transient"} ]}));
2425        "service offers"
2426    )]
2427    #[test_case(
2428        &document(json!({ "expose": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
2429        "expose protocols from self"
2430    )]
2431    #[test_case(
2432        &document(json!({ "expose": [{ "service": ["foo.bar.Baz", "some.other.Service"], "from": "self"}]}));
2433        "expose service from self"
2434    )]
2435    #[test_case(
2436        &document(json!({ "capabilities": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
2437        "capabilities from self"
2438    )]
2439    #[test_case(
2440        &document(json!({ "facets": { "my.key": "my.value" } }));
2441        "facets"
2442    )]
2443    #[test_case(
2444        &document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2445        "elf runner program"
2446    )]
2447    fn serialize_roundtrips<T>(val: &T)
2448    where
2449        T: serde::de::DeserializeOwned + Serialize + PartialEq + std::fmt::Debug,
2450    {
2451        let raw = serde_json::to_string(val).expect("serializing `val` should work");
2452        let parsed: T =
2453            serde_json::from_str(&raw).expect("must be able to parse back serialized value");
2454        assert_eq!(val, &parsed, "parsed value must equal original value");
2455    }
2456}