Skip to main content

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;
9use crate::types::child::ContextChild;
10use crate::types::collection::ContextCollection;
11use crate::types::common::*;
12use crate::types::environment::ContextEnvironment;
13use crate::types::expose::ContextExpose;
14use crate::types::offer::ContextOffer;
15use crate::types::program::ContextProgram;
16use crate::types::r#use::ContextUse;
17use crate::{
18    Canonicalize, CanonicalizeContext, Capability, CapabilityClause, CapabilityFromRef, Child,
19    Collection, ConfigKey, ConfigValueType, Environment, Error, Expose, Location, Offer, Program,
20    Use, merge_spanned_vec,
21};
22
23pub use cm_types::{
24    Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
25    OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
26};
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
907fn merge_from_context_capability_field<T: ContextCapabilityClause>(
908    us: &mut Option<Vec<T>>,
909    other: &mut Option<Vec<T>>,
910) -> Result<(), Error> {
911    // Empty entries are an error, and merging removes empty entries so we first need to check
912    // for them.
913    for entry in us.iter().flatten().chain(other.iter().flatten()) {
914        if entry.names().is_empty() {
915            return Err(Error::Validate {
916                // TODO change error type
917                err: format!("{}: Missing type name: {:#?}", entry.decl_type(), entry),
918                filename: None,
919            });
920        }
921    }
922
923    if let Some(all_ours) = us.as_mut() {
924        if let Some(all_theirs) = other.take() {
925            for mut theirs in all_theirs {
926                for ours in &mut *all_ours {
927                    compute_diff_context(ours, &mut theirs);
928                }
929                all_ours.push(theirs);
930            }
931        }
932        // Post-filter step: remove empty entries.
933        all_ours.retain(|ours| !ours.names().is_empty())
934    } else if let Some(theirs) = other.take() {
935        us.replace(theirs);
936    }
937    Ok(())
938}
939
940/// Merges `us` into `other` according to the rules documented for [`include`].
941/// [`include`]: #include
942fn merge_from_other_field<T: std::cmp::PartialEq>(
943    us: &mut Option<Vec<T>>,
944    other: &mut Option<Vec<T>>,
945) {
946    if let Some(ours) = us {
947        if let Some(theirs) = other.take() {
948            // Add their elements, ignoring dupes with ours
949            for t in theirs {
950                if !ours.contains(&t) {
951                    ours.push(t);
952                }
953            }
954        }
955    } else if let Some(theirs) = other.take() {
956        us.replace(theirs);
957    }
958}
959
960/// Subtracts the capabilities in `ours` from `theirs` if the declarations match in their type and
961/// other fields, resulting in the removal of duplicates between `ours` and `theirs`. Stores the
962/// result in `theirs`.
963///
964/// Inexact matches on `availability` are allowed if there is a partial order between them. The
965/// stronger availability is chosen.
966fn compute_diff<T: CapabilityClause>(ours: &mut T, theirs: &mut T) {
967    // Return early if one is empty.
968    if ours.names().is_empty() || theirs.names().is_empty() {
969        return;
970    }
971
972    // Return early if the types don't match.
973    if ours.capability_type().unwrap() != theirs.capability_type().unwrap() {
974        return;
975    }
976
977    // Check if the non-capability fields match before proceeding.
978    let mut ours_partial = ours.clone();
979    let mut theirs_partial = theirs.clone();
980    for e in [&mut ours_partial, &mut theirs_partial] {
981        e.set_names(Vec::new());
982        // Availability is allowed to differ (see merge algorithm below)
983        e.set_availability(None);
984    }
985    if ours_partial != theirs_partial {
986        // The fields other than `availability` do not match, nothing to remove.
987        return;
988    }
989
990    // Compare the availabilities.
991    let Some(avail_cmp) = ours
992        .availability()
993        .unwrap_or_default()
994        .partial_cmp(&theirs.availability().unwrap_or_default())
995    else {
996        // The availabilities are incompatible (no partial order).
997        return;
998    };
999
1000    let mut our_names: Vec<Name> = ours.names().into_iter().map(Into::into).collect();
1001    let mut their_names: Vec<Name> = theirs.names().into_iter().map(Into::into).collect();
1002
1003    let mut our_entries_to_remove = HashSet::new();
1004    let mut their_entries_to_remove = HashSet::new();
1005    for e in &their_names {
1006        if !our_names.contains(e) {
1007            // Not a duplicate, so keep.
1008            continue;
1009        }
1010        match avail_cmp {
1011            cmp::Ordering::Less => {
1012                // Their availability is stronger, meaning theirs should take
1013                // priority. Keep `e` in theirs, and remove it from ours.
1014                our_entries_to_remove.insert(e.clone());
1015            }
1016            cmp::Ordering::Greater => {
1017                // Our availability is stronger, meaning ours should take
1018                // priority. Remove `e` from theirs.
1019                their_entries_to_remove.insert(e.clone());
1020            }
1021            cmp::Ordering::Equal => {
1022                // The availabilities are equal, so `e` is a duplicate.
1023                their_entries_to_remove.insert(e.clone());
1024            }
1025        }
1026    }
1027    our_names.retain(|e| !our_entries_to_remove.contains(e));
1028    their_names.retain(|e| !their_entries_to_remove.contains(e));
1029
1030    ours.set_names(our_names);
1031    theirs.set_names(their_names);
1032}
1033
1034/// Subtracts the capabilities in `ours` from `theirs` if the declarations match in their type and
1035/// other fields, resulting in the removal of duplicates between `ours` and `theirs`. Stores the
1036/// result in `theirs`.
1037///
1038/// Inexact matches on `availability` are allowed if there is a partial order between them. The
1039/// stronger availability is chosen.
1040fn compute_diff_context<T: ContextCapabilityClause>(ours: &mut T, theirs: &mut T) {
1041    let our_spanned = ours.names();
1042    let their_spanned = theirs.names();
1043
1044    if our_spanned.is_empty() || their_spanned.is_empty() {
1045        return;
1046    }
1047
1048    if ours.capability_type(None).unwrap() != theirs.capability_type(None).unwrap() {
1049        return;
1050    }
1051
1052    let mut ours_check = ours.clone();
1053    let mut theirs_check = theirs.clone();
1054
1055    ours_check.set_names(Vec::new());
1056    theirs_check.set_names(Vec::new());
1057    ours_check.set_availability(None);
1058    theirs_check.set_availability(None);
1059
1060    if ours_check != theirs_check {
1061        return;
1062    }
1063
1064    let our_avail = ours.availability().map(|a| a.value).unwrap_or_default();
1065    let their_avail = theirs.availability().map(|a| a.value).unwrap_or_default();
1066
1067    let Some(avail_cmp) = our_avail.partial_cmp(&their_avail) else {
1068        return;
1069    };
1070
1071    let our_raw_set: HashSet<&Name> = our_spanned.iter().map(|s| &s.value).collect();
1072
1073    let mut remove_from_ours_raw = HashSet::new();
1074    let mut remove_from_theirs_raw = HashSet::new();
1075
1076    for item in &their_spanned {
1077        let name = &item.value;
1078        if !our_raw_set.contains(name) {
1079            continue;
1080        }
1081
1082        match avail_cmp {
1083            cmp::Ordering::Less => {
1084                remove_from_ours_raw.insert(name.clone());
1085            }
1086            cmp::Ordering::Greater => {
1087                remove_from_theirs_raw.insert(name.clone());
1088            }
1089            cmp::Ordering::Equal => {
1090                remove_from_theirs_raw.insert(name.clone());
1091            }
1092        }
1093    }
1094
1095    if !remove_from_ours_raw.is_empty() {
1096        let new_ours =
1097            our_spanned.into_iter().filter(|s| !remove_from_ours_raw.contains(&s.value)).collect();
1098        ours.set_names(new_ours);
1099    }
1100
1101    if !remove_from_theirs_raw.is_empty() {
1102        let new_theirs = their_spanned
1103            .into_iter()
1104            .filter(|s| !remove_from_theirs_raw.contains(&s.value))
1105            .collect();
1106        theirs.set_names(new_theirs);
1107    }
1108}
1109
1110/// Trait that allows us to merge `serde_json::Map`s into `indexmap::IndexMap`s and vice versa.
1111trait ValueMap {
1112    fn get_mut(&mut self, key: &str) -> Option<&mut Value>;
1113    fn insert(&mut self, key: String, val: Value);
1114}
1115
1116impl ValueMap for Map<String, Value> {
1117    fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
1118        self.get_mut(key)
1119    }
1120
1121    fn insert(&mut self, key: String, val: Value) {
1122        self.insert(key, val);
1123    }
1124}
1125
1126impl ValueMap for IndexMap<String, Value> {
1127    fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
1128        self.get_mut(key)
1129    }
1130
1131    fn insert(&mut self, key: String, val: Value) {
1132        self.insert(key, val);
1133    }
1134}
1135
1136#[derive(Debug, Default, Serialize, PartialEq)]
1137pub struct DocumentContext {
1138    #[serde(skip_serializing_if = "Option::is_none")]
1139    pub include: Option<Vec<ContextSpanned<String>>>,
1140    #[serde(skip_serializing_if = "Option::is_none")]
1141    pub program: Option<ContextSpanned<ContextProgram>>,
1142    #[serde(skip_serializing_if = "Option::is_none")]
1143    pub children: Option<Vec<ContextSpanned<ContextChild>>>,
1144    #[serde(skip_serializing_if = "Option::is_none")]
1145    pub collections: Option<Vec<ContextSpanned<ContextCollection>>>,
1146    #[serde(skip_serializing_if = "Option::is_none")]
1147    pub environments: Option<Vec<ContextSpanned<ContextEnvironment>>>,
1148    #[serde(skip_serializing_if = "Option::is_none")]
1149    pub capabilities: Option<Vec<ContextSpanned<ContextCapability>>>,
1150    #[serde(skip_serializing_if = "Option::is_none")]
1151    pub r#use: Option<Vec<ContextSpanned<ContextUse>>>,
1152    #[serde(skip_serializing_if = "Option::is_none")]
1153    pub expose: Option<Vec<ContextSpanned<ContextExpose>>>,
1154    #[serde(skip_serializing_if = "Option::is_none")]
1155    pub offer: Option<Vec<ContextSpanned<ContextOffer>>>,
1156    #[serde(skip_serializing_if = "Option::is_none")]
1157    pub facets: Option<IndexMap<String, ContextSpanned<Value>>>,
1158    #[serde(skip_serializing_if = "Option::is_none")]
1159    pub config: Option<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>,
1160}
1161
1162impl DocumentContext {
1163    pub fn merge_from(
1164        &mut self,
1165        mut other: DocumentContext,
1166        include_path: &path::Path,
1167    ) -> Result<(), Error> {
1168        merge_spanned_vec!(self, other, include);
1169        self.merge_program(&mut other, include_path)?;
1170        merge_spanned_vec!(self, other, children);
1171        merge_spanned_vec!(self, other, collections);
1172        self.merge_environment(&mut other)?;
1173        merge_from_context_capability_field(&mut self.capabilities, &mut other.capabilities)?;
1174        merge_from_context_capability_field(&mut self.r#use, &mut other.r#use)?;
1175        merge_from_context_capability_field(&mut self.expose, &mut other.expose)?;
1176        merge_from_context_capability_field(&mut self.offer, &mut other.offer)?;
1177        self.merge_facets(&mut other, include_path)?;
1178        self.merge_config(&mut other)?;
1179        Ok(())
1180    }
1181
1182    pub fn canonicalize(&mut self) {
1183        if let Some(children) = &mut self.children {
1184            children.sort_by(|a, b| a.value.name.cmp(&b.value.name));
1185        }
1186        if let Some(collections) = &mut self.collections {
1187            collections.sort_by(|a, b| a.value.name.cmp(&b.value.name));
1188        }
1189        if let Some(environments) = &mut self.environments {
1190            environments.sort_by(|a, b| a.value.name.cmp(&b.value.name));
1191        }
1192        if let Some(capabilities) = &mut self.capabilities {
1193            capabilities.canonicalize_context();
1194        }
1195        if let Some(offers) = &mut self.offer {
1196            offers.canonicalize_context();
1197        }
1198        if let Some(expose) = &mut self.expose {
1199            expose.canonicalize_context();
1200        }
1201        if let Some(r#use) = &mut self.r#use {
1202            r#use.canonicalize_context();
1203        }
1204    }
1205
1206    pub fn all_storage_with_sources<'a>(&'a self) -> HashMap<Name, &'a CapabilityFromRef> {
1207        if let Some(capabilities) = self.capabilities.as_ref() {
1208            capabilities
1209                .iter()
1210                .filter_map(|cap_wrapper| {
1211                    let c = &cap_wrapper.value;
1212
1213                    let storage_span_opt = c.storage.as_ref();
1214                    let source_span_opt = c.from.as_ref();
1215
1216                    match (storage_span_opt, source_span_opt) {
1217                        (Some(s_span), Some(f_span)) => {
1218                            let name_ref: Name = s_span.value.clone();
1219                            let source_ref: &CapabilityFromRef = &f_span.value;
1220
1221                            Some((name_ref, source_ref))
1222                        }
1223                        _ => None,
1224                    }
1225                })
1226                .collect()
1227        } else {
1228            HashMap::new()
1229        }
1230    }
1231
1232    pub fn all_capability_names(&self) -> HashSet<Name> {
1233        self.capabilities
1234            .as_ref()
1235            .map(|c| {
1236                c.iter()
1237                    .flat_map(|capability_wrapper| capability_wrapper.value.names())
1238                    .map(|spanned_name| spanned_name.value)
1239                    .collect()
1240            })
1241            .unwrap_or_default()
1242    }
1243
1244    pub fn all_collection_names(&self) -> HashSet<Name> {
1245        if let Some(collections) = self.collections.as_ref() {
1246            collections.iter().map(|c| c.value.name.value.clone()).collect()
1247        } else {
1248            HashSet::new()
1249        }
1250    }
1251
1252    pub fn all_config_names(&self) -> HashSet<Name> {
1253        self.capabilities
1254            .as_ref()
1255            .map(|caps| {
1256                caps.iter()
1257                    .filter_map(|cap_wrapper| {
1258                        let cap = &cap_wrapper.value;
1259
1260                        cap.config.as_ref().map(|spanned_key| spanned_key.value.clone())
1261                    })
1262                    .collect()
1263            })
1264            .unwrap_or_default()
1265    }
1266
1267    pub fn all_children_names(&self) -> HashSet<Name> {
1268        if let Some(children) = self.children.as_ref() {
1269            children.iter().map(|c| c.value.name.value.clone()).collect()
1270        } else {
1271            HashSet::new()
1272        }
1273    }
1274
1275    pub fn all_dictionaries<'a>(&'a self) -> HashMap<Name, &'a ContextCapability> {
1276        if let Some(capabilities) = self.capabilities.as_ref() {
1277            capabilities
1278                .iter()
1279                .filter_map(|cap_wrapper| {
1280                    let cap = &cap_wrapper.value;
1281                    let dict_span_opt = cap.dictionary.as_ref();
1282
1283                    dict_span_opt.and_then(|dict_span| {
1284                        let name_value = &dict_span.value;
1285                        let name: Name = name_value.clone();
1286                        Some((name, cap))
1287                    })
1288                })
1289                .collect()
1290        } else {
1291            HashMap::new()
1292        }
1293    }
1294
1295    fn merge_program(
1296        &mut self,
1297        other: &mut DocumentContext,
1298        include_path: &path::Path,
1299    ) -> Result<(), Error> {
1300        if other.program.is_none() {
1301            return Ok(());
1302        }
1303        if self.program.is_none() {
1304            self.program = other.program.clone();
1305            return Ok(());
1306        }
1307
1308        let my_program = &mut self.program.as_mut().unwrap().value;
1309        let other_wrapper = other.program.as_mut().unwrap();
1310
1311        let other_origin = other_wrapper.origin.clone();
1312        let other_program_val = &mut other_wrapper.value;
1313
1314        if let Some(other_runner) = other_program_val.runner.take() {
1315            if let Some(my_runner) = my_program.runner.as_ref() {
1316                if my_runner.value != other_runner.value {
1317                    return Err(Error::merge(
1318                        format!(
1319                            "Manifest include had a conflicting `program.runner`: parent='{}', include='{}'",
1320                            my_runner.value, other_runner.value
1321                        ),
1322                        Some(other_runner.origin),
1323                    ));
1324                }
1325            } else {
1326                my_program.runner = Some(other_runner);
1327            }
1328        }
1329
1330        Self::merge_maps_unified(
1331            &mut my_program.info,
1332            &other_program_val.info,
1333            "program",
1334            include_path,
1335            Some(&other_origin),
1336            Some(&vec!["environ", "features"]),
1337        )
1338    }
1339
1340    fn merge_environment(&mut self, other: &mut DocumentContext) -> Result<(), Error> {
1341        if other.environments.is_none() {
1342            return Ok(());
1343        }
1344        if self.environments.is_none() {
1345            self.environments = Some(vec![]);
1346        }
1347
1348        let merged_results = {
1349            let my_environments = self.environments.as_mut().unwrap();
1350            let other_environments = other.environments.as_mut().unwrap();
1351
1352            my_environments.sort_by(|x, y| x.value.name.value.cmp(&y.value.name.value));
1353            other_environments.sort_by(|x, y| x.value.name.value.cmp(&y.value.name.value));
1354
1355            let all_environments =
1356                my_environments.drain(..).merge_by(other_environments.drain(..), |x, y| {
1357                    x.value.name.value <= y.value.name.value
1358                });
1359
1360            let groups = all_environments.chunk_by(|e| e.value.name.value.clone());
1361
1362            let mut results = vec![];
1363            for (_name_value, group) in &groups {
1364                let mut group_iter = group.into_iter();
1365                let first_wrapper = group_iter.next().expect("chunk cannot be empty");
1366                let first_origin = first_wrapper.origin.clone();
1367                let mut merged_inner = first_wrapper.value;
1368
1369                for subsequent in group_iter {
1370                    merged_inner.merge_from(subsequent.value)?;
1371                }
1372
1373                results.push(ContextSpanned { value: merged_inner, origin: first_origin });
1374            }
1375            results
1376        };
1377
1378        self.environments = Some(merged_results);
1379        Ok(())
1380    }
1381
1382    fn merge_facets(
1383        &mut self,
1384        other: &mut DocumentContext,
1385        include_path: &path::Path,
1386    ) -> Result<(), Error> {
1387        if let None = other.facets {
1388            return Ok(());
1389        }
1390        if let None = self.facets {
1391            self.facets = Some(Default::default());
1392        }
1393        let other_facets = other.facets.as_ref().unwrap();
1394
1395        for (key, include_spanned) in other_facets {
1396            let entry_origin = Some(&include_spanned.origin);
1397            let my_facets = self.facets.as_mut().unwrap();
1398
1399            if !my_facets.contains_key(key) {
1400                my_facets.insert(key.clone(), include_spanned.clone());
1401            } else {
1402                let self_spanned = my_facets.get_mut(key).unwrap();
1403                match (&mut self_spanned.value, &include_spanned.value) {
1404                    (
1405                        serde_json::Value::Object(self_obj),
1406                        serde_json::Value::Object(include_obj),
1407                    ) => {
1408                        Self::merge_maps_unified(
1409                            self_obj,
1410                            include_obj,
1411                            &format!("facets.{}", key),
1412                            include_path,
1413                            entry_origin,
1414                            None,
1415                        )?;
1416                    }
1417                    (v1, v2) => {
1418                        if v1 != v2 {
1419                            return Err(Error::merge(
1420                                format!(
1421                                    "Manifest include '{}' had a conflicting value for field \"facets.{}\"",
1422                                    include_path.display(),
1423                                    key
1424                                ),
1425                                entry_origin.cloned(),
1426                            ));
1427                        }
1428                    }
1429                }
1430            }
1431        }
1432        Ok(())
1433    }
1434
1435    fn merge_config(&mut self, other: &mut DocumentContext) -> Result<(), Error> {
1436        if other.config.is_none() {
1437            return Ok(());
1438        }
1439        if self.config.is_none() {
1440            self.config = Some(BTreeMap::new());
1441        }
1442
1443        let my_config = self.config.as_mut().unwrap();
1444        let other_config = other.config.as_ref().unwrap();
1445
1446        for (key, other_spanned) in other_config {
1447            if let Some(my_spanned) = my_config.get(key) {
1448                if my_spanned.value != other_spanned.value {
1449                    return Err(Error::merge(
1450                        format!("Conflicting configuration key found: '{}'", key),
1451                        Some(other_spanned.origin.clone()),
1452                    ));
1453                }
1454            } else {
1455                my_config.insert(key.clone(), other_spanned.clone());
1456            }
1457        }
1458        Ok(())
1459    }
1460
1461    fn merge_maps_unified<'s, Source, Dest>(
1462        self_map: &mut Dest,
1463        include_map: Source,
1464        outer_key: &str,
1465        include_path: &path::Path,
1466        origin: Option<&Arc<PathBuf>>,
1467        allow_array_concatenation_keys: Option<&Vec<&str>>,
1468    ) -> Result<(), Error>
1469    where
1470        Source: IntoIterator<Item = (&'s String, &'s serde_json::Value)>,
1471        Dest: ValueMap,
1472    {
1473        for (key, include_val) in include_map {
1474            match self_map.get_mut(key) {
1475                None => {
1476                    self_map.insert(key.clone(), include_val.clone());
1477                }
1478                Some(self_val) => match (self_val, include_val) {
1479                    (serde_json::Value::Object(s_inner), serde_json::Value::Object(i_inner)) => {
1480                        let combined_key = format!("{}.{}", outer_key, key);
1481                        Self::merge_maps_unified(
1482                            s_inner,
1483                            i_inner,
1484                            &combined_key,
1485                            include_path,
1486                            origin,
1487                            allow_array_concatenation_keys,
1488                        )?;
1489                    }
1490                    (serde_json::Value::Array(s_arr), serde_json::Value::Array(i_arr)) => {
1491                        let is_allowed = allow_array_concatenation_keys
1492                            .map_or(true, |keys| keys.contains(&key.as_str()));
1493
1494                        if is_allowed {
1495                            s_arr.extend(i_arr.clone());
1496                        } else if s_arr != i_arr {
1497                            return Err(Error::merge(
1498                                format!(
1499                                    "Conflicting array values for field \"{}.{}\"",
1500                                    outer_key, key
1501                                ),
1502                                origin.cloned(),
1503                            ));
1504                        }
1505                    }
1506                    (v1, v2) if v1 == v2 => {}
1507                    _ => {
1508                        return Err(Error::merge(
1509                            format!(
1510                                "Manifest include '{}' had a conflicting value for field \"{}.{}\"",
1511                                include_path.display(),
1512                                outer_key,
1513                                key
1514                            ),
1515                            origin.cloned(),
1516                        ));
1517                    }
1518                },
1519            }
1520        }
1521        Ok(())
1522    }
1523
1524    pub fn includes(&self) -> Vec<String> {
1525        self.include
1526            .as_ref()
1527            .map(|includes| includes.iter().map(|s| s.value.clone()).collect())
1528            .unwrap_or_default()
1529    }
1530}
1531
1532pub fn parse_and_hydrate(
1533    file_arc: Arc<PathBuf>,
1534    buffer: &String,
1535) -> Result<DocumentContext, Error> {
1536    let parsed_doc: Document = serde_json5::from_str(buffer).map_err(|e| {
1537        let serde_json5::Error::Message { location, msg } = e;
1538        let location = location.map(|l| Location { line: l.line, column: l.column });
1539        Error::parse(msg, location, Some(&(*file_arc).clone()))
1540    })?;
1541
1542    let include = parsed_doc.include.map(|raw_includes| {
1543        raw_includes
1544            .into_iter()
1545            .map(|path| hydrate_simple(path, &file_arc))
1546            .collect::<Vec<ContextSpanned<String>>>()
1547    });
1548
1549    let facets = parsed_doc.facets.map(|raw_facets| {
1550        raw_facets
1551            .into_iter()
1552            .map(|(key, val)| (key, hydrate_simple(val, &file_arc)))
1553            .collect::<IndexMap<String, ContextSpanned<serde_json::Value>>>()
1554    });
1555
1556    let config = parsed_doc.config.map(|raw_config| {
1557        raw_config
1558            .into_iter()
1559            .map(|(key, val)| (key, hydrate_simple(val, &file_arc)))
1560            .collect::<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>()
1561    });
1562
1563    Ok(DocumentContext {
1564        include,
1565        program: hydrate_opt(parsed_doc.program, &file_arc)?,
1566        children: hydrate_list(parsed_doc.children, &file_arc)?,
1567        collections: hydrate_list(parsed_doc.collections, &file_arc)?,
1568        environments: hydrate_list(parsed_doc.environments, &file_arc)?,
1569        capabilities: hydrate_list(parsed_doc.capabilities, &file_arc)?,
1570        r#use: hydrate_list(parsed_doc.r#use, &file_arc)?,
1571        expose: hydrate_list(parsed_doc.expose, &file_arc)?,
1572        offer: hydrate_list(parsed_doc.offer, &file_arc)?,
1573        facets,
1574        config,
1575    })
1576}
1577
1578#[cfg(test)]
1579mod tests {
1580    use super::*;
1581    use crate::OneOrMany;
1582    use crate::types::document::Document;
1583    use crate::types::offer::OfferFromRef;
1584    use crate::types::right::Right;
1585    use difference::Changeset;
1586    use serde_json::{json, to_string_pretty, to_value};
1587    use std::path;
1588    use std::path::Path;
1589    use test_case::test_case;
1590
1591    fn document(contents: serde_json::Value) -> Document {
1592        serde_json5::from_str::<Document>(&contents.to_string()).unwrap()
1593    }
1594
1595    fn document_context(contents: &str) -> DocumentContext {
1596        let file_arc = Arc::new("test.cml".into());
1597        parse_and_hydrate(file_arc, &contents.to_string()).unwrap()
1598    }
1599
1600    macro_rules! assert_json_eq {
1601        ($a:expr, $e:expr) => {{
1602            if $a != $e {
1603                let expected = to_string_pretty(&$e).unwrap();
1604                let actual = to_string_pretty(&$a).unwrap();
1605                assert_eq!(
1606                    $a,
1607                    $e,
1608                    "JSON actual != expected. Diffs:\n\n{}",
1609                    Changeset::new(&actual, &expected, "\n")
1610                );
1611            }
1612        }};
1613    }
1614
1615    #[test]
1616    fn test_includes_v1() {
1617        assert_eq!(document(json!({})).includes(), Vec::<String>::new());
1618        assert_eq!(document(json!({ "include": []})).includes(), Vec::<String>::new());
1619        assert_eq!(
1620            document(json!({ "include": [ "foo.cml", "bar.cml" ]})).includes(),
1621            vec!["foo.cml", "bar.cml"]
1622        );
1623    }
1624
1625    #[test]
1626    fn test_includes() {
1627        let buffer = r##"{}"##;
1628        let empty_document = document_context(buffer);
1629        assert_eq!(empty_document.includes(), Vec::<String>::new());
1630
1631        let buffer = r##"{"include": []}"##;
1632        let empty_include = document_context(buffer);
1633        assert_eq!(empty_include.includes(), Vec::<String>::new());
1634
1635        let buffer = r##"{ "include": [ "foo.cml", "bar.cml" ]}"##;
1636        let include_doc = document_context(buffer);
1637
1638        assert_eq!(include_doc.includes(), vec!["foo.cml", "bar.cml"]);
1639    }
1640
1641    #[test]
1642    fn test_merge_same_section_v1() {
1643        let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1644        let mut other = document(json!({ "use": [{ "protocol": "bar" }] }));
1645        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1646        let uses = some.r#use.as_ref().unwrap();
1647        assert_eq!(uses.len(), 2);
1648        assert_eq!(
1649            uses[0].protocol.as_ref().unwrap(),
1650            &OneOrMany::One("foo".parse::<Name>().unwrap())
1651        );
1652        assert_eq!(
1653            uses[1].protocol.as_ref().unwrap(),
1654            &OneOrMany::One("bar".parse::<Name>().unwrap())
1655        );
1656    }
1657
1658    #[test]
1659    fn test_merge_same_section() {
1660        let mut some = document_context(r##"{ "use": [{ "protocol": "foo" }] }"##);
1661        let other = document_context(r##"{ "use": [{ "protocol": "bar" }] }"##);
1662        some.merge_from(other, &Path::new("some/path")).unwrap();
1663        let uses = some.r#use.as_ref().unwrap();
1664        assert_eq!(uses.len(), 2);
1665        let get_protocol = |u: &ContextSpanned<ContextUse>| -> String {
1666            let proto_wrapper = u.value.protocol.as_ref().expect("Missing protocol");
1667
1668            match &proto_wrapper.value {
1669                OneOrMany::One(name) => name.to_string(),
1670                OneOrMany::Many(_) => panic!("Expected single protocol, found list"),
1671            }
1672        };
1673
1674        assert_eq!(get_protocol(&uses[0]), "foo");
1675        assert_eq!(get_protocol(&uses[1]), "bar");
1676    }
1677
1678    #[test]
1679    fn test_merge_upgraded_availability_v1() {
1680        let mut some =
1681            document(json!({ "use": [{ "protocol": "foo", "availability": "optional" }] }));
1682        let mut other1 = document(json!({ "use": [{ "protocol": "foo" }] }));
1683        let mut other2 =
1684            document(json!({ "use": [{ "protocol": "foo", "availability": "transitional" }] }));
1685        let mut other3 =
1686            document(json!({ "use": [{ "protocol": "foo", "availability": "same_as_target" }] }));
1687        some.merge_from(&mut other1, &Path::new("some/path")).unwrap();
1688        some.merge_from(&mut other2, &Path::new("some/path")).unwrap();
1689        some.merge_from(&mut other3, &Path::new("some/path")).unwrap();
1690        let uses = some.r#use.as_ref().unwrap();
1691        assert_eq!(uses.len(), 2);
1692        assert_eq!(
1693            uses[0].protocol.as_ref().unwrap(),
1694            &OneOrMany::One("foo".parse::<Name>().unwrap())
1695        );
1696        assert!(uses[0].availability.is_none());
1697        assert_eq!(
1698            uses[1].protocol.as_ref().unwrap(),
1699            &OneOrMany::One("foo".parse::<Name>().unwrap())
1700        );
1701        assert_eq!(uses[1].availability.as_ref().unwrap(), &Availability::SameAsTarget,);
1702    }
1703
1704    #[test]
1705    fn test_merge_upgraded_availability() {
1706        let mut some =
1707            document_context(r##"{ "use": [{ "protocol": "foo", "availability": "optional" }] }"##);
1708        let other1 = document_context(r##"{ "use": [{ "protocol": "foo" }] }"##);
1709        let other2 = document_context(
1710            r##"{ "use": [{ "protocol": "foo", "availability": "transitional" }] }"##,
1711        );
1712        let other3 = document_context(
1713            r##"{ "use": [{ "protocol": "foo", "availability": "same_as_target" }] }"##,
1714        );
1715        some.merge_from(other1, &Path::new("some/path")).unwrap();
1716        some.merge_from(other2, &Path::new("some/path")).unwrap();
1717        some.merge_from(other3, &Path::new("some/path")).unwrap();
1718
1719        let uses = some.r#use.as_ref().unwrap();
1720        assert_eq!(uses.len(), 2);
1721        assert_eq!(
1722            uses[0].protocol().as_ref().unwrap().value,
1723            OneOrMany::One("foo".parse::<Name>().unwrap().as_ref())
1724        );
1725        assert!(uses[0].availability().is_none());
1726        assert_eq!(
1727            uses[1].protocol().as_ref().unwrap().value,
1728            OneOrMany::One("foo".parse::<Name>().unwrap().as_ref())
1729        );
1730        assert_eq!(uses[1].availability().as_ref().unwrap().value, Availability::SameAsTarget,);
1731    }
1732
1733    #[test]
1734    fn test_merge_different_sections_v1() {
1735        let mut some = document(json!({ "use": [{ "protocol": "foo" }] }));
1736        let mut other = document(json!({ "expose": [{ "protocol": "bar", "from": "self" }] }));
1737        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1738        let uses = some.r#use.as_ref().unwrap();
1739        let exposes = some.expose.as_ref().unwrap();
1740        assert_eq!(uses.len(), 1);
1741        assert_eq!(exposes.len(), 1);
1742        assert_eq!(
1743            uses[0].protocol.as_ref().unwrap(),
1744            &OneOrMany::One("foo".parse::<Name>().unwrap())
1745        );
1746        assert_eq!(
1747            exposes[0].protocol.as_ref().unwrap(),
1748            &OneOrMany::One("bar".parse::<Name>().unwrap())
1749        );
1750    }
1751
1752    #[test]
1753    fn test_merge_different_sections() {
1754        let mut some = document_context(r##"{ "use": [{ "protocol": "foo" }] }"##);
1755        let other = document_context(r##"{ "expose": [{ "protocol": "bar", "from": "self" }] }"##);
1756        some.merge_from(other, &Path::new("some/path")).unwrap();
1757        let uses = some.r#use.as_ref().unwrap();
1758        let exposes = some.expose.as_ref().unwrap();
1759        assert_eq!(uses.len(), 1);
1760        assert_eq!(exposes.len(), 1);
1761        assert_eq!(
1762            uses[0].protocol().as_ref().unwrap().value,
1763            OneOrMany::One("foo".parse::<Name>().unwrap().as_ref())
1764        );
1765        assert_eq!(
1766            exposes[0].protocol().as_ref().unwrap().value,
1767            OneOrMany::One("bar".parse::<Name>().unwrap().as_ref())
1768        );
1769    }
1770
1771    #[test]
1772    fn test_merge_environments_v1() {
1773        let mut some = document(json!({ "environments": [
1774            {
1775                "name": "one",
1776                "extends": "realm",
1777            },
1778            {
1779                "name": "two",
1780                "extends": "none",
1781                "runners": [
1782                    {
1783                        "runner": "r1",
1784                        "from": "#c1",
1785                    },
1786                    {
1787                        "runner": "r2",
1788                        "from": "#c2",
1789                    },
1790                ],
1791                "resolvers": [
1792                    {
1793                        "resolver": "res1",
1794                        "from": "#c1",
1795                        "scheme": "foo",
1796                    },
1797                ],
1798                "debug": [
1799                    {
1800                        "protocol": "baz",
1801                        "from": "#c2"
1802                    }
1803                ]
1804            },
1805        ]}));
1806        let mut other = document(json!({ "environments": [
1807            {
1808                "name": "two",
1809                "__stop_timeout_ms": 100,
1810                "runners": [
1811                    {
1812                        "runner": "r3",
1813                        "from": "#c3",
1814                    },
1815                ],
1816                "resolvers": [
1817                    {
1818                        "resolver": "res2",
1819                        "from": "#c1",
1820                        "scheme": "bar",
1821                    },
1822                ],
1823                "debug": [
1824                    {
1825                        "protocol": "faz",
1826                        "from": "#c2"
1827                    }
1828                ]
1829            },
1830            {
1831                "name": "three",
1832                "__stop_timeout_ms": 1000,
1833            },
1834        ]}));
1835        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
1836        assert_eq!(
1837            to_value(some).unwrap(),
1838            json!({"environments": [
1839                {
1840                    "name": "one",
1841                    "extends": "realm",
1842                },
1843                {
1844                    "name": "three",
1845                    "__stop_timeout_ms": 1000,
1846                },
1847                {
1848                    "name": "two",
1849                    "extends": "none",
1850                    "__stop_timeout_ms": 100,
1851                    "runners": [
1852                        {
1853                            "runner": "r1",
1854                            "from": "#c1",
1855                        },
1856                        {
1857                            "runner": "r2",
1858                            "from": "#c2",
1859                        },
1860                        {
1861                            "runner": "r3",
1862                            "from": "#c3",
1863                        },
1864                    ],
1865                    "resolvers": [
1866                        {
1867                            "resolver": "res1",
1868                            "from": "#c1",
1869                            "scheme": "foo",
1870                        },
1871                        {
1872                            "resolver": "res2",
1873                            "from": "#c1",
1874                            "scheme": "bar",
1875                        },
1876                    ],
1877                    "debug": [
1878                        {
1879                            "protocol": "baz",
1880                            "from": "#c2"
1881                        },
1882                        {
1883                            "protocol": "faz",
1884                            "from": "#c2"
1885                        }
1886                    ]
1887                },
1888            ]})
1889        );
1890    }
1891
1892    #[test]
1893    fn test_merge_environments() {
1894        let mut some = document_context(
1895            r##"
1896        { "environments": [
1897            {
1898                "name": "one",
1899                "extends": "realm"
1900            },
1901            {
1902                "name": "two",
1903                "extends": "none",
1904                "runners": [
1905                    {
1906                        "runner": "r1",
1907                        "from": "#c1"
1908                    },
1909                    {
1910                        "runner": "r2",
1911                        "from": "#c2"
1912                    }
1913                ],
1914                "resolvers": [
1915                    {
1916                        "resolver": "res1",
1917                        "from": "#c1",
1918                        "scheme": "foo"
1919                    }
1920                ],
1921                "debug": [
1922                    {
1923                        "protocol": "baz",
1924                        "from": "#c2"
1925                    }
1926                ]
1927            }
1928        ]}"##,
1929        );
1930        let other = document_context(
1931            r##"
1932        { "environments": [
1933            {
1934                "name": "two",
1935                "__stop_timeout_ms": 100,
1936                "runners": [
1937                    {
1938                        "runner": "r3",
1939                        "from": "#c3"
1940                    }
1941                ],
1942                "resolvers": [
1943                    {
1944                        "resolver": "res2",
1945                        "from": "#c1",
1946                        "scheme": "bar"
1947                    }
1948                ],
1949                "debug": [
1950                    {
1951                        "protocol": "faz",
1952                        "from": "#c2"
1953                    }
1954                ]
1955            },
1956            {
1957                "name": "three",
1958                "__stop_timeout_ms": 1000
1959            }
1960        ]}"##,
1961        );
1962        some.merge_from(other, &Path::new("some/path")).unwrap();
1963        assert_eq!(
1964            to_value(some).unwrap(),
1965            json!({"environments": [
1966                {
1967                    "name": "one",
1968                    "extends": "realm",
1969                },
1970                {
1971                    "name": "three",
1972                    "__stop_timeout_ms": 1000,
1973                },
1974                {
1975                    "name": "two",
1976                    "extends": "none",
1977                    "__stop_timeout_ms": 100,
1978                    "runners": [
1979                        {
1980                            "runner": "r1",
1981                            "from": "#c1",
1982                        },
1983                        {
1984                            "runner": "r2",
1985                            "from": "#c2",
1986                        },
1987                        {
1988                            "runner": "r3",
1989                            "from": "#c3",
1990                        },
1991                    ],
1992                    "resolvers": [
1993                        {
1994                            "resolver": "res1",
1995                            "from": "#c1",
1996                            "scheme": "foo",
1997                        },
1998                        {
1999                            "resolver": "res2",
2000                            "from": "#c1",
2001                            "scheme": "bar",
2002                        },
2003                    ],
2004                    "debug": [
2005                        {
2006                            "protocol": "baz",
2007                            "from": "#c2"
2008                        },
2009                        {
2010                            "protocol": "faz",
2011                            "from": "#c2"
2012                        }
2013                    ]
2014                },
2015            ]})
2016        );
2017    }
2018
2019    #[test]
2020    fn test_merge_environments_errors_v1() {
2021        {
2022            let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
2023            let mut other = document(json!({"environments": [{"name": "one", "extends": "none"}]}));
2024            assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
2025        }
2026        {
2027            let mut some =
2028                document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
2029            let mut other =
2030                document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 20}]}));
2031            assert!(some.merge_from(&mut other, &Path::new("some/path")).is_err());
2032        }
2033
2034        // It's ok if the values match.
2035        {
2036            let mut some = document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
2037            let mut other =
2038                document(json!({"environments": [{"name": "one", "extends": "realm"}]}));
2039            some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2040            assert_eq!(
2041                to_value(some).unwrap(),
2042                json!({"environments": [{"name": "one", "extends": "realm"}]})
2043            );
2044        }
2045        {
2046            let mut some =
2047                document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
2048            let mut other =
2049                document(json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]}));
2050            some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2051            assert_eq!(
2052                to_value(some).unwrap(),
2053                json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]})
2054            );
2055        }
2056    }
2057
2058    #[test]
2059    fn test_merge_environments_errors() {
2060        {
2061            let mut some =
2062                document_context(r##"{"environments": [{"name": "one", "extends": "realm"}]}"##);
2063            let other =
2064                document_context(r##"{"environments": [{"name": "one", "extends": "none"}]}"##);
2065            assert!(some.merge_from(other, &Path::new("some/path")).is_err());
2066        }
2067        {
2068            let mut some = document_context(
2069                r##"{"environments": [{"name": "one", "__stop_timeout_ms": 10}]}"##,
2070            );
2071            let other = document_context(
2072                r##"{"environments": [{"name": "one", "__stop_timeout_ms": 20}]}"##,
2073            );
2074            assert!(some.merge_from(other, &Path::new("some/path")).is_err());
2075        }
2076
2077        // It's ok if the values match.
2078        {
2079            let mut some =
2080                document_context(r##"{"environments": [{"name": "one", "extends": "realm"}]}"##);
2081            let other =
2082                document_context(r##"{"environments": [{"name": "one", "extends": "realm"}]}"##);
2083            some.merge_from(other, &Path::new("some/path")).unwrap();
2084            assert_eq!(
2085                to_value(some).unwrap(),
2086                json!({"environments": [{"name": "one", "extends": "realm"}]})
2087            );
2088        }
2089        {
2090            let mut some = document_context(
2091                r##"{"environments": [{"name": "one", "__stop_timeout_ms": 10}]}"##,
2092            );
2093            let other = document_context(
2094                r##"{"environments": [{"name": "one", "__stop_timeout_ms": 10}]}"##,
2095            );
2096            some.merge_from(other, &Path::new("some/path")).unwrap();
2097            assert_eq!(
2098                to_value(some).unwrap(),
2099                json!({"environments": [{"name": "one", "__stop_timeout_ms": 10}]})
2100            );
2101        }
2102    }
2103
2104    #[test]
2105    fn test_merge_from_other_config_v1() {
2106        let mut some = document(json!({}));
2107        let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
2108
2109        some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2110        let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
2111        assert_eq!(some.config, expected.config);
2112    }
2113
2114    #[test]
2115    fn test_merge_from_other_config() {
2116        let mut some = document_context(r##"{}"##);
2117        let other = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2118
2119        some.merge_from(other, &path::Path::new("some/path")).unwrap();
2120        let expected = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2121        assert_eq!(some.config, expected.config);
2122    }
2123
2124    #[test]
2125    fn test_merge_from_some_config_v1() {
2126        let mut some = document(json!({ "config": { "bar": { "type": "bool" } } }));
2127        let mut other = document(json!({}));
2128
2129        some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2130        let expected = document(json!({ "config": { "bar": { "type": "bool" } } }));
2131        assert_eq!(some.config, expected.config);
2132    }
2133
2134    #[test]
2135    fn test_merge_from_some_config() {
2136        let mut some = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2137        let other = document_context(r##"{}"##);
2138
2139        some.merge_from(other, &path::Path::new("some/path")).unwrap();
2140        let expected = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2141        assert_eq!(some.config, expected.config);
2142    }
2143
2144    #[test]
2145    fn test_merge_from_config_v1() {
2146        let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
2147        let mut other = document(json!({ "config": { "bar": { "type": "bool" } } }));
2148        some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2149
2150        assert_eq!(
2151            some,
2152            document(json!({
2153                "config": {
2154                    "foo": { "type": "bool" },
2155                    "bar": { "type": "bool" },
2156                }
2157            })),
2158        );
2159    }
2160
2161    #[test]
2162    fn test_merge_from_config() {
2163        let mut some = document_context(r##"{ "config": { "foo": { "type": "bool" } } }"##);
2164        let other = document_context(r##"{ "config": { "bar": { "type": "bool" } } }"##);
2165        some.merge_from(other, &path::Path::new("some/path")).unwrap();
2166
2167        assert_eq!(
2168            to_value(some).unwrap(),
2169            json!({
2170                "config": {
2171                    "foo": { "type": "bool" },
2172                    "bar": { "type": "bool" }
2173                }
2174            }),
2175        );
2176    }
2177
2178    #[test]
2179    fn test_merge_from_config_dedupe_identical_fields_v1() {
2180        let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
2181        let mut other = document(json!({ "config": { "foo": { "type": "bool" } } }));
2182        some.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2183
2184        assert_eq!(some, document(json!({ "config": { "foo": { "type": "bool" } } })));
2185    }
2186
2187    #[test]
2188    fn test_merge_from_config_dedupe_identical_fields() {
2189        let mut some = document_context(r##"{ "config": { "foo": { "type": "bool" } } }"##);
2190        let other = document_context(r##"{ "config": { "foo": { "type": "bool" } } }"##);
2191        some.merge_from(other, &path::Path::new("some/path")).unwrap();
2192
2193        assert_eq!(to_value(some).unwrap(), json!({ "config": { "foo": { "type": "bool" } } }));
2194    }
2195
2196    #[test]
2197    fn test_merge_from_config_conflicting_keys_v1() {
2198        let mut some = document(json!({ "config": { "foo": { "type": "bool" } } }));
2199        let mut other = document(json!({ "config": { "foo": { "type": "uint8" } } }));
2200
2201        assert_matches::assert_matches!(
2202            some.merge_from(&mut other, &path::Path::new("some/path")),
2203            Err(Error::Validate { err, .. })
2204                if err == "Found conflicting entry for config key `foo` in `some/path`."
2205        );
2206    }
2207
2208    #[test]
2209    fn test_merge_from_config_conflicting_keys() {
2210        let mut some = document_context(r##"{ "config": { "foo": { "type": "bool" } } }"##);
2211        let other = document_context(r##"{ "config": { "foo": { "type": "uint8" } } }"##);
2212
2213        assert_matches::assert_matches!(
2214            some.merge_from(other, &path::Path::new("some/path")),
2215            Err(Error::Merge { err, .. })
2216                if err == "Conflicting configuration key found: 'foo'"
2217        );
2218    }
2219
2220    #[test]
2221    fn test_canonicalize() {
2222        let mut some = document(json!({
2223            "children": [
2224                // Will be sorted by name
2225                { "name": "b_child", "url": "http://foo/b" },
2226                { "name": "a_child", "url": "http://foo/a" },
2227            ],
2228            "environments": [
2229                // Will be sorted by name
2230                { "name": "b_env" },
2231                { "name": "a_env" },
2232            ],
2233            "collections": [
2234                // Will be sorted by name
2235                { "name": "b_coll", "durability": "transient" },
2236                { "name": "a_coll", "durability": "transient" },
2237            ],
2238            // Will have entries sorted by capability type, then
2239            // by capability name (using the first entry in Many cases).
2240            "capabilities": [
2241                // Will be merged with "bar"
2242                { "protocol": ["foo"] },
2243                { "protocol": "bar" },
2244                // Will not be merged, but will be sorted before "bar"
2245                { "protocol": "arg", "path": "/arg" },
2246                // Will have list of names sorted
2247                { "service": ["b", "a"] },
2248                // Will have list of names sorted
2249                { "event_stream": ["b", "a"] },
2250                { "runner": "myrunner" },
2251                // The following two will *not* be merged, because they have a `path`.
2252                { "runner": "mypathrunner1", "path": "/foo" },
2253                { "runner": "mypathrunner2", "path": "/foo" },
2254            ],
2255            // Same rules as for "capabilities".
2256            "offer": [
2257                // Will be sorted after "bar"
2258                { "protocol": "baz", "from": "#a_child", "to": "#c_child"  },
2259                // The following two entries will be merged
2260                { "protocol": ["foo"], "from": "#a_child", "to": "#b_child"  },
2261                { "protocol": "bar", "from": "#a_child", "to": "#b_child"  },
2262                // Will have list of names sorted
2263                { "service": ["b", "a"], "from": "#a_child", "to": "#b_child"  },
2264                // Will have list of names sorted
2265                {
2266                    "event_stream": ["b", "a"],
2267                    "from": "#a_child",
2268                    "to": "#b_child",
2269                    "scope": ["#b", "#c", "#a"]  // Also gets sorted
2270                },
2271                { "runner": [ "myrunner", "a" ], "from": "#a_child", "to": "#b_child"  },
2272                { "runner": [ "b" ], "from": "#a_child", "to": "#b_child"  },
2273                { "directory": [ "b" ], "from": "#a_child", "to": "#b_child"  },
2274            ],
2275            "expose": [
2276                { "protocol": ["foo"], "from": "#a_child" },
2277                { "protocol": "bar", "from": "#a_child" },  // Will appear before protocol: foo
2278                // Will have list of names sorted
2279                { "service": ["b", "a"], "from": "#a_child" },
2280                // Will have list of names sorted
2281                {
2282                    "event_stream": ["b", "a"],
2283                    "from": "#a_child",
2284                    "scope": ["#b", "#c", "#a"]  // Also gets sorted
2285                },
2286                { "runner": [ "myrunner", "a" ], "from": "#a_child" },
2287                { "runner": [ "b" ], "from": "#a_child" },
2288                { "directory": [ "b" ], "from": "#a_child" },
2289            ],
2290            "use": [
2291                // Will be sorted after "baz"
2292                { "protocol": ["zazzle"], "path": "/zazbaz" },
2293                // These will be merged
2294                { "protocol": ["foo"] },
2295                { "protocol": "bar" },
2296                // Will have list of names sorted
2297                { "service": ["b", "a"] },
2298                // Will have list of names sorted
2299                { "event_stream": ["b", "a"], "scope": ["#b", "#a"] },
2300            ],
2301        }));
2302        some.canonicalize();
2303
2304        assert_json_eq!(
2305            some,
2306            document(json!({
2307                "children": [
2308                    { "name": "a_child", "url": "http://foo/a" },
2309                    { "name": "b_child", "url": "http://foo/b" },
2310                ],
2311                "collections": [
2312                    { "name": "a_coll", "durability": "transient" },
2313                    { "name": "b_coll", "durability": "transient" },
2314                ],
2315                "environments": [
2316                    { "name": "a_env" },
2317                    { "name": "b_env" },
2318                ],
2319                "capabilities": [
2320                    { "event_stream": ["a", "b"] },
2321                    { "protocol": "arg", "path": "/arg" },
2322                    { "protocol": ["bar", "foo"] },
2323                    { "runner": "mypathrunner1", "path": "/foo" },
2324                    { "runner": "mypathrunner2", "path": "/foo" },
2325                    { "runner": "myrunner" },
2326                    { "service": ["a", "b"] },
2327                ],
2328                "use": [
2329                    { "event_stream": ["a", "b"], "scope": ["#a", "#b"] },
2330                    { "protocol": ["bar", "foo"] },
2331                    { "protocol": "zazzle", "path": "/zazbaz" },
2332                    { "service": ["a", "b"] },
2333                ],
2334                "offer": [
2335                    { "directory": "b", "from": "#a_child", "to": "#b_child" },
2336                    {
2337                        "event_stream": ["a", "b"],
2338                        "from": "#a_child",
2339                        "to": "#b_child",
2340                        "scope": ["#a", "#b", "#c"],
2341                    },
2342                    { "protocol": ["bar", "foo"], "from": "#a_child", "to": "#b_child" },
2343                    { "protocol": "baz", "from": "#a_child", "to": "#c_child"  },
2344                    { "runner": [ "a", "b", "myrunner" ], "from": "#a_child", "to": "#b_child" },
2345                    { "service": ["a", "b"], "from": "#a_child", "to": "#b_child" },
2346                ],
2347                "expose": [
2348                    { "directory": "b", "from": "#a_child" },
2349                    {
2350                        "event_stream": ["a", "b"],
2351                        "from": "#a_child",
2352                        "scope": ["#a", "#b", "#c"],
2353                    },
2354                    { "protocol": ["bar", "foo"], "from": "#a_child" },
2355                    { "runner": [ "a", "b", "myrunner" ], "from": "#a_child" },
2356                    { "service": ["a", "b"], "from": "#a_child" },
2357                ],
2358            }))
2359        )
2360    }
2361
2362    #[test]
2363    fn test_canonicalize_context() {
2364        let mut some = document_context(
2365            &json!({
2366                "children": [
2367                    // Will be sorted by name
2368                    { "name": "b_child", "url": "http://foo/b" },
2369                    { "name": "a_child", "url": "http://foo/a" },
2370                ],
2371                "environments": [
2372                    // Will be sorted by name
2373                    { "name": "b_env" },
2374                    { "name": "a_env" },
2375                ],
2376                "collections": [
2377                    // Will be sorted by name
2378                    { "name": "b_coll", "durability": "transient" },
2379                    { "name": "a_coll", "durability": "transient" },
2380                ],
2381                // Will have entries sorted by capability type, then
2382                // by capability name (using the first entry in Many cases).
2383                "capabilities": [
2384                    // Will be merged with "bar"
2385                    { "protocol": ["foo"] },
2386                    { "protocol": "bar" },
2387                    // Will not be merged, but will be sorted before "bar"
2388                    { "protocol": "arg", "path": "/arg" },
2389                    // Will have list of names sorted
2390                    { "service": ["b", "a"] },
2391                    // Will have list of names sorted
2392                    { "event_stream": ["b", "a"] },
2393                    { "runner": "myrunner" },
2394                    // The following two will *not* be merged, because they have a `path`.
2395                    { "runner": "mypathrunner1", "path": "/foo" },
2396                    { "runner": "mypathrunner2", "path": "/foo" },
2397                ],
2398                // Same rules as for "capabilities".
2399                "offer": [
2400                    // Will be sorted after "bar"
2401                    { "protocol": "baz", "from": "#a_child", "to": "#c_child"  },
2402                    // The following two entries will be merged
2403                    { "protocol": ["foo"], "from": "#a_child", "to": "#b_child"  },
2404                    { "protocol": "bar", "from": "#a_child", "to": "#b_child"  },
2405                    // Will have list of names sorted
2406                    { "service": ["b", "a"], "from": "#a_child", "to": "#b_child"  },
2407                    // Will have list of names sorted
2408                    {
2409                        "event_stream": ["b", "a"],
2410                        "from": "#a_child",
2411                        "to": "#b_child",
2412                        "scope": ["#b", "#c", "#a"]  // Also gets sorted
2413                    },
2414                    { "runner": [ "myrunner", "a" ], "from": "#a_child", "to": "#b_child"  },
2415                    { "runner": [ "b" ], "from": "#a_child", "to": "#b_child"  },
2416                    { "directory": [ "b" ], "from": "#a_child", "to": "#b_child"  },
2417                ],
2418                "expose": [
2419                    { "protocol": ["foo"], "from": "#a_child" },
2420                    { "protocol": "bar", "from": "#a_child" },  // Will appear before protocol: foo
2421                    // Will have list of names sorted
2422                    { "service": ["b", "a"], "from": "#a_child" },
2423                    // Will have list of names sorted
2424                    {
2425                        "event_stream": ["b", "a"],
2426                        "from": "#a_child",
2427                        "scope": ["#b", "#c", "#a"]  // Also gets sorted
2428                    },
2429                    { "runner": [ "myrunner", "a" ], "from": "#a_child" },
2430                    { "runner": [ "b" ], "from": "#a_child" },
2431                    { "directory": [ "b" ], "from": "#a_child" },
2432                ],
2433                "use": [
2434                    // Will be sorted after "baz"
2435                    { "protocol": ["zazzle"], "path": "/zazbaz" },
2436                    // These will be merged
2437                    { "protocol": ["foo"] },
2438                    { "protocol": "bar" },
2439                    // Will have list of names sorted
2440                    { "service": ["b", "a"] },
2441                    // Will have list of names sorted
2442                    { "event_stream": ["b", "a"], "scope": ["#b", "#a"] },
2443                ],
2444            })
2445            .to_string(),
2446        );
2447        some.canonicalize();
2448
2449        assert_json_eq!(
2450            some,
2451            document_context(&json!({
2452                "children": [
2453                    { "name": "a_child", "url": "http://foo/a" },
2454                    { "name": "b_child", "url": "http://foo/b" },
2455                ],
2456                "collections": [
2457                    { "name": "a_coll", "durability": "transient" },
2458                    { "name": "b_coll", "durability": "transient" },
2459                ],
2460                "environments": [
2461                    { "name": "a_env" },
2462                    { "name": "b_env" },
2463                ],
2464                "capabilities": [
2465                    { "event_stream": ["a", "b"] },
2466                    { "protocol": "arg", "path": "/arg" },
2467                    { "protocol": ["bar", "foo"] },
2468                    { "runner": "mypathrunner1", "path": "/foo" },
2469                    { "runner": "mypathrunner2", "path": "/foo" },
2470                    { "runner": "myrunner" },
2471                    { "service": ["a", "b"] },
2472                ],
2473                "use": [
2474                    { "event_stream": ["a", "b"], "scope": ["#a", "#b"] },
2475                    { "protocol": ["bar", "foo"] },
2476                    { "protocol": "zazzle", "path": "/zazbaz" },
2477                    { "service": ["a", "b"] },
2478                ],
2479                "offer": [
2480                    { "directory": "b", "from": "#a_child", "to": "#b_child" },
2481                    {
2482                        "event_stream": ["a", "b"],
2483                        "from": "#a_child",
2484                        "to": "#b_child",
2485                        "scope": ["#a", "#b", "#c"],
2486                    },
2487                    { "protocol": ["bar", "foo"], "from": "#a_child", "to": "#b_child" },
2488                    { "protocol": "baz", "from": "#a_child", "to": "#c_child"  },
2489                    { "runner": [ "a", "b", "myrunner" ], "from": "#a_child", "to": "#b_child" },
2490                    { "service": ["a", "b"], "from": "#a_child", "to": "#b_child" },
2491                ],
2492                "expose": [
2493                    { "directory": "b", "from": "#a_child" },
2494                    {
2495                        "event_stream": ["a", "b"],
2496                        "from": "#a_child",
2497                        "scope": ["#a", "#b", "#c"],
2498                    },
2499                    { "protocol": ["bar", "foo"], "from": "#a_child" },
2500                    { "runner": [ "a", "b", "myrunner" ], "from": "#a_child" },
2501                    { "service": ["a", "b"], "from": "#a_child" },
2502                ],
2503            }).to_string())
2504        )
2505    }
2506
2507    #[test]
2508    fn deny_unknown_config_type_fields_v1() {
2509        let input = json!({ "config": { "foo": { "type": "bool", "unknown": "should error" } } });
2510        serde_json5::from_str::<Document>(&input.to_string())
2511            .expect_err("must reject unknown config field attributes");
2512    }
2513
2514    #[test]
2515    fn deny_unknown_config_type_fields() {
2516        let contents =
2517            json!({ "config": { "foo": { "type": "bool", "unknown": "should error" } } });
2518        let file_arc = Arc::new("test.cml".into());
2519        parse_and_hydrate(file_arc, &contents.to_string())
2520            .expect_err("must reject unknown config field attributes");
2521    }
2522
2523    #[test]
2524    fn deny_unknown_config_nested_type_fields() {
2525        let input = json!({
2526            "config": {
2527                "foo": {
2528                    "type": "vector",
2529                    "max_count": 10,
2530                    "element": {
2531                        "type": "bool",
2532                        "unknown": "should error"
2533                    },
2534
2535                }
2536            }
2537        });
2538        serde_json5::from_str::<Document>(&input.to_string())
2539            .expect_err("must reject unknown config field attributes");
2540
2541        let file_arc = Arc::new("test.cml".into());
2542        parse_and_hydrate(file_arc, &input.to_string())
2543            .expect_err("must reject unknown config field attributes");
2544    }
2545
2546    #[test]
2547    fn test_merge_from_program_v1() {
2548        let mut some = document(json!({ "program": { "binary": "bin/hello_world" } }));
2549        let mut other = document(json!({ "program": { "runner": "elf" } }));
2550        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2551        let expected =
2552            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2553        assert_eq!(some.program, expected.program);
2554    }
2555
2556    #[test]
2557    fn test_merge_from_program() {
2558        let mut some =
2559            document_context(&json!({ "program": { "binary": "bin/hello_world" } }).to_string());
2560        let other = document_context(&json!({ "program": { "runner": "elf" } }).to_string());
2561        some.merge_from(other, &Path::new("some/path")).unwrap();
2562        let expected = document_context(
2563            &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2564        );
2565        assert_eq!(some.program, expected.program);
2566    }
2567
2568    #[test]
2569    fn test_merge_from_program_without_runner_v1() {
2570        let mut some =
2571            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2572        // https://fxbug.dev/42160240: merging with a document that doesn't have a runner doesn't override the
2573        // runner that we already have assigned.
2574        let mut other = document(json!({ "program": {} }));
2575        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2576        let expected =
2577            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2578        assert_eq!(some.program, expected.program);
2579    }
2580
2581    #[test]
2582    fn test_merge_from_program_without_runner() {
2583        let mut some = document_context(
2584            &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2585        );
2586        // https://fxbug.dev/42160240: merging with a document that doesn't have a runner doesn't override the
2587        // runner that we already have assigned.
2588        let other = document_context(&json!({ "program": {} }).to_string());
2589        some.merge_from(other, &Path::new("some/path")).unwrap();
2590        let expected = document_context(
2591            &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2592        );
2593        assert_eq!(some.program, expected.program);
2594    }
2595
2596    #[test]
2597    fn test_merge_from_program_overlapping_environ_v1() {
2598        // It's ok to merge `program.environ` by concatenating the arrays together.
2599        let mut some = document(json!({ "program": { "environ": ["1"] } }));
2600        let mut other = document(json!({ "program": { "environ": ["2"] } }));
2601        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2602        let expected = document(json!({ "program": { "environ": ["1", "2"] } }));
2603        assert_eq!(some.program, expected.program);
2604    }
2605
2606    #[test]
2607    fn test_merge_from_program_overlapping_environ() {
2608        // It's ok to merge `program.environ` by concatenating the arrays together.
2609        let mut some = document_context(&json!({ "program": { "environ": ["1"] } }).to_string());
2610        let other = document_context(&json!({ "program": { "environ": ["2"] } }).to_string());
2611        some.merge_from(other, &Path::new("some/path")).unwrap();
2612        let expected =
2613            document_context(&json!({ "program": { "environ": ["1", "2"] } }).to_string());
2614        assert_eq!(some.program, expected.program);
2615    }
2616
2617    #[test]
2618    fn test_merge_from_program_overlapping_runner_v1() {
2619        // It's ok to merge `program.runner = "elf"` with `program.runner = "elf"`.
2620        let mut some =
2621            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2622        let mut other = document(json!({ "program": { "runner": "elf" } }));
2623        some.merge_from(&mut other, &Path::new("some/path")).unwrap();
2624        let expected =
2625            document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
2626        assert_eq!(some.program, expected.program);
2627    }
2628
2629    #[test]
2630    fn test_merge_from_program_overlapping_runner() {
2631        // It's ok to merge `program.runner = "elf"` with `program.runner = "elf"`.
2632        let mut some = document_context(
2633            &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2634        );
2635        let other = document_context(&json!({ "program": { "runner": "elf" } }).to_string());
2636        some.merge_from(other, &Path::new("some/path")).unwrap();
2637        let expected = document_context(
2638            &json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }).to_string(),
2639        );
2640        assert_eq!(some.program, expected.program);
2641    }
2642
2643    #[test_case(
2644        document(json!({ "program": { "runner": "elf" } })),
2645        document(json!({ "program": { "runner": "fle" } })),
2646        "runner"
2647        ; "when_runner_conflicts"
2648    )]
2649    #[test_case(
2650        document(json!({ "program": { "binary": "bin/hello_world" } })),
2651        document(json!({ "program": { "binary": "bin/hola_mundo" } })),
2652        "binary"
2653        ; "when_binary_conflicts"
2654    )]
2655    #[test_case(
2656        document(json!({ "program": { "args": ["a".to_owned()] } })),
2657        document(json!({ "program": { "args": ["b".to_owned()] } })),
2658        "args"
2659        ; "when_args_conflicts"
2660    )]
2661    fn test_merge_from_program_error_v1(mut some: Document, mut other: Document, field: &str) {
2662        assert_matches::assert_matches!(
2663            some.merge_from(&mut other, &path::Path::new("some/path")),
2664            Err(Error::Validate {  err, .. })
2665                if err == format!("manifest include had a conflicting `program.{}`: some/path", field)
2666        );
2667    }
2668
2669    #[test]
2670    fn test_merge_from_program_error_runner() {
2671        let mut some = document_context(&json!({ "program": { "runner": "elf" } }).to_string());
2672        let other = document_context(&json!({ "program": { "runner": "fle" } }).to_string());
2673        assert_matches::assert_matches!(
2674            some.merge_from(other, &Path::new("some/path")),
2675            Err(Error::Merge {  err, .. })
2676                if err == format!("Manifest include had a conflicting `program.runner`: parent='elf', include='fle'"));
2677    }
2678
2679    #[test]
2680    fn test_merge_from_program_error_binary() {
2681        let mut some =
2682            document_context(&json!({ "program": { "binary": "bin/hello_world" } }).to_string());
2683        let other =
2684            document_context(&json!({ "program": { "binary": "bin/hola_mundo" } }).to_string());
2685        assert_matches::assert_matches!(
2686            some.merge_from(other, &Path::new("some/path")),
2687            Err(Error::Merge {  err, .. })
2688                if err == format!("Manifest include 'some/path' had a conflicting value for field \"program.binary\""));
2689    }
2690
2691    #[test]
2692    fn test_merge_from_program_error_args() {
2693        let mut some =
2694            document_context(&json!({ "program": { "args": ["a".to_owned()] } }).to_string());
2695        let other =
2696            document_context(&json!({ "program": { "args": ["b".to_owned()] } }).to_string());
2697        assert_matches::assert_matches!(
2698            some.merge_from(other, &Path::new("some/path")),
2699            Err(Error::Merge {  err, .. })
2700                if err == format!("Conflicting array values for field \"program.args\""));
2701    }
2702
2703    #[test_case(
2704        document(json!({ "facets": { "my.key": "my.value" } })),
2705        document(json!({ "facets": { "other.key": "other.value" } })),
2706        document(json!({ "facets": { "my.key": "my.value",  "other.key": "other.value" } }))
2707        ; "two separate keys"
2708    )]
2709    #[test_case(
2710        document(json!({ "facets": { "my.key": "my.value" } })),
2711        document(json!({ "facets": {} })),
2712        document(json!({ "facets": { "my.key": "my.value" } }))
2713        ; "empty other facet"
2714    )]
2715    #[test_case(
2716        document(json!({ "facets": {} })),
2717        document(json!({ "facets": { "other.key": "other.value" } })),
2718        document(json!({ "facets": { "other.key": "other.value" } }))
2719        ; "empty my facet"
2720    )]
2721    #[test_case(
2722        document(json!({ "facets": { "key": { "type": "some_type" } } })),
2723        document(json!({ "facets": { "key": { "runner": "some_runner"} } })),
2724        document(json!({ "facets": { "key": { "type": "some_type", "runner": "some_runner" } } }))
2725        ; "nested facet key"
2726    )]
2727    #[test_case(
2728        document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "type": "new type" }}}})),
2729        document(json!({ "facets": { "key": { "nested_key": { "runner": "some_runner" }} } })),
2730        document(json!({ "facets": { "key": { "type": "some_type", "nested_key": { "runner": "some_runner", "type": "new type" }}}}))
2731        ; "double nested facet key"
2732    )]
2733    #[test_case(
2734        document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2"] } } })),
2735        document(json!({ "facets": { "key": { "array_key": ["value_3", "value_4"] } } })),
2736        document(json!({ "facets": { "key": { "array_key": ["value_1", "value_2", "value_3", "value_4"] } } }))
2737        ; "merge array values"
2738    )]
2739    fn test_merge_from_facets_v1(mut my: Document, mut other: Document, expected: Document) {
2740        my.merge_from(&mut other, &Path::new("some/path")).unwrap();
2741        assert_eq!(my.facets, expected.facets);
2742    }
2743
2744    #[test_case(
2745        document_context(&json!({ "facets": { "my.key": "my.value" } }).to_string()),
2746        document_context(&json!({ "facets": { "other.key": "other.value" } }).to_string()),
2747        document_context(&json!({ "facets": { "my.key": "my.value",  "other.key": "other.value" } }).to_string())
2748        ; "two separate keys"
2749    )]
2750    #[test_case(
2751        document_context(&json!({ "facets": { "my.key": "my.value" } }).to_string()),
2752        document_context(&json!({ "facets": {} }).to_string()),
2753        document_context(&json!({ "facets": { "my.key": "my.value" } }).to_string())
2754        ; "empty other facet"
2755    )]
2756    #[test_case(
2757        document_context(&json!({ "facets": {} }).to_string()),
2758        document_context(&json!({ "facets": { "other.key": "other.value" } }).to_string()),
2759        document_context(&json!({ "facets": { "other.key": "other.value" } }).to_string())
2760        ; "empty my facet"
2761    )]
2762    #[test_case(
2763        document_context(&json!({ "facets": { "key": { "type": "some_type" } } }).to_string()),
2764        document_context(&json!({ "facets": { "key": { "runner": "some_runner"} } }).to_string()),
2765        document_context(&json!({ "facets": { "key": { "type": "some_type", "runner": "some_runner" } } }).to_string())
2766        ; "nested facet key"
2767    )]
2768    #[test_case(
2769        document_context(&json!({ "facets": { "key": { "type": "some_type", "nested_key": { "type": "new type" }}}}).to_string()),
2770        document_context(&json!({ "facets": { "key": { "nested_key": { "runner": "some_runner" }} } }).to_string()),
2771        document_context(&json!({ "facets": { "key": { "type": "some_type", "nested_key": { "runner": "some_runner", "type": "new type" }}}}).to_string())
2772        ; "double nested facet key"
2773    )]
2774    #[test_case(
2775        document_context(&json!({ "facets": { "key": { "array_key": ["value_1", "value_2"] } } }).to_string()),
2776        document_context(&json!({ "facets": { "key": { "array_key": ["value_3", "value_4"] } } }).to_string()),
2777        document_context(&json!({ "facets": { "key": { "array_key": ["value_1", "value_2", "value_3", "value_4"] } } }).to_string())
2778        ; "merge array values" // failing
2779    )]
2780    fn test_merge_from_facets(
2781        mut my: DocumentContext,
2782        other: DocumentContext,
2783        expected: DocumentContext,
2784    ) {
2785        my.merge_from(other, &Path::new("some/path")).unwrap();
2786        assert_eq!(my.facets, expected.facets);
2787    }
2788
2789    #[test_case(
2790        document(json!({ "facets": { "key": "my.value" }})),
2791        document(json!({ "facets": { "key": "other.value" }})),
2792        "facets.key"
2793        ; "conflict first level keys"
2794    )]
2795    #[test_case(
2796        document(json!({ "facets": { "key":  {"type": "cts" }}})),
2797        document(json!({ "facets": { "key":  {"type": "system" }}})),
2798        "facets.key.type"
2799        ; "conflict second level keys"
2800    )]
2801    #[test_case(
2802        document(json!({ "facets": { "key":  {"type": {"key": "value" }}}})),
2803        document(json!({ "facets": { "key":  {"type": "system" }}})),
2804        "facets.key.type"
2805        ; "incompatible self nested type"
2806    )]
2807    #[test_case(
2808        document(json!({ "facets": { "key":  {"type": "system" }}})),
2809        document(json!({ "facets": { "key":  {"type":  {"key": "value" }}}})),
2810        "facets.key.type"
2811        ; "incompatible other nested type"
2812    )]
2813    #[test_case(
2814        document(json!({ "facets": { "key":  {"type": {"key": "my.value" }}}})),
2815        document(json!({ "facets": { "key":  {"type":  {"key": "some.value" }}}})),
2816        "facets.key.type.key"
2817        ; "conflict third level keys"
2818    )]
2819    #[test_case(
2820        document(json!({ "facets": { "key":  {"type": [ "value_1" ]}}})),
2821        document(json!({ "facets": { "key":  {"type":  "value_2" }}})),
2822        "facets.key.type"
2823        ; "incompatible keys"
2824    )]
2825    fn test_merge_from_facet_error_v1(mut my: Document, mut other: Document, field: &str) {
2826        assert_matches::assert_matches!(
2827            my.merge_from(&mut other, &path::Path::new("some/path")),
2828            Err(Error::Validate {  err, .. })
2829                if err == format!("manifest include had a conflicting `{}`: some/path", field)
2830        );
2831    }
2832
2833    #[test_case(
2834        document_context(&json!({ "facets": { "key": "my.value" }}).to_string()),
2835        document_context(&json!({ "facets": { "key": "other.value" }}).to_string()),
2836        "facets.key"
2837        ; "conflict first level keys" // failing
2838    )]
2839    #[test_case(
2840        document_context(&json!({ "facets": { "key":  {"type": "cts" }}}).to_string()),
2841        document_context(&json!({ "facets": { "key":  {"type": "system" }}}).to_string()),
2842        "facets.key.type"
2843        ; "conflict second level keys"
2844    )]
2845    #[test_case(
2846        document_context(&json!({ "facets": { "key":  {"type": {"key": "value" }}}}).to_string()),
2847        document_context(&json!({ "facets": { "key":  {"type": "system" }}}).to_string()),
2848        "facets.key.type"
2849        ; "incompatible self nested type"
2850    )]
2851    #[test_case(
2852        document_context(&json!({ "facets": { "key":  {"type": "system" }}}).to_string()),
2853        document_context(&json!({ "facets": { "key":  {"type":  {"key": "value" }}}}).to_string()),
2854        "facets.key.type"
2855        ; "incompatible other nested type"
2856    )]
2857    #[test_case(
2858        document_context(&json!({ "facets": { "key":  {"type": {"key": "my.value" }}}}).to_string()),
2859        document_context(&json!({ "facets": { "key":  {"type":  {"key": "some.value" }}}}).to_string()),
2860        "facets.key.type.key"
2861        ; "conflict third level keys"
2862    )]
2863    #[test_case(
2864        document_context(&json!({ "facets": { "key":  {"type": [ "value_1" ]}}}).to_string()),
2865        document_context(&json!({ "facets": { "key":  {"type":  "value_2" }}}).to_string()),
2866        "facets.key.type"
2867        ; "incompatible keys"
2868    )]
2869    fn test_merge_from_facet_error(mut my: DocumentContext, other: DocumentContext, field: &str) {
2870        assert_matches::assert_matches!(
2871            my.merge_from(other, &path::Path::new("some/path")),
2872            Err(Error::Merge {  err, .. })
2873                if err == format!("Manifest include 'some/path' had a conflicting value for field \"{}\"", field)
2874        );
2875    }
2876
2877    #[test_case("protocol")]
2878    #[test_case("service")]
2879    #[test_case("event_stream")]
2880    fn test_merge_from_duplicate_use_array_v1(typename: &str) {
2881        let mut my = document(json!({ "use": [{ typename: "a" }]}));
2882        let mut other = document(json!({ "use": [
2883            { typename: ["a", "b"], "availability": "optional"}
2884        ]}));
2885        let result = document(json!({ "use": [
2886            { typename: "a" },
2887            { typename: "b", "availability": "optional" },
2888        ]}));
2889
2890        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2891        assert_eq!(my, result);
2892    }
2893
2894    #[test_case("protocol")]
2895    #[test_case("service")]
2896    #[test_case("event_stream")]
2897    fn test_merge_from_duplicate_use_array(typename: &str) {
2898        let mut my = document_context(&json!({ "use": [{ typename: "a" }]}).to_string());
2899        let other = document_context(
2900            &json!({ "use": [
2901                { typename: ["a", "b"], "availability": "optional"}
2902            ]})
2903            .to_string(),
2904        );
2905        let result = document_context(
2906            &json!({ "use": [
2907                { typename: "a" },
2908                { typename: "b", "availability": "optional" },
2909            ]})
2910            .to_string(),
2911        );
2912
2913        my.merge_from(other, &path::Path::new("some/path")).unwrap();
2914        assert_eq!(my, result);
2915    }
2916
2917    #[test_case("directory")]
2918    #[test_case("storage")]
2919    fn test_merge_from_duplicate_use_noarray_v1(typename: &str) {
2920        let mut my = document(json!({ "use": [{ typename: "a", "path": "/a"}]}));
2921        let mut other = document(json!({ "use": [
2922            { typename: "a", "path": "/a", "availability": "optional" },
2923            { typename: "b", "path": "/b", "availability": "optional" },
2924        ]}));
2925        let result = document(json!({ "use": [
2926            { typename: "a", "path": "/a" },
2927            { typename: "b", "path": "/b", "availability": "optional" },
2928        ]}));
2929        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2930        assert_eq!(my, result);
2931    }
2932
2933    #[test_case("directory")]
2934    #[test_case("storage")]
2935    fn test_merge_from_duplicate_use_noarray(typename: &str) {
2936        let mut my =
2937            document_context(&json!({ "use": [{ typename: "a", "path": "/a"}]}).to_string());
2938        let other = document_context(
2939            &json!({ "use": [
2940                { typename: "a", "path": "/a", "availability": "optional" },
2941                { typename: "b", "path": "/b", "availability": "optional" },
2942            ]})
2943            .to_string(),
2944        );
2945        let result = document_context(
2946            &json!({ "use": [
2947                { typename: "a", "path": "/a" },
2948                { typename: "b", "path": "/b", "availability": "optional" },
2949            ]})
2950            .to_string(),
2951        );
2952        my.merge_from(other, &path::Path::new("some/path")).unwrap();
2953        assert_eq!(my, result);
2954    }
2955
2956    #[test_case("protocol")]
2957    #[test_case("service")]
2958    #[test_case("event_stream")]
2959    fn test_merge_from_duplicate_capabilities_array_v1(typename: &str) {
2960        let mut my = document(json!({ "capabilities": [{ typename: "a" }]}));
2961        let mut other = document(json!({ "capabilities": [ { typename: ["a", "b"] } ]}));
2962        let result = document(json!({ "capabilities": [ { typename: "a" }, { typename: "b" } ]}));
2963
2964        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2965        assert_eq!(my, result);
2966    }
2967
2968    #[test_case("protocol")]
2969    #[test_case("service")]
2970    #[test_case("event_stream")]
2971    fn test_merge_from_duplicate_capabilities_array(typename: &str) {
2972        let mut my = document_context(&json!({ "capabilities": [{ typename: "a" }]}).to_string());
2973        let other =
2974            document_context(&json!({ "capabilities": [ { typename: ["a", "b"] } ]}).to_string());
2975        let result = document_context(
2976            &json!({ "capabilities": [ { typename: "a" }, { typename: "b" } ]}).to_string(),
2977        );
2978
2979        my.merge_from(other, &path::Path::new("some/path")).unwrap();
2980        assert_eq!(my, result);
2981    }
2982
2983    #[test_case("directory")]
2984    #[test_case("storage")]
2985    #[test_case("runner")]
2986    #[test_case("resolver")]
2987    fn test_merge_from_duplicate_capabilities_noarray_v1(typename: &str) {
2988        let mut my = document(json!({ "capabilities": [{ typename: "a", "path": "/a"}]}));
2989        let mut other = document(json!({ "capabilities": [
2990            { typename: "a", "path": "/a" },
2991            { typename: "b", "path": "/b" },
2992        ]}));
2993        let result = document(json!({ "capabilities": [
2994            { typename: "a", "path": "/a" },
2995            { typename: "b", "path": "/b" },
2996        ]}));
2997        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
2998        assert_eq!(my, result);
2999    }
3000
3001    #[test_case("directory")]
3002    #[test_case("storage")]
3003    #[test_case("runner")]
3004    #[test_case("resolver")]
3005    fn test_merge_from_duplicate_capabilities_noarray(typename: &str) {
3006        let mut my = document_context(
3007            &json!({ "capabilities": [{ typename: "a", "path": "/a"}]}).to_string(),
3008        );
3009        let other = document_context(
3010            &json!({ "capabilities": [
3011                { typename: "a", "path": "/a" },
3012                { typename: "b", "path": "/b" },
3013            ]})
3014            .to_string(),
3015        );
3016        let result = document_context(
3017            &json!({ "capabilities": [
3018                { typename: "a", "path": "/a" },
3019                { typename: "b", "path": "/b" },
3020            ]})
3021            .to_string(),
3022        );
3023        my.merge_from(other, &path::Path::new("some/path")).unwrap();
3024        assert_eq!(my, result);
3025    }
3026
3027    #[test]
3028    fn test_merge_with_empty_names_v1() {
3029        // This document is an error because there is no capability name.
3030        let mut my = document(json!({ "capabilities": [{ "path": "/a"}]}));
3031
3032        let mut other = document(json!({ "capabilities": [
3033            { "directory": "a", "path": "/a" },
3034            { "directory": "b", "path": "/b" },
3035        ]}));
3036        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap_err();
3037    }
3038
3039    #[test]
3040    fn test_merge_with_empty_names() {
3041        // This document is an error because there is no capability name.
3042        let mut my = document_context(&json!({ "capabilities": [{ "path": "/a"}]}).to_string());
3043
3044        let other = document_context(
3045            &json!({ "capabilities": [
3046                { "directory": "a", "path": "/a" },
3047                { "directory": "b", "path": "/b" },
3048            ]})
3049            .to_string(),
3050        );
3051        my.merge_from(other, &path::Path::new("some/path")).unwrap_err();
3052    }
3053
3054    #[test_case("protocol")]
3055    #[test_case("service")]
3056    #[test_case("event_stream")]
3057    #[test_case("directory")]
3058    #[test_case("storage")]
3059    #[test_case("runner")]
3060    #[test_case("resolver")]
3061    fn test_merge_from_duplicate_offers_v1(typename: &str) {
3062        let mut my = document(json!({ "offer": [{ typename: "a", "from": "self", "to": "#c" }]}));
3063        let mut other = document(json!({ "offer": [
3064            { typename: ["a", "b"], "from": "self", "to": "#c", "availability": "optional" }
3065        ]}));
3066        let result = document(json!({ "offer": [
3067            { typename: "a", "from": "self", "to": "#c" },
3068            { typename: "b", "from": "self", "to": "#c", "availability": "optional" },
3069        ]}));
3070
3071        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
3072        assert_eq!(my, result);
3073    }
3074
3075    #[test_case("protocol")]
3076    #[test_case("service")]
3077    #[test_case("event_stream")]
3078    #[test_case("directory")]
3079    #[test_case("storage")]
3080    #[test_case("runner")]
3081    #[test_case("resolver")]
3082    fn test_merge_from_duplicate_offers(typename: &str) {
3083        let mut my = document_context(
3084            &json!({ "offer": [{ typename: "a", "from": "self", "to": "#c" }]}).to_string(),
3085        );
3086        let other = document_context(
3087            &json!({ "offer": [
3088                { typename: ["a", "b"], "from": "self", "to": "#c", "availability": "optional" }
3089            ]})
3090            .to_string(),
3091        );
3092        let result = document_context(
3093            &json!({ "offer": [
3094                { typename: "a", "from": "self", "to": "#c" },
3095                { typename: "b", "from": "self", "to": "#c", "availability": "optional" },
3096            ]})
3097            .to_string(),
3098        );
3099
3100        my.merge_from(other, &path::Path::new("some/path")).unwrap();
3101        assert_eq!(my, result);
3102    }
3103
3104    #[test_case("protocol")]
3105    #[test_case("service")]
3106    #[test_case("event_stream")]
3107    #[test_case("directory")]
3108    #[test_case("runner")]
3109    #[test_case("resolver")]
3110    fn test_merge_from_duplicate_exposes_v1(typename: &str) {
3111        let mut my = document(json!({ "expose": [{ typename: "a", "from": "self" }]}));
3112        let mut other = document(json!({ "expose": [
3113            { typename: ["a", "b"], "from": "self" }
3114        ]}));
3115        let result = document(json!({ "expose": [
3116            { typename: "a", "from": "self" },
3117            { typename: "b", "from": "self" },
3118        ]}));
3119
3120        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
3121        assert_eq!(my, result);
3122    }
3123
3124    #[test_case("protocol")]
3125    #[test_case("service")]
3126    #[test_case("event_stream")]
3127    #[test_case("directory")]
3128    #[test_case("runner")]
3129    #[test_case("resolver")]
3130    fn test_merge_from_duplicate_exposes(typename: &str) {
3131        let mut my =
3132            document_context(&json!({ "expose": [{ typename: "a", "from": "self" }]}).to_string());
3133        let other = document_context(
3134            &json!({ "expose": [
3135                { typename: ["a", "b"], "from": "self" }
3136            ]})
3137            .to_string(),
3138        );
3139        let result = document_context(
3140            &json!({ "expose": [
3141                { typename: "a", "from": "self" },
3142                { typename: "b", "from": "self" },
3143            ]})
3144            .to_string(),
3145        );
3146
3147        my.merge_from(other, &path::Path::new("some/path")).unwrap();
3148        assert_eq!(my, result);
3149    }
3150
3151    #[test_case(
3152        document(json!({ "use": [
3153            { "protocol": "a", "availability": "required" },
3154            { "protocol": "b", "availability": "optional" },
3155            { "protocol": "c", "availability": "transitional" },
3156            { "protocol": "d", "availability": "same_as_target" },
3157        ]})),
3158        document(json!({ "use": [
3159            { "protocol": ["a"], "availability": "required" },
3160            { "protocol": ["b"], "availability": "optional" },
3161            { "protocol": ["c"], "availability": "transitional" },
3162            { "protocol": ["d"], "availability": "same_as_target" },
3163        ]})),
3164        document(json!({ "use": [
3165            { "protocol": "a", "availability": "required" },
3166            { "protocol": "b", "availability": "optional" },
3167            { "protocol": "c", "availability": "transitional" },
3168            { "protocol": "d", "availability": "same_as_target" },
3169        ]}))
3170        ; "merge both same"
3171    )]
3172    #[test_case(
3173        document(json!({ "use": [
3174            { "protocol": "a", "availability": "optional" },
3175            { "protocol": "b", "availability": "transitional" },
3176            { "protocol": "c", "availability": "transitional" },
3177        ]})),
3178        document(json!({ "use": [
3179            { "protocol": ["a", "x"], "availability": "required" },
3180            { "protocol": ["b", "y"], "availability": "optional" },
3181            { "protocol": ["c", "z"], "availability": "required" },
3182        ]})),
3183        document(json!({ "use": [
3184            { "protocol": ["a", "x"], "availability": "required" },
3185            { "protocol": ["b", "y"], "availability": "optional" },
3186            { "protocol": ["c", "z"], "availability": "required" },
3187        ]}))
3188        ; "merge with upgrade"
3189    )]
3190    #[test_case(
3191        document(json!({ "use": [
3192            { "protocol": "a", "availability": "required" },
3193            { "protocol": "b", "availability": "optional" },
3194            { "protocol": "c", "availability": "required" },
3195        ]})),
3196        document(json!({ "use": [
3197            { "protocol": ["a", "x"], "availability": "optional" },
3198            { "protocol": ["b", "y"], "availability": "transitional" },
3199            { "protocol": ["c", "z"], "availability": "transitional" },
3200        ]})),
3201        document(json!({ "use": [
3202            { "protocol": "a", "availability": "required" },
3203            { "protocol": "b", "availability": "optional" },
3204            { "protocol": "c", "availability": "required" },
3205            { "protocol": "x", "availability": "optional" },
3206            { "protocol": "y", "availability": "transitional" },
3207            { "protocol": "z", "availability": "transitional" },
3208        ]}))
3209        ; "merge with downgrade"
3210    )]
3211    #[test_case(
3212        document(json!({ "use": [
3213            { "protocol": "a", "availability": "optional" },
3214            { "protocol": "b", "availability": "transitional" },
3215            { "protocol": "c", "availability": "transitional" },
3216        ]})),
3217        document(json!({ "use": [
3218            { "protocol": ["a", "x"], "availability": "same_as_target" },
3219            { "protocol": ["b", "y"], "availability": "same_as_target" },
3220            { "protocol": ["c", "z"], "availability": "same_as_target" },
3221        ]})),
3222        document(json!({ "use": [
3223            { "protocol": "a", "availability": "optional" },
3224            { "protocol": "b", "availability": "transitional" },
3225            { "protocol": "c", "availability": "transitional" },
3226            { "protocol": ["a", "x"], "availability": "same_as_target" },
3227            { "protocol": ["b", "y"], "availability": "same_as_target" },
3228            { "protocol": ["c", "z"], "availability": "same_as_target" },
3229        ]}))
3230        ; "merge with no replacement"
3231    )]
3232    #[test_case(
3233        document(json!({ "use": [
3234            { "protocol": ["a", "b", "c"], "availability": "optional" },
3235            { "protocol": "d", "availability": "same_as_target" },
3236            { "protocol": ["e", "f"] },
3237        ]})),
3238        document(json!({ "use": [
3239            { "protocol": ["c", "e", "g"] },
3240            { "protocol": ["d", "h"] },
3241            { "protocol": ["f", "i"], "availability": "transitional" },
3242        ]})),
3243        document(json!({ "use": [
3244            { "protocol": ["a", "b"], "availability": "optional" },
3245            { "protocol": "d", "availability": "same_as_target" },
3246            { "protocol": ["e", "f"] },
3247            { "protocol": ["c", "g"] },
3248            { "protocol": ["d", "h"] },
3249            { "protocol": "i", "availability": "transitional" },
3250        ]}))
3251        ; "merge multiple"
3252    )]
3253
3254    fn test_merge_from_duplicate_capability_availability_v1(
3255        mut my: Document,
3256        mut other: Document,
3257        result: Document,
3258    ) {
3259        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
3260        assert_eq!(my, result);
3261    }
3262
3263    #[test_case(
3264        document_context(&json!({ "use": [
3265            { "protocol": "a", "availability": "required" },
3266            { "protocol": "b", "availability": "optional" },
3267            { "protocol": "c", "availability": "transitional" },
3268            { "protocol": "d", "availability": "same_as_target" },
3269        ]}).to_string()),
3270        document_context(&json!({ "use": [
3271            { "protocol": ["a"], "availability": "required" },
3272            { "protocol": ["b"], "availability": "optional" },
3273            { "protocol": ["c"], "availability": "transitional" },
3274            { "protocol": ["d"], "availability": "same_as_target" },
3275        ]}).to_string()),
3276        document_context(&json!({ "use": [
3277            { "protocol": "a", "availability": "required" },
3278            { "protocol": "b", "availability": "optional" },
3279            { "protocol": "c", "availability": "transitional" },
3280            { "protocol": "d", "availability": "same_as_target" },
3281        ]}).to_string())
3282        ; "merge both same"
3283    )]
3284    #[test_case(
3285        document_context(&json!({ "use": [
3286            { "protocol": "a", "availability": "optional" },
3287            { "protocol": "b", "availability": "transitional" },
3288            { "protocol": "c", "availability": "transitional" },
3289        ]}).to_string()),
3290        document_context(&json!({ "use": [
3291            { "protocol": ["a", "x"], "availability": "required" },
3292            { "protocol": ["b", "y"], "availability": "optional" },
3293            { "protocol": ["c", "z"], "availability": "required" },
3294        ]}).to_string()),
3295        document_context(&json!({ "use": [
3296            { "protocol": ["a", "x"], "availability": "required" },
3297            { "protocol": ["b", "y"], "availability": "optional" },
3298            { "protocol": ["c", "z"], "availability": "required" },
3299        ]}).to_string())
3300        ; "merge with upgrade"
3301    )]
3302    #[test_case(
3303        document_context(&json!({ "use": [
3304            { "protocol": "a", "availability": "required" },
3305            { "protocol": "b", "availability": "optional" },
3306            { "protocol": "c", "availability": "required" },
3307        ]}).to_string()),
3308        document_context(&json!({ "use": [
3309            { "protocol": ["a", "x"], "availability": "optional" },
3310            { "protocol": ["b", "y"], "availability": "transitional" },
3311            { "protocol": ["c", "z"], "availability": "transitional" },
3312        ]}).to_string()),
3313        document_context(&json!({ "use": [
3314            { "protocol": "a", "availability": "required" },
3315            { "protocol": "b", "availability": "optional" },
3316            { "protocol": "c", "availability": "required" },
3317            { "protocol": "x", "availability": "optional" },
3318            { "protocol": "y", "availability": "transitional" },
3319            { "protocol": "z", "availability": "transitional" },
3320        ]}).to_string())
3321        ; "merge with downgrade"
3322    )]
3323    #[test_case(
3324        document_context(&json!({ "use": [
3325            { "protocol": "a", "availability": "optional" },
3326            { "protocol": "b", "availability": "transitional" },
3327            { "protocol": "c", "availability": "transitional" },
3328        ]}).to_string()),
3329        document_context(&json!({ "use": [
3330            { "protocol": ["a", "x"], "availability": "same_as_target" },
3331            { "protocol": ["b", "y"], "availability": "same_as_target" },
3332            { "protocol": ["c", "z"], "availability": "same_as_target" },
3333        ]}).to_string()),
3334        document_context(&json!({ "use": [
3335            { "protocol": "a", "availability": "optional" },
3336            { "protocol": "b", "availability": "transitional" },
3337            { "protocol": "c", "availability": "transitional" },
3338            { "protocol": ["a", "x"], "availability": "same_as_target" },
3339            { "protocol": ["b", "y"], "availability": "same_as_target" },
3340            { "protocol": ["c", "z"], "availability": "same_as_target" },
3341        ]}).to_string())
3342        ; "merge with no replacement"
3343    )]
3344    #[test_case(
3345        document_context(&json!({ "use": [
3346            { "protocol": ["a", "b", "c"], "availability": "optional" },
3347            { "protocol": "d", "availability": "same_as_target" },
3348            { "protocol": ["e", "f"] },
3349        ]}).to_string()),
3350        document_context(&json!({ "use": [
3351            { "protocol": ["c", "e", "g"] },
3352            { "protocol": ["d", "h"] },
3353            { "protocol": ["f", "i"], "availability": "transitional" },
3354        ]}).to_string()),
3355        document_context(&json!({ "use": [
3356            { "protocol": ["a", "b"], "availability": "optional" },
3357            { "protocol": "d", "availability": "same_as_target" },
3358            { "protocol": ["e", "f"] },
3359            { "protocol": ["c", "g"] },
3360            { "protocol": ["d", "h"] },
3361            { "protocol": "i", "availability": "transitional" },
3362        ]}).to_string())
3363        ; "merge multiple"
3364    )]
3365
3366    fn test_merge_from_duplicate_capability_availability(
3367        mut my: DocumentContext,
3368        other: DocumentContext,
3369        result: DocumentContext,
3370    ) {
3371        my.merge_from(other, &path::Path::new("some/path")).unwrap();
3372        assert_eq!(my, result);
3373    }
3374
3375    #[test_case(
3376        document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
3377        document(json!({ "use": [{ "protocol": ["c", "d"] }]})),
3378        document(json!({ "use": [
3379            { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
3380        ]}))
3381        ; "merge capabilities with disjoint sets"
3382    )]
3383    #[test_case(
3384        document(json!({ "use": [
3385            { "protocol": ["a"] },
3386            { "protocol": "b" },
3387        ]})),
3388        document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
3389        document(json!({ "use": [
3390            { "protocol": ["a"] }, { "protocol": "b" },
3391        ]}))
3392        ; "merge capabilities with equal set"
3393    )]
3394    #[test_case(
3395        document(json!({ "use": [
3396            { "protocol": ["a", "b"] },
3397            { "protocol": "c" },
3398        ]})),
3399        document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
3400        document(json!({ "use": [
3401            { "protocol": ["a", "b"] }, { "protocol": "c" },
3402        ]}))
3403        ; "merge capabilities with subset"
3404    )]
3405    #[test_case(
3406        document(json!({ "use": [
3407            { "protocol": ["a", "b"] },
3408        ]})),
3409        document(json!({ "use": [{ "protocol": ["a", "b", "c"] }]})),
3410        document(json!({ "use": [
3411            { "protocol": ["a", "b"] },
3412            { "protocol": "c" },
3413        ]}))
3414        ; "merge capabilities with superset"
3415    )]
3416    #[test_case(
3417        document(json!({ "use": [
3418            { "protocol": ["a", "b"] },
3419        ]})),
3420        document(json!({ "use": [{ "protocol": ["b", "c", "d"] }]})),
3421        document(json!({ "use": [
3422            { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
3423        ]}))
3424        ; "merge capabilities with intersection"
3425    )]
3426    #[test_case(
3427        document(json!({ "use": [{ "protocol": ["a", "b"] }]})),
3428        document(json!({ "use": [
3429            { "protocol": ["c", "b", "d"] },
3430            { "protocol": ["e", "d"] },
3431        ]})),
3432        document(json!({ "use": [
3433            {"protocol": ["a", "b"] },
3434            {"protocol": ["c", "d"] },
3435            {"protocol": "e" }]}))
3436        ; "merge capabilities from multiple arrays"
3437    )]
3438    #[test_case(
3439        document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
3440        document(json!({ "use": [{ "service": "foo.bar.Baz", "from": "self"}]})),
3441        document(json!({ "use": [
3442            {"protocol": "foo.bar.Baz", "from": "self"},
3443            {"service": "foo.bar.Baz", "from": "self"}]}))
3444        ; "merge capabilities, types don't match"
3445    )]
3446    #[test_case(
3447        document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]})),
3448        document(json!({ "use": [{ "protocol": "foo.bar.Baz" }]})),
3449        document(json!({ "use": [
3450            {"protocol": "foo.bar.Baz", "from": "self"},
3451            {"protocol": "foo.bar.Baz"}]}))
3452        ; "merge capabilities, fields don't match"
3453    )]
3454
3455    fn test_merge_from_duplicate_capability_v1(
3456        mut my: Document,
3457        mut other: Document,
3458        result: Document,
3459    ) {
3460        my.merge_from(&mut other, &path::Path::new("some/path")).unwrap();
3461        assert_eq!(my, result);
3462    }
3463
3464    #[test_case(
3465        document_context(&json!({ "use": [{ "protocol": ["a", "b"] }]}).to_string()),
3466        document_context(&json!({ "use": [{ "protocol": ["c", "d"] }]}).to_string()),
3467        document_context(&json!({ "use": [
3468            { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
3469        ]}).to_string())
3470        ; "merge capabilities with disjoint sets"
3471    )]
3472    #[test_case(
3473        document_context(&json!({ "use": [
3474            { "protocol": ["a"] },
3475            { "protocol": "b" },
3476        ]}).to_string()),
3477        document_context(&json!({ "use": [{ "protocol": ["a", "b"] }]}).to_string()),
3478        document_context(&json!({ "use": [
3479            { "protocol": ["a"] }, { "protocol": "b" },
3480        ]}).to_string())
3481        ; "merge capabilities with equal set"
3482    )]
3483    #[test_case(
3484        document_context(&json!({ "use": [
3485            { "protocol": ["a", "b"] },
3486            { "protocol": "c" },
3487        ]}).to_string()),
3488        document_context(&json!({ "use": [{ "protocol": ["a", "b"] }]}).to_string()),
3489        document_context(&json!({ "use": [
3490            { "protocol": ["a", "b"] }, { "protocol": "c" },
3491        ]}).to_string())
3492        ; "merge capabilities with subset"
3493    )]
3494    #[test_case(
3495        document_context(&json!({ "use": [
3496            { "protocol": ["a", "b"] },
3497        ]}).to_string()),
3498        document_context(&json!({ "use": [{ "protocol": ["a", "b", "c"] }]}).to_string()),
3499        document_context(&json!({ "use": [
3500            { "protocol": ["a", "b"] },
3501            { "protocol": "c" },
3502        ]}).to_string())
3503        ; "merge capabilities with superset"
3504    )]
3505    #[test_case(
3506        document_context(&json!({ "use": [
3507            { "protocol": ["a", "b"] },
3508        ]}).to_string()),
3509        document_context(&json!({ "use": [{ "protocol": ["b", "c", "d"] }]}).to_string()),
3510        document_context(&json!({ "use": [
3511            { "protocol": ["a", "b"] }, { "protocol": ["c", "d"] }
3512        ]}).to_string())
3513        ; "merge capabilities with intersection"
3514    )]
3515    #[test_case(
3516        document_context(&json!({ "use": [{ "protocol": ["a", "b"] }]}).to_string()),
3517        document_context(&json!({ "use": [
3518            { "protocol": ["c", "b", "d"] },
3519            { "protocol": ["e", "d"] },
3520        ]}).to_string()),
3521        document_context(&json!({ "use": [
3522            {"protocol": ["a", "b"] },
3523            {"protocol": ["c", "d"] },
3524            {"protocol": "e" }]}).to_string())
3525        ; "merge capabilities from multiple arrays"
3526    )]
3527    #[test_case(
3528        document_context(&json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]}).to_string()),
3529        document_context(&json!({ "use": [{ "service": "foo.bar.Baz", "from": "self"}]}).to_string()),
3530        document_context(&json!({ "use": [
3531            {"protocol": "foo.bar.Baz", "from": "self"},
3532            {"service": "foo.bar.Baz", "from": "self"}]}).to_string())
3533        ; "merge capabilities, types don't match"
3534    )]
3535    #[test_case(
3536        document_context(&json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]}).to_string()),
3537        document_context(&json!({ "use": [{ "protocol": "foo.bar.Baz" }]}).to_string()),
3538        document_context(&json!({ "use": [
3539            {"protocol": "foo.bar.Baz", "from": "self"},
3540            {"protocol": "foo.bar.Baz"}]}).to_string())
3541        ; "merge capabilities, fields don't match"
3542    )]
3543
3544    fn test_merge_from_duplicate_capability(
3545        mut my: DocumentContext,
3546        other: DocumentContext,
3547        result: DocumentContext,
3548    ) {
3549        my.merge_from(other, &path::Path::new("some/path")).unwrap();
3550        assert_eq!(my, result);
3551    }
3552
3553    #[test_case(&Right::Connect; "connect right")]
3554    #[test_case(&Right::Enumerate; "enumerate right")]
3555    #[test_case(&Right::Execute; "execute right")]
3556    #[test_case(&Right::GetAttributes; "getattr right")]
3557    #[test_case(&Right::ModifyDirectory; "modifydir right")]
3558    #[test_case(&Right::ReadBytes; "readbytes right")]
3559    #[test_case(&Right::Traverse; "traverse right")]
3560    #[test_case(&Right::UpdateAttributes; "updateattrs right")]
3561    #[test_case(&Right::WriteBytes; "writebytes right")]
3562    #[test_case(&Right::ReadAlias; "r right")]
3563    #[test_case(&Right::WriteAlias; "w right")]
3564    #[test_case(&Right::ExecuteAlias; "x right")]
3565    #[test_case(&Right::ReadWriteAlias; "rw right")]
3566    #[test_case(&Right::ReadExecuteAlias; "rx right")]
3567    #[test_case(&OfferFromRef::Self_; "offer from self")]
3568    #[test_case(&OfferFromRef::Parent; "offer from parent")]
3569    #[test_case(&OfferFromRef::Named(Name::new("child".to_string()).unwrap()); "offer from named")]
3570    #[test_case(
3571        &document(json!({}));
3572        "empty document"
3573    )]
3574    #[test_case(
3575        &document(json!({ "use": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
3576        "use one from self"
3577    )]
3578    #[test_case(
3579        &document(json!({ "use": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
3580        "use multiple from self"
3581    )]
3582    #[test_case(
3583        &document(json!({
3584            "offer": [{ "protocol": "foo.bar.Baz", "from": "self", "to": "#elements"}],
3585            "collections" :[{"name": "elements", "durability": "transient" }]
3586        }));
3587        "offer from self to collection"
3588    )]
3589    #[test_case(
3590        &document(json!({
3591            "offer": [
3592                { "service": "foo.bar.Baz", "from": "self", "to": "#elements" },
3593                { "service": "some.other.Service", "from": "self", "to": "#elements"},
3594            ],
3595            "collections":[ {"name": "elements", "durability": "transient"} ]}));
3596        "service offers"
3597    )]
3598    #[test_case(
3599        &document(json!({ "expose": [{ "protocol": ["foo.bar.Baz", "some.other.Protocol"], "from": "self"}]}));
3600        "expose protocols from self"
3601    )]
3602    #[test_case(
3603        &document(json!({ "expose": [{ "service": ["foo.bar.Baz", "some.other.Service"], "from": "self"}]}));
3604        "expose service from self"
3605    )]
3606    #[test_case(
3607        &document(json!({ "capabilities": [{ "protocol": "foo.bar.Baz", "from": "self"}]}));
3608        "capabilities from self"
3609    )]
3610    #[test_case(
3611        &document(json!({ "facets": { "my.key": "my.value" } }));
3612        "facets"
3613    )]
3614    #[test_case(
3615        &document(json!({ "program": { "binary": "bin/hello_world", "runner": "elf" } }));
3616        "elf runner program"
3617    )]
3618    fn serialize_roundtrips<T>(val: &T)
3619    where
3620        T: serde::de::DeserializeOwned + Serialize + PartialEq + std::fmt::Debug,
3621    {
3622        let raw = serde_json::to_string(val).expect("serializing `val` should work");
3623        let parsed: T =
3624            serde_json::from_str(&raw).expect("must be able to parse back serialized value");
3625        assert_eq!(val, &parsed, "parsed value must equal original value");
3626    }
3627}