Skip to main content

cml/types/
capability_id.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::one_or_many::OneOrMany;
6use crate::types::capability::ContextCapability;
7use crate::types::common::{ContextCapabilityClause, option_one_or_many_as_ref_context};
8use crate::types::r#use::ContextUse;
9use crate::{AsClauseContext, ContextSpanned, Error, alias_or_name_context};
10pub use cm_types::{
11    Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
12    NamespacePath, OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
13};
14
15use std::fmt;
16use std::path::PathBuf;
17use std::sync::Arc;
18
19/// A name/identity of a capability exposed/offered to another component.
20///
21/// Exposed or offered capabilities have an identifier whose format
22/// depends on the capability type. For directories and services this is
23/// a path, while for storage this is a storage name. Paths and storage
24/// names, however, are in different conceptual namespaces, and can't
25/// collide with each other.
26///
27/// This enum allows such names to be specified disambiguating what
28/// namespace they are in.
29#[derive(Debug, PartialEq, Eq, Hash, Clone)]
30pub enum CapabilityId<'a> {
31    Service(&'a BorrowedName),
32    Protocol(&'a BorrowedName),
33    Directory(&'a BorrowedName),
34    // A service in a `use` declaration has a target path in the component's namespace.
35    UsedService(Path),
36    // A protocol in a `use` declaration has a target path in the component's namespace.
37    UsedProtocol(Path),
38    // A protocol in a `use` declaration has a numbered handle in the component's namespace.
39    UsedProtocolNumberedHandle(HandleType),
40    // A directory in a `use` declaration has a target path in the component's namespace.
41    UsedDirectory(Path),
42    // A storage in a `use` declaration has a target path in the component's namespace.
43    UsedStorage(Path),
44    // An event stream in a `use` declaration has a target path in the component's namespace.
45    UsedEventStream(Path),
46    // A configuration in a `use` declaration has a target name that matches a config.
47    UsedConfiguration(&'a BorrowedName),
48    UsedRunner(&'a BorrowedName),
49    // A dictionary in a `use` declaration that has a target path in the component's namespace.
50    UsedDictionary(Path),
51    Storage(&'a BorrowedName),
52    Runner(&'a BorrowedName),
53    Resolver(&'a BorrowedName),
54    EventStream(&'a BorrowedName),
55    Dictionary(&'a BorrowedName),
56    Configuration(&'a BorrowedName),
57}
58
59/// Generates a `Vec<ContextSpanned<&BorrowedName>>` -> `Vec<(CapabilityId, Arc<PathBuf>)>` conversion function.
60macro_rules! capability_ids_from_context_names {
61    ($name:ident, $variant:expr) => {
62        fn $name(names: Vec<ContextSpanned<&'a BorrowedName>>) -> Vec<(Self, Arc<PathBuf>)> {
63            names
64                .into_iter()
65                .map(|spanned_name| ($variant(spanned_name.value), spanned_name.origin))
66                .collect()
67        }
68    };
69}
70
71/// Generates a `Vec<ContextSpanned<Path>>` -> `Vec<(CapabilityId, Arc<PathBuf>)>` conversion function.
72macro_rules! capability_ids_from_context_paths {
73    ($name:ident, $variant:expr) => {
74        fn $name(paths: Vec<ContextSpanned<Path>>) -> Vec<(Self, Arc<PathBuf>)> {
75            paths
76                .into_iter()
77                .map(|spanned_path| ($variant(spanned_path.value), spanned_path.origin))
78                .collect()
79        }
80    };
81}
82
83impl<'a> CapabilityId<'a> {
84    /// Human readable description of this capability type.
85    pub fn type_str(&self) -> &'static str {
86        match self {
87            CapabilityId::Service(_) => "service",
88            CapabilityId::Protocol(_) => "protocol",
89            CapabilityId::Directory(_) => "directory",
90            CapabilityId::UsedService(_) => "service",
91            CapabilityId::UsedProtocol(_) => "protocol",
92            CapabilityId::UsedProtocolNumberedHandle(_) => "protocol",
93            CapabilityId::UsedDirectory(_) => "directory",
94            CapabilityId::UsedStorage(_) => "storage",
95            CapabilityId::UsedEventStream(_) => "event_stream",
96            CapabilityId::UsedRunner(_) => "runner",
97            CapabilityId::UsedConfiguration(_) => "config",
98            CapabilityId::UsedDictionary(_) => "dictionary",
99            CapabilityId::Storage(_) => "storage",
100            CapabilityId::Runner(_) => "runner",
101            CapabilityId::Resolver(_) => "resolver",
102            CapabilityId::EventStream(_) => "event_stream",
103            CapabilityId::Dictionary(_) => "dictionary",
104            CapabilityId::Configuration(_) => "config",
105        }
106    }
107
108    /// Return the directory containing the capability, if this capability takes a target path.
109    pub fn get_dir_path(&self) -> Option<NamespacePath> {
110        match self {
111            CapabilityId::UsedService(p)
112            | CapabilityId::UsedProtocol(p)
113            | CapabilityId::UsedEventStream(p) => Some(p.parent()),
114            CapabilityId::UsedDirectory(p)
115            | CapabilityId::UsedStorage(p)
116            | CapabilityId::UsedDictionary(p) => Some(p.clone().into()),
117            _ => None,
118        }
119    }
120
121    /// Return the target path of the capability, if this capability has one.
122    pub fn get_target_path(&self) -> Option<NamespacePath> {
123        match self {
124            CapabilityId::UsedService(p)
125            | CapabilityId::UsedProtocol(p)
126            | CapabilityId::UsedEventStream(p)
127            | CapabilityId::UsedDirectory(p)
128            | CapabilityId::UsedStorage(p)
129            | CapabilityId::UsedDictionary(p) => Some(p.clone().into()),
130            _ => None,
131        }
132    }
133
134    pub fn from_context_capability(
135        capability_input: &'a ContextSpanned<ContextCapability>,
136    ) -> Result<Vec<(Self, Arc<PathBuf>)>, Error> {
137        let capability = &capability_input.value;
138        let origin = &capability_input.origin;
139
140        if let Some(n) = capability.service() {
141            if n.value.is_many()
142                && let Some(cs_path) = &capability.path
143            {
144                return Err(Error::validate_context(
145                    "\"path\" can only be specified when one `service` is supplied.",
146                    Some(cs_path.origin.clone()),
147                ));
148            }
149            return Ok(Self::services_from_context(Self::get_one_or_many_names_context(
150                n,
151                None,
152                capability.capability_type(None).unwrap(),
153            )?));
154        } else if let Some(n) = capability.protocol() {
155            if n.value.is_many()
156                && let Some(cs_path) = &capability.path
157            {
158                return Err(Error::validate_context(
159                    "\"path\" can only be specified when one `protocol` is supplied.",
160                    Some(cs_path.origin.clone()),
161                ));
162            }
163            return Ok(Self::protocols_from_context(Self::get_one_or_many_names_context(
164                n,
165                None,
166                capability.capability_type(None).unwrap(),
167            )?));
168        } else if let Some(n) = capability.directory() {
169            return Ok(Self::directories_from_context(Self::get_one_or_many_names_context(
170                n,
171                None,
172                capability.capability_type(None).unwrap(),
173            )?));
174        } else if let Some(cs_storage) = capability.storage() {
175            if capability.storage_id.is_none() {
176                return Err(Error::validate_context(
177                    "Storage declaration is missing \"storage_id\", but is required.",
178                    Some(cs_storage.origin),
179                ));
180            }
181            return Ok(Self::storages_from_context(Self::get_one_or_many_names_context(
182                cs_storage,
183                None,
184                capability.capability_type(None).unwrap(),
185            )?));
186        } else if let Some(n) = capability.runner() {
187            return Ok(Self::runners_from_context(Self::get_one_or_many_names_context(
188                n,
189                None,
190                capability.capability_type(None).unwrap(),
191            )?));
192        } else if let Some(n) = capability.resolver() {
193            return Ok(Self::resolvers_from_context(Self::get_one_or_many_names_context(
194                n,
195                None,
196                capability.capability_type(None).unwrap(),
197            )?));
198        } else if let Some(n) = capability.event_stream() {
199            return Ok(Self::event_streams_from_context(Self::get_one_or_many_names_context(
200                n,
201                None,
202                capability.capability_type(None).unwrap(),
203            )?));
204        } else if let Some(n) = capability.dictionary() {
205            return Ok(Self::dictionaries_from_context(Self::get_one_or_many_names_context(
206                n,
207                None,
208                capability.capability_type(None).unwrap(),
209            )?));
210        } else if let Some(n) = capability.config() {
211            return Ok(Self::configurations_from_context(Self::get_one_or_many_names_context(
212                n,
213                None,
214                capability.capability_type(None).unwrap(),
215            )?));
216        }
217
218        // Unsupported capability type.
219        let supported_keywords = capability
220            .supported()
221            .into_iter()
222            .map(|k| format!("\"{}\"", k))
223            .collect::<Vec<_>>()
224            .join(", ");
225        Err(Error::validate_context(
226            format!(
227                "`{}` declaration is missing a capability keyword, one of: {}",
228                capability.decl_type(),
229                supported_keywords,
230            ),
231            Some(origin.clone()),
232        ))
233    }
234
235    pub fn from_context_offer_expose<T>(
236        clause_input: &'a ContextSpanned<T>,
237    ) -> Result<Vec<(Self, Arc<PathBuf>)>, Error>
238    where
239        T: ContextCapabilityClause + AsClauseContext + fmt::Debug,
240    {
241        let clause = &clause_input.value;
242        let origin = &clause_input.origin;
243
244        let alias = clause.r#as();
245
246        if let Some(n) = clause.service() {
247            return Ok(Self::services_from_context(Self::get_one_or_many_names_context(
248                n,
249                alias,
250                clause.capability_type(Some(origin.clone())).unwrap(),
251            )?));
252        } else if let Some(n) = clause.protocol() {
253            return Ok(Self::protocols_from_context(Self::get_one_or_many_names_context(
254                n,
255                alias,
256                clause.capability_type(Some(origin.clone())).unwrap(),
257            )?));
258        } else if let Some(n) = clause.directory() {
259            return Ok(Self::directories_from_context(Self::get_one_or_many_names_context(
260                n,
261                alias,
262                clause.capability_type(Some(origin.clone())).unwrap(),
263            )?));
264        } else if let Some(n) = clause.storage() {
265            return Ok(Self::storages_from_context(Self::get_one_or_many_names_context(
266                n,
267                alias,
268                clause.capability_type(Some(origin.clone())).unwrap(),
269            )?));
270        } else if let Some(n) = clause.runner() {
271            return Ok(Self::runners_from_context(Self::get_one_or_many_names_context(
272                n,
273                alias,
274                clause.capability_type(Some(origin.clone())).unwrap(),
275            )?));
276        } else if let Some(n) = clause.resolver() {
277            return Ok(Self::resolvers_from_context(Self::get_one_or_many_names_context(
278                n,
279                alias,
280                clause.capability_type(Some(origin.clone())).unwrap(),
281            )?));
282        } else if let Some(event_stream) = clause.event_stream() {
283            return Ok(Self::event_streams_from_context(Self::get_one_or_many_names_context(
284                event_stream,
285                alias,
286                clause.capability_type(Some(origin.clone())).unwrap(),
287            )?));
288        } else if let Some(n) = clause.dictionary() {
289            return Ok(Self::dictionaries_from_context(Self::get_one_or_many_names_context(
290                n,
291                alias,
292                clause.capability_type(Some(origin.clone())).unwrap(),
293            )?));
294        } else if let Some(n) = clause.config() {
295            return Ok(Self::configurations_from_context(Self::get_one_or_many_names_context(
296                n,
297                alias,
298                clause.capability_type(Some(origin.clone())).unwrap(),
299            )?));
300        }
301
302        // Unsupported capability type.
303        let supported_keywords = clause
304            .supported()
305            .into_iter()
306            .map(|k| format!("\"{}\"", k))
307            .collect::<Vec<_>>()
308            .join(", ");
309        Err(Error::validate_context(
310            format!(
311                "`{}` declaration is missing a capability keyword, one of: {}",
312                clause.decl_type(),
313                supported_keywords,
314            ),
315            Some(origin.clone()),
316        ))
317    }
318
319    /// Given a ContextUse clause, return the set of target identifiers.
320    ///
321    /// When only one capability identifier is specified, the target identifier name is derived
322    /// using the "path" clause. If a "path" clause is not specified, the target identifier is the
323    /// same name as the source.
324    ///
325    /// When multiple capability identifiers are specified, the target names are the same as the
326    /// source names.
327    pub fn from_context_use(
328        use_input: &'a ContextSpanned<ContextUse>,
329    ) -> Result<Vec<(Self, Arc<PathBuf>)>, Error> {
330        let use_ = &use_input.value;
331        let origin = &use_input.origin;
332
333        let alias = use_.path.as_ref();
334
335        if let Some(n) = option_one_or_many_as_ref_context(&use_.service) {
336            return Ok(Self::used_services_from_context(Self::get_one_or_many_svc_paths_context(
337                n,
338                alias,
339                use_input.capability_type(Some(origin.clone())).unwrap(),
340            )?));
341        } else if let Some(n) = option_one_or_many_as_ref_context(&use_.protocol) {
342            if let Some(numbered_handle) = &use_.numbered_handle {
343                return Ok(n
344                    .value
345                    .iter()
346                    .map(|_| {
347                        (
348                            CapabilityId::UsedProtocolNumberedHandle(numbered_handle.value),
349                            n.origin.clone(),
350                        )
351                    })
352                    .collect());
353            }
354
355            return Ok(Self::used_protocols_from_context(Self::get_one_or_many_svc_paths_context(
356                n,
357                alias,
358                use_input.capability_type(Some(origin.clone())).unwrap(),
359            )?));
360        } else if let Some(_) = &use_.directory {
361            if use_.path.is_none() {
362                return Err(Error::validate_context(
363                    "\"path\" should be present for `use directory`.",
364                    Some(origin.clone()),
365                ));
366            }
367            return Ok(vec![(
368                CapabilityId::UsedDirectory(use_.path.as_ref().unwrap().value.clone()),
369                origin.clone(),
370            )]);
371        } else if let Some(_) = &use_.storage {
372            if use_.path.is_none() {
373                return Err(Error::validate_context(
374                    "\"path\" should be present for `use storage`.",
375                    Some(origin.clone()),
376                ));
377            }
378            return Ok(vec![(
379                CapabilityId::UsedStorage(use_.path.as_ref().unwrap().value.clone()),
380                origin.clone(),
381            )]);
382        } else if let Some(_) = &use_.event_stream {
383            if let Some(path) = &use_.path {
384                return Ok(vec![(
385                    CapabilityId::UsedEventStream(path.value.clone()),
386                    origin.clone(),
387                )]);
388            }
389            return Ok(vec![(
390                CapabilityId::UsedEventStream(Path::new("/svc/fuchsia.component.EventStream")?),
391                origin.clone(),
392            )]);
393        } else if let Some(n) = &use_.runner {
394            return Ok(vec![(CapabilityId::UsedRunner(&n.value), n.origin.clone())]);
395        } else if let Some(_) = &use_.config {
396            return match &use_.key {
397                None => Err(Error::validate_context(
398                    "\"key\" should be present for `use config`.",
399                    Some(origin.clone()),
400                )),
401                Some(name) => {
402                    Ok(vec![(CapabilityId::UsedConfiguration(&name.value), origin.clone())])
403                }
404            };
405        } else if let Some(n) = option_one_or_many_as_ref_context(&use_.dictionary) {
406            return Ok(Self::used_dictionaries_from_context(
407                Self::get_one_or_many_svc_paths_context(
408                    n,
409                    alias,
410                    use_input.capability_type(Some(origin.clone())).unwrap(),
411                )?,
412            ));
413        }
414
415        // Unsupported capability type.
416        let supported_keywords = use_input
417            .supported()
418            .into_iter()
419            .map(|k| format!("\"{}\"", k))
420            .collect::<Vec<_>>()
421            .join(", ");
422
423        Err(Error::validate_context(
424            format!(
425                "`{}` declaration is missing a capability keyword, one of: {}",
426                use_input.decl_type(),
427                supported_keywords,
428            ),
429            Some(origin.clone()),
430        ))
431    }
432
433    /// Returns the target names as a `Vec` from a declaration with `names` and `alias` as a `Vec`.
434    fn get_one_or_many_names_context<'b>(
435        name_wrapper: ContextSpanned<OneOrMany<&'b BorrowedName>>,
436        alias: Option<ContextSpanned<&'b BorrowedName>>,
437        capability_type: &str,
438    ) -> Result<Vec<ContextSpanned<&'b BorrowedName>>, Error> {
439        let names_origin = name_wrapper.origin;
440        let names_vec: Vec<&'b BorrowedName> = name_wrapper.value.into_iter().collect();
441        let num_names = names_vec.len();
442
443        if num_names > 1 && alias.is_some() {
444            return Err(Error::validate_contexts(
445                format!("\"as\" can only be specified when one `{}` is supplied.", capability_type),
446                vec![alias.map(|s| s.origin).unwrap_or(names_origin)],
447            ));
448        }
449
450        if num_names == 1 {
451            let final_name_span = alias_or_name_context(alias, names_vec[0], names_origin);
452            return Ok(vec![final_name_span]);
453        }
454
455        let final_names = names_vec
456            .into_iter()
457            .map(|name| ContextSpanned { value: name, origin: names_origin.clone() })
458            .collect();
459
460        Ok(final_names)
461    }
462
463    fn get_one_or_many_svc_paths_context(
464        names: ContextSpanned<OneOrMany<&BorrowedName>>,
465        alias: Option<&ContextSpanned<Path>>,
466        capability_type: &str,
467    ) -> Result<Vec<ContextSpanned<Path>>, Error> {
468        let names_origin = &names.origin;
469        let names_vec: Vec<_> = names.value.into_iter().collect();
470
471        match (names_vec.len(), alias) {
472            (_, None) => {
473                let generated_paths = names_vec
474                    .into_iter()
475                    .map(|n| {
476                        let new_path: Path = format!("/svc/{}", n).parse().unwrap();
477                        ContextSpanned { value: new_path, origin: names_origin.clone() }
478                    })
479                    .collect();
480                Ok(generated_paths)
481            }
482
483            (1, Some(spanned_alias)) => Ok(vec![spanned_alias.clone()]),
484
485            (_, Some(spanned_alias)) => Err(Error::validate_contexts(
486                format!(
487                    "\"path\" can only be specified when one `{}` is supplied.",
488                    capability_type,
489                ),
490                vec![spanned_alias.origin.clone()],
491            )),
492        }
493    }
494
495    capability_ids_from_context_names!(services_from_context, CapabilityId::Service);
496    capability_ids_from_context_names!(protocols_from_context, CapabilityId::Protocol);
497    capability_ids_from_context_names!(directories_from_context, CapabilityId::Directory);
498    capability_ids_from_context_names!(storages_from_context, CapabilityId::Storage);
499    capability_ids_from_context_names!(runners_from_context, CapabilityId::Runner);
500    capability_ids_from_context_names!(resolvers_from_context, CapabilityId::Resolver);
501    capability_ids_from_context_names!(event_streams_from_context, CapabilityId::EventStream);
502    capability_ids_from_context_names!(dictionaries_from_context, CapabilityId::Dictionary);
503    capability_ids_from_context_names!(configurations_from_context, CapabilityId::Configuration);
504
505    capability_ids_from_context_paths!(used_services_from_context, CapabilityId::UsedService);
506    capability_ids_from_context_paths!(used_protocols_from_context, CapabilityId::UsedProtocol);
507    capability_ids_from_context_paths!(
508        used_dictionaries_from_context,
509        CapabilityId::UsedDictionary
510    );
511}
512
513impl fmt::Display for CapabilityId<'_> {
514    /// Return the string ID of this clause.
515    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
516        match self {
517            CapabilityId::Service(n)
518            | CapabilityId::Storage(n)
519            | CapabilityId::Runner(n)
520            | CapabilityId::UsedRunner(n)
521            | CapabilityId::Resolver(n)
522            | CapabilityId::EventStream(n)
523            | CapabilityId::Configuration(n)
524            | CapabilityId::UsedConfiguration(n)
525            | CapabilityId::Dictionary(n) => write!(f, "{}", n),
526            CapabilityId::UsedService(p)
527            | CapabilityId::UsedProtocol(p)
528            | CapabilityId::UsedDirectory(p)
529            | CapabilityId::UsedStorage(p)
530            | CapabilityId::UsedEventStream(p)
531            | CapabilityId::UsedDictionary(p) => write!(f, "{}", p),
532            CapabilityId::UsedProtocolNumberedHandle(p) => write!(f, "{}", p),
533            CapabilityId::Protocol(p) | CapabilityId::Directory(p) => write!(f, "{}", p),
534        }
535    }
536}
537
538#[cfg(test)]
539mod tests {
540    use super::*;
541    use crate::types::offer::ContextOffer;
542    use assert_matches::assert_matches;
543    use std::path::PathBuf;
544    use std::sync::Arc;
545
546    #[test]
547    fn test_offer_service() -> Result<(), Error> {
548        let a: Name = "a".parse().unwrap();
549        let b: Name = "b".parse().unwrap();
550
551        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
552
553        assert_eq!(
554            CapabilityId::from_context_offer_expose(&ContextSpanned {
555                value: ContextOffer {
556                    service: Some(ContextSpanned {
557                        value: OneOrMany::One(a.clone()),
558                        origin: synthetic_origin.clone(),
559                    }),
560                    ..ContextOffer::default()
561                },
562                origin: synthetic_origin.clone(),
563            })?,
564            vec![(CapabilityId::Service(&a), synthetic_origin.clone())]
565        );
566
567        assert_eq!(
568            CapabilityId::from_context_offer_expose(&ContextSpanned {
569                value: ContextOffer {
570                    service: Some(ContextSpanned {
571                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
572                        origin: synthetic_origin.clone(),
573                    }),
574                    ..ContextOffer::default()
575                },
576                origin: synthetic_origin.clone(),
577            })?,
578            vec![
579                (CapabilityId::Service(&a), synthetic_origin.clone()),
580                (CapabilityId::Service(&b), synthetic_origin.clone())
581            ]
582        );
583
584        // "as" aliasing.
585        assert_eq!(
586            CapabilityId::from_context_offer_expose(&ContextSpanned {
587                value: ContextOffer {
588                    service: Some(ContextSpanned {
589                        value: OneOrMany::One(a.clone()),
590                        origin: synthetic_origin.clone(),
591                    }),
592                    r#as: Some(ContextSpanned {
593                        value: b.clone(),
594                        origin: synthetic_origin.clone()
595                    }),
596                    ..ContextOffer::default()
597                },
598                origin: synthetic_origin.clone(),
599            })?,
600            vec![(CapabilityId::Service(&b), synthetic_origin)]
601        );
602
603        Ok(())
604    }
605
606    #[test]
607    fn test_use_service() -> Result<(), Error> {
608        let a: Name = "a".parse().unwrap();
609        let b: Name = "b".parse().unwrap();
610
611        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
612
613        assert_eq!(
614            CapabilityId::from_context_use(&ContextSpanned {
615                value: ContextUse {
616                    service: Some(ContextSpanned {
617                        value: OneOrMany::One(a.clone()),
618                        origin: synthetic_origin.clone(),
619                    }),
620                    ..ContextUse::default()
621                },
622                origin: synthetic_origin.clone(),
623            })?,
624            vec![(CapabilityId::UsedService("/svc/a".parse().unwrap()), synthetic_origin.clone())]
625        );
626
627        assert_eq!(
628            CapabilityId::from_context_use(&ContextSpanned {
629                value: ContextUse {
630                    service: Some(ContextSpanned {
631                        value: OneOrMany::Many(vec![a.clone(), b.clone(),]),
632                        origin: synthetic_origin.clone(),
633                    }),
634                    ..ContextUse::default()
635                },
636                origin: synthetic_origin.clone(),
637            })?,
638            vec![
639                (CapabilityId::UsedService("/svc/a".parse().unwrap()), synthetic_origin.clone()),
640                (CapabilityId::UsedService("/svc/b".parse().unwrap()), synthetic_origin.clone())
641            ]
642        );
643
644        assert_eq!(
645            CapabilityId::from_context_use(&ContextSpanned {
646                value: ContextUse {
647                    service: Some(ContextSpanned {
648                        value: OneOrMany::One(a.clone()),
649                        origin: synthetic_origin.clone(),
650                    }),
651                    path: Some(ContextSpanned {
652                        value: "/b".parse().unwrap(),
653                        origin: synthetic_origin.clone(),
654                    }),
655                    ..ContextUse::default()
656                },
657                origin: synthetic_origin.clone(),
658            })?,
659            vec![(CapabilityId::UsedService("/b".parse().unwrap()), synthetic_origin.clone())]
660        );
661
662        Ok(())
663    }
664
665    #[test]
666    fn test_use_event_stream() -> Result<(), Error> {
667        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
668
669        assert_eq!(
670            CapabilityId::from_context_use(&ContextSpanned {
671                value: ContextUse {
672                    event_stream: Some(ContextSpanned {
673                        value: OneOrMany::One(Name::new("test".to_string()).unwrap()),
674                        origin: synthetic_origin.clone(),
675                    }),
676                    path: Some(ContextSpanned {
677                        value: cm_types::Path::new("/svc/myevent".to_string()).unwrap(),
678                        origin: synthetic_origin.clone(),
679                    }),
680                    ..ContextUse::default()
681                },
682                origin: synthetic_origin.clone(),
683            })?,
684            vec![(
685                CapabilityId::UsedEventStream("/svc/myevent".parse().unwrap()),
686                synthetic_origin.clone()
687            )]
688        );
689
690        assert_eq!(
691            CapabilityId::from_context_use(&ContextSpanned {
692                value: ContextUse {
693                    event_stream: Some(ContextSpanned {
694                        value: OneOrMany::One(Name::new("test".to_string()).unwrap()),
695                        origin: synthetic_origin.clone(),
696                    }),
697                    ..ContextUse::default()
698                },
699                origin: synthetic_origin.clone(),
700            })?,
701            vec![(
702                CapabilityId::UsedEventStream(
703                    "/svc/fuchsia.component.EventStream".parse().unwrap()
704                ),
705                synthetic_origin.clone()
706            )]
707        );
708
709        Ok(())
710    }
711
712    #[test]
713    fn test_offer_protocol() -> Result<(), Error> {
714        let a: Name = "a".parse().unwrap();
715        let b: Name = "b".parse().unwrap();
716
717        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
718
719        assert_eq!(
720            CapabilityId::from_context_offer_expose(&ContextSpanned {
721                value: ContextOffer {
722                    protocol: Some(ContextSpanned {
723                        value: OneOrMany::One(a.clone()),
724                        origin: synthetic_origin.clone(),
725                    }),
726                    ..ContextOffer::default()
727                },
728                origin: synthetic_origin.clone(),
729            })?,
730            vec![(CapabilityId::Protocol(&a), synthetic_origin.clone())]
731        );
732
733        assert_eq!(
734            CapabilityId::from_context_offer_expose(&ContextSpanned {
735                value: ContextOffer {
736                    protocol: Some(ContextSpanned {
737                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
738                        origin: synthetic_origin.clone(),
739                    }),
740                    ..ContextOffer::default()
741                },
742                origin: synthetic_origin.clone(),
743            })?,
744            vec![
745                (CapabilityId::Protocol(&a), synthetic_origin.clone()),
746                (CapabilityId::Protocol(&b), synthetic_origin)
747            ]
748        );
749
750        Ok(())
751    }
752
753    #[test]
754    fn test_use_protocol() -> Result<(), Error> {
755        let a: Name = "a".parse().unwrap();
756        let b: Name = "b".parse().unwrap();
757
758        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
759
760        assert_eq!(
761            CapabilityId::from_context_use(&ContextSpanned {
762                value: ContextUse {
763                    protocol: Some(ContextSpanned {
764                        value: OneOrMany::One(a.clone()),
765                        origin: synthetic_origin.clone(),
766                    }),
767                    ..ContextUse::default()
768                },
769                origin: synthetic_origin.clone(),
770            })?,
771            vec![(CapabilityId::UsedProtocol("/svc/a".parse().unwrap()), synthetic_origin.clone())]
772        );
773
774        assert_eq!(
775            CapabilityId::from_context_use(&ContextSpanned {
776                value: ContextUse {
777                    protocol: Some(ContextSpanned {
778                        value: OneOrMany::Many(vec![a.clone(), b.clone(),]),
779                        origin: synthetic_origin.clone(),
780                    }),
781                    ..ContextUse::default()
782                },
783                origin: synthetic_origin.clone(),
784            })?,
785            vec![
786                (CapabilityId::UsedProtocol("/svc/a".parse().unwrap()), synthetic_origin.clone()),
787                (CapabilityId::UsedProtocol("/svc/b".parse().unwrap()), synthetic_origin.clone())
788            ]
789        );
790
791        assert_eq!(
792            CapabilityId::from_context_use(&ContextSpanned {
793                value: ContextUse {
794                    protocol: Some(ContextSpanned {
795                        value: OneOrMany::One(a.clone()),
796                        origin: synthetic_origin.clone(),
797                    }),
798                    path: Some(ContextSpanned {
799                        value: "/b".parse().unwrap(),
800                        origin: synthetic_origin.clone(),
801                    }),
802                    ..ContextUse::default()
803                },
804                origin: synthetic_origin.clone(),
805            })?,
806            vec![(CapabilityId::UsedProtocol("/b".parse().unwrap()), synthetic_origin.clone())]
807        );
808
809        Ok(())
810    }
811
812    #[test]
813    fn test_offer_directory() -> Result<(), Error> {
814        let a: Name = "a".parse().unwrap();
815        let b: Name = "b".parse().unwrap();
816
817        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
818
819        assert_eq!(
820            CapabilityId::from_context_offer_expose(&ContextSpanned {
821                value: ContextOffer {
822                    directory: Some(ContextSpanned {
823                        value: OneOrMany::One(a.clone()),
824                        origin: synthetic_origin.clone(),
825                    }),
826                    ..ContextOffer::default()
827                },
828                origin: synthetic_origin.clone(),
829            })?,
830            vec![(CapabilityId::Directory(&a), synthetic_origin.clone())]
831        );
832
833        assert_eq!(
834            CapabilityId::from_context_offer_expose(&ContextSpanned {
835                value: ContextOffer {
836                    directory: Some(ContextSpanned {
837                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
838                        origin: synthetic_origin.clone(),
839                    }),
840                    ..ContextOffer::default()
841                },
842                origin: synthetic_origin.clone(),
843            })?,
844            vec![
845                (CapabilityId::Directory(&a), synthetic_origin.clone()),
846                (CapabilityId::Directory(&b), synthetic_origin.clone())
847            ]
848        );
849
850        Ok(())
851    }
852
853    #[test]
854    fn test_use_directory() -> Result<(), Error> {
855        let a: Name = "a".parse().unwrap();
856
857        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
858
859        assert_eq!(
860            CapabilityId::from_context_use(&ContextSpanned {
861                value: ContextUse {
862                    directory: Some(ContextSpanned {
863                        value: a.clone(),
864                        origin: synthetic_origin.clone(),
865                    }),
866                    path: Some(ContextSpanned {
867                        value: "/b".parse().unwrap(),
868                        origin: synthetic_origin.clone(),
869                    }),
870                    ..ContextUse::default()
871                },
872                origin: synthetic_origin.clone(),
873            })?,
874            vec![(CapabilityId::UsedDirectory("/b".parse().unwrap()), synthetic_origin.clone())]
875        );
876
877        Ok(())
878    }
879
880    #[test]
881    fn test_offer_storage() -> Result<(), Error> {
882        let a: Name = "a".parse().unwrap();
883        let b: Name = "b".parse().unwrap();
884
885        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
886
887        assert_eq!(
888            CapabilityId::from_context_offer_expose(&ContextSpanned {
889                value: ContextOffer {
890                    storage: Some(ContextSpanned {
891                        value: OneOrMany::One(a.clone()),
892                        origin: synthetic_origin.clone(),
893                    }),
894                    ..ContextOffer::default()
895                },
896                origin: synthetic_origin.clone(),
897            })?,
898            vec![(CapabilityId::Storage(&a), synthetic_origin.clone())]
899        );
900
901        assert_eq!(
902            CapabilityId::from_context_offer_expose(&ContextSpanned {
903                value: ContextOffer {
904                    storage: Some(ContextSpanned {
905                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
906                        origin: synthetic_origin.clone(),
907                    }),
908                    ..ContextOffer::default()
909                },
910                origin: synthetic_origin.clone(),
911            })?,
912            vec![
913                (CapabilityId::Storage(&a), synthetic_origin.clone()),
914                (CapabilityId::Storage(&b), synthetic_origin.clone())
915            ]
916        );
917
918        Ok(())
919    }
920
921    #[test]
922    fn test_use_storage() -> Result<(), Error> {
923        let a: Name = "a".parse().unwrap();
924
925        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
926
927        assert_eq!(
928            CapabilityId::from_context_use(&ContextSpanned {
929                value: ContextUse {
930                    storage: Some(ContextSpanned {
931                        value: a.clone(),
932                        origin: synthetic_origin.clone(),
933                    }),
934                    path: Some(ContextSpanned {
935                        value: "/b".parse().unwrap(),
936                        origin: synthetic_origin.clone(),
937                    }),
938                    ..ContextUse::default()
939                },
940                origin: synthetic_origin.clone(),
941            })?,
942            vec![(CapabilityId::UsedStorage("/b".parse().unwrap()), synthetic_origin.clone())]
943        );
944
945        Ok(())
946    }
947
948    #[test]
949    fn test_use_runner() -> Result<(), Error> {
950        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
951
952        assert_eq!(
953            CapabilityId::from_context_use(&ContextSpanned {
954                value: ContextUse {
955                    runner: Some(ContextSpanned {
956                        value: "elf".parse().unwrap(),
957                        origin: synthetic_origin.clone(),
958                    }),
959                    ..ContextUse::default()
960                },
961                origin: synthetic_origin.clone(),
962            })?,
963            vec![(
964                CapabilityId::UsedRunner(BorrowedName::new("elf").unwrap()),
965                synthetic_origin.clone()
966            )]
967        );
968
969        Ok(())
970    }
971
972    #[test]
973    fn test_offer_dictionary() -> Result<(), Error> {
974        let a: Name = "a".parse().unwrap();
975        let b: Name = "b".parse().unwrap();
976
977        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
978
979        assert_eq!(
980            CapabilityId::from_context_offer_expose(&ContextSpanned {
981                value: ContextOffer {
982                    dictionary: Some(ContextSpanned {
983                        value: OneOrMany::One(a.clone()),
984                        origin: synthetic_origin.clone(),
985                    }),
986                    ..ContextOffer::default()
987                },
988                origin: synthetic_origin.clone(),
989            })?,
990            vec![(CapabilityId::Dictionary(&a), synthetic_origin.clone())]
991        );
992
993        assert_eq!(
994            CapabilityId::from_context_offer_expose(&ContextSpanned {
995                value: ContextOffer {
996                    dictionary: Some(ContextSpanned {
997                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
998                        origin: synthetic_origin.clone(),
999                    }),
1000                    ..ContextOffer::default()
1001                },
1002                origin: synthetic_origin.clone(),
1003            })?,
1004            vec![
1005                (CapabilityId::Dictionary(&a), synthetic_origin.clone()),
1006                (CapabilityId::Dictionary(&b), synthetic_origin.clone())
1007            ]
1008        );
1009
1010        Ok(())
1011    }
1012
1013    #[test]
1014    fn test_use_dictionary() -> Result<(), Error> {
1015        let a: Name = "a".parse().unwrap();
1016        let b: Name = "b".parse().unwrap();
1017
1018        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
1019
1020        assert_eq!(
1021            CapabilityId::from_context_use(&ContextSpanned {
1022                value: ContextUse {
1023                    dictionary: Some(ContextSpanned {
1024                        value: OneOrMany::One(a.clone()),
1025                        origin: synthetic_origin.clone(),
1026                    }),
1027                    ..ContextUse::default()
1028                },
1029                origin: synthetic_origin.clone(),
1030            })?,
1031            vec![(
1032                CapabilityId::UsedDictionary("/svc/a".parse().unwrap()),
1033                synthetic_origin.clone()
1034            )]
1035        );
1036
1037        assert_eq!(
1038            CapabilityId::from_context_use(&ContextSpanned {
1039                value: ContextUse {
1040                    dictionary: Some(ContextSpanned {
1041                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
1042                        origin: synthetic_origin.clone(),
1043                    }),
1044                    ..ContextUse::default()
1045                },
1046                origin: synthetic_origin.clone(),
1047            })?,
1048            vec![
1049                (CapabilityId::UsedDictionary("/svc/a".parse().unwrap()), synthetic_origin.clone()),
1050                (CapabilityId::UsedDictionary("/svc/b".parse().unwrap()), synthetic_origin.clone())
1051            ]
1052        );
1053
1054        assert_eq!(
1055            CapabilityId::from_context_use(&ContextSpanned {
1056                value: ContextUse {
1057                    dictionary: Some(ContextSpanned {
1058                        value: OneOrMany::One(a.clone()),
1059                        origin: synthetic_origin.clone(),
1060                    }),
1061                    path: Some(ContextSpanned {
1062                        value: "/b".parse().unwrap(),
1063                        origin: synthetic_origin.clone()
1064                    }),
1065                    ..ContextUse::default()
1066                },
1067                origin: synthetic_origin.clone(),
1068            })?,
1069            vec![(CapabilityId::UsedDictionary("/b".parse().unwrap()), synthetic_origin.clone())]
1070        );
1071
1072        Ok(())
1073    }
1074
1075    #[test]
1076    fn test_errors() -> Result<(), Error> {
1077        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
1078
1079        assert_matches!(
1080            CapabilityId::from_context_offer_expose(&ContextSpanned {
1081                value: ContextOffer::default(),
1082                origin: synthetic_origin
1083            }),
1084            Err(_)
1085        );
1086
1087        Ok(())
1088    }
1089}