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            .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 =
304            clause.supported().iter().map(|k| format!("\"{}\"", k)).collect::<Vec<_>>().join(", ");
305        Err(Error::validate_context(
306            format!(
307                "`{}` declaration is missing a capability keyword, one of: {}",
308                clause.decl_type(),
309                supported_keywords,
310            ),
311            Some(origin.clone()),
312        ))
313    }
314
315    /// Given a ContextUse clause, return the set of target identifiers.
316    ///
317    /// When only one capability identifier is specified, the target identifier name is derived
318    /// using the "path" clause. If a "path" clause is not specified, the target identifier is the
319    /// same name as the source.
320    ///
321    /// When multiple capability identifiers are specified, the target names are the same as the
322    /// source names.
323    pub fn from_context_use(
324        use_input: &'a ContextSpanned<ContextUse>,
325    ) -> Result<Vec<(Self, Arc<PathBuf>)>, Error> {
326        let use_ = &use_input.value;
327        let origin = &use_input.origin;
328
329        let alias = use_.path.as_ref();
330
331        if let Some(n) = option_one_or_many_as_ref_context(&use_.service) {
332            return Ok(Self::used_services_from_context(Self::get_one_or_many_svc_paths_context(
333                n,
334                alias,
335                use_input.capability_type(Some(origin.clone())).unwrap(),
336            )?));
337        } else if let Some(n) = option_one_or_many_as_ref_context(&use_.protocol) {
338            if let Some(numbered_handle) = &use_.numbered_handle {
339                return Ok(n
340                    .value
341                    .iter()
342                    .map(|_| {
343                        (
344                            CapabilityId::UsedProtocolNumberedHandle(numbered_handle.value),
345                            n.origin.clone(),
346                        )
347                    })
348                    .collect());
349            }
350
351            return Ok(Self::used_protocols_from_context(Self::get_one_or_many_svc_paths_context(
352                n,
353                alias,
354                use_input.capability_type(Some(origin.clone())).unwrap(),
355            )?));
356        } else if let Some(_) = &use_.directory {
357            if use_.path.is_none() {
358                return Err(Error::validate_context(
359                    "\"path\" should be present for `use directory`.",
360                    Some(origin.clone()),
361                ));
362            }
363            return Ok(vec![(
364                CapabilityId::UsedDirectory(use_.path.as_ref().unwrap().value.clone()),
365                origin.clone(),
366            )]);
367        } else if let Some(_) = &use_.storage {
368            if use_.path.is_none() {
369                return Err(Error::validate_context(
370                    "\"path\" should be present for `use storage`.",
371                    Some(origin.clone()),
372                ));
373            }
374            return Ok(vec![(
375                CapabilityId::UsedStorage(use_.path.as_ref().unwrap().value.clone()),
376                origin.clone(),
377            )]);
378        } else if let Some(_) = &use_.event_stream {
379            if let Some(path) = &use_.path {
380                return Ok(vec![(
381                    CapabilityId::UsedEventStream(path.value.clone()),
382                    origin.clone(),
383                )]);
384            }
385            return Ok(vec![(
386                CapabilityId::UsedEventStream(Path::new("/svc/fuchsia.component.EventStream")?),
387                origin.clone(),
388            )]);
389        } else if let Some(n) = &use_.runner {
390            return Ok(vec![(CapabilityId::UsedRunner(&n.value), n.origin.clone())]);
391        } else if let Some(_) = &use_.config {
392            return match &use_.key {
393                None => Err(Error::validate_context(
394                    "\"key\" should be present for `use config`.",
395                    Some(origin.clone()),
396                )),
397                Some(name) => {
398                    Ok(vec![(CapabilityId::UsedConfiguration(&name.value), origin.clone())])
399                }
400            };
401        } else if let Some(n) = option_one_or_many_as_ref_context(&use_.dictionary) {
402            return Ok(Self::used_dictionaries_from_context(
403                Self::get_one_or_many_svc_paths_context(
404                    n,
405                    alias,
406                    use_input.capability_type(Some(origin.clone())).unwrap(),
407                )?,
408            ));
409        }
410
411        // Unsupported capability type.
412        let supported_keywords = use_input
413            .supported()
414            .iter()
415            .map(|k| format!("\"{}\"", k))
416            .collect::<Vec<_>>()
417            .join(", ");
418
419        Err(Error::validate_context(
420            format!(
421                "`{}` declaration is missing a capability keyword, one of: {}",
422                use_input.decl_type(),
423                supported_keywords,
424            ),
425            Some(origin.clone()),
426        ))
427    }
428
429    /// Returns the target names as a `Vec` from a declaration with `names` and `alias` as a `Vec`.
430    fn get_one_or_many_names_context<'b>(
431        name_wrapper: ContextSpanned<OneOrMany<&'b BorrowedName>>,
432        alias: Option<ContextSpanned<&'b BorrowedName>>,
433        capability_type: &str,
434    ) -> Result<Vec<ContextSpanned<&'b BorrowedName>>, Error> {
435        let names_origin = name_wrapper.origin;
436        let names_vec: Vec<&'b BorrowedName> = name_wrapper.value.into_iter().collect();
437        let num_names = names_vec.len();
438
439        if num_names > 1 && alias.is_some() {
440            return Err(Error::validate_contexts(
441                format!("\"as\" can only be specified when one `{}` is supplied.", capability_type),
442                vec![alias.map(|s| s.origin).unwrap_or(names_origin)],
443            ));
444        }
445
446        if num_names == 1 {
447            let final_name_span = alias_or_name_context(alias, names_vec[0], names_origin);
448            return Ok(vec![final_name_span]);
449        }
450
451        let final_names = names_vec
452            .into_iter()
453            .map(|name| ContextSpanned { value: name, origin: names_origin.clone() })
454            .collect();
455
456        Ok(final_names)
457    }
458
459    fn get_one_or_many_svc_paths_context(
460        names: ContextSpanned<OneOrMany<&BorrowedName>>,
461        alias: Option<&ContextSpanned<Path>>,
462        capability_type: &str,
463    ) -> Result<Vec<ContextSpanned<Path>>, Error> {
464        let names_origin = &names.origin;
465        let names_vec: Vec<_> = names.value.into_iter().collect();
466
467        match (names_vec.len(), alias) {
468            (_, None) => {
469                let generated_paths = names_vec
470                    .into_iter()
471                    .map(|n| {
472                        let new_path: Path = format!("/svc/{}", n).parse().unwrap();
473                        ContextSpanned { value: new_path, origin: names_origin.clone() }
474                    })
475                    .collect();
476                Ok(generated_paths)
477            }
478
479            (1, Some(spanned_alias)) => Ok(vec![spanned_alias.clone()]),
480
481            (_, Some(spanned_alias)) => Err(Error::validate_contexts(
482                format!(
483                    "\"path\" can only be specified when one `{}` is supplied.",
484                    capability_type,
485                ),
486                vec![spanned_alias.origin.clone()],
487            )),
488        }
489    }
490
491    capability_ids_from_context_names!(services_from_context, CapabilityId::Service);
492    capability_ids_from_context_names!(protocols_from_context, CapabilityId::Protocol);
493    capability_ids_from_context_names!(directories_from_context, CapabilityId::Directory);
494    capability_ids_from_context_names!(storages_from_context, CapabilityId::Storage);
495    capability_ids_from_context_names!(runners_from_context, CapabilityId::Runner);
496    capability_ids_from_context_names!(resolvers_from_context, CapabilityId::Resolver);
497    capability_ids_from_context_names!(event_streams_from_context, CapabilityId::EventStream);
498    capability_ids_from_context_names!(dictionaries_from_context, CapabilityId::Dictionary);
499    capability_ids_from_context_names!(configurations_from_context, CapabilityId::Configuration);
500
501    capability_ids_from_context_paths!(used_services_from_context, CapabilityId::UsedService);
502    capability_ids_from_context_paths!(used_protocols_from_context, CapabilityId::UsedProtocol);
503    capability_ids_from_context_paths!(
504        used_dictionaries_from_context,
505        CapabilityId::UsedDictionary
506    );
507}
508
509impl fmt::Display for CapabilityId<'_> {
510    /// Return the string ID of this clause.
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        match self {
513            CapabilityId::Service(n)
514            | CapabilityId::Storage(n)
515            | CapabilityId::Runner(n)
516            | CapabilityId::UsedRunner(n)
517            | CapabilityId::Resolver(n)
518            | CapabilityId::EventStream(n)
519            | CapabilityId::Configuration(n)
520            | CapabilityId::UsedConfiguration(n)
521            | CapabilityId::Dictionary(n) => write!(f, "{}", n),
522            CapabilityId::UsedService(p)
523            | CapabilityId::UsedProtocol(p)
524            | CapabilityId::UsedDirectory(p)
525            | CapabilityId::UsedStorage(p)
526            | CapabilityId::UsedEventStream(p)
527            | CapabilityId::UsedDictionary(p) => write!(f, "{}", p),
528            CapabilityId::UsedProtocolNumberedHandle(p) => write!(f, "{}", p),
529            CapabilityId::Protocol(p) | CapabilityId::Directory(p) => write!(f, "{}", p),
530        }
531    }
532}
533
534#[cfg(test)]
535mod tests {
536    use super::*;
537    use crate::types::offer::ContextOffer;
538    use assert_matches::assert_matches;
539    use std::path::PathBuf;
540    use std::sync::Arc;
541
542    #[test]
543    fn test_offer_service() -> Result<(), Error> {
544        let a: Name = "a".parse().unwrap();
545        let b: Name = "b".parse().unwrap();
546
547        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
548
549        assert_eq!(
550            CapabilityId::from_context_offer_expose(&ContextSpanned {
551                value: ContextOffer {
552                    service: Some(ContextSpanned {
553                        value: OneOrMany::One(a.clone()),
554                        origin: synthetic_origin.clone(),
555                    }),
556                    ..ContextOffer::default()
557                },
558                origin: synthetic_origin.clone(),
559            })?,
560            vec![(CapabilityId::Service(&a), synthetic_origin.clone())]
561        );
562
563        assert_eq!(
564            CapabilityId::from_context_offer_expose(&ContextSpanned {
565                value: ContextOffer {
566                    service: Some(ContextSpanned {
567                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
568                        origin: synthetic_origin.clone(),
569                    }),
570                    ..ContextOffer::default()
571                },
572                origin: synthetic_origin.clone(),
573            })?,
574            vec![
575                (CapabilityId::Service(&a), synthetic_origin.clone()),
576                (CapabilityId::Service(&b), synthetic_origin.clone())
577            ]
578        );
579
580        // "as" aliasing.
581        assert_eq!(
582            CapabilityId::from_context_offer_expose(&ContextSpanned {
583                value: ContextOffer {
584                    service: Some(ContextSpanned {
585                        value: OneOrMany::One(a.clone()),
586                        origin: synthetic_origin.clone(),
587                    }),
588                    r#as: Some(ContextSpanned {
589                        value: b.clone(),
590                        origin: synthetic_origin.clone()
591                    }),
592                    ..ContextOffer::default()
593                },
594                origin: synthetic_origin.clone(),
595            })?,
596            vec![(CapabilityId::Service(&b), synthetic_origin)]
597        );
598
599        Ok(())
600    }
601
602    #[test]
603    fn test_use_service() -> Result<(), Error> {
604        let a: Name = "a".parse().unwrap();
605        let b: Name = "b".parse().unwrap();
606
607        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
608
609        assert_eq!(
610            CapabilityId::from_context_use(&ContextSpanned {
611                value: ContextUse {
612                    service: Some(ContextSpanned {
613                        value: OneOrMany::One(a.clone()),
614                        origin: synthetic_origin.clone(),
615                    }),
616                    ..ContextUse::default()
617                },
618                origin: synthetic_origin.clone(),
619            })?,
620            vec![(CapabilityId::UsedService("/svc/a".parse().unwrap()), synthetic_origin.clone())]
621        );
622
623        assert_eq!(
624            CapabilityId::from_context_use(&ContextSpanned {
625                value: ContextUse {
626                    service: Some(ContextSpanned {
627                        value: OneOrMany::Many(vec![a.clone(), b.clone(),]),
628                        origin: synthetic_origin.clone(),
629                    }),
630                    ..ContextUse::default()
631                },
632                origin: synthetic_origin.clone(),
633            })?,
634            vec![
635                (CapabilityId::UsedService("/svc/a".parse().unwrap()), synthetic_origin.clone()),
636                (CapabilityId::UsedService("/svc/b".parse().unwrap()), synthetic_origin.clone())
637            ]
638        );
639
640        assert_eq!(
641            CapabilityId::from_context_use(&ContextSpanned {
642                value: ContextUse {
643                    service: Some(ContextSpanned {
644                        value: OneOrMany::One(a.clone()),
645                        origin: synthetic_origin.clone(),
646                    }),
647                    path: Some(ContextSpanned {
648                        value: "/b".parse().unwrap(),
649                        origin: synthetic_origin.clone(),
650                    }),
651                    ..ContextUse::default()
652                },
653                origin: synthetic_origin.clone(),
654            })?,
655            vec![(CapabilityId::UsedService("/b".parse().unwrap()), synthetic_origin.clone())]
656        );
657
658        Ok(())
659    }
660
661    #[test]
662    fn test_use_event_stream() -> Result<(), Error> {
663        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
664
665        assert_eq!(
666            CapabilityId::from_context_use(&ContextSpanned {
667                value: ContextUse {
668                    event_stream: Some(ContextSpanned {
669                        value: OneOrMany::One(Name::new("test".to_string()).unwrap()),
670                        origin: synthetic_origin.clone(),
671                    }),
672                    path: Some(ContextSpanned {
673                        value: cm_types::Path::new("/svc/myevent".to_string()).unwrap(),
674                        origin: synthetic_origin.clone(),
675                    }),
676                    ..ContextUse::default()
677                },
678                origin: synthetic_origin.clone(),
679            })?,
680            vec![(
681                CapabilityId::UsedEventStream("/svc/myevent".parse().unwrap()),
682                synthetic_origin.clone()
683            )]
684        );
685
686        assert_eq!(
687            CapabilityId::from_context_use(&ContextSpanned {
688                value: ContextUse {
689                    event_stream: Some(ContextSpanned {
690                        value: OneOrMany::One(Name::new("test".to_string()).unwrap()),
691                        origin: synthetic_origin.clone(),
692                    }),
693                    ..ContextUse::default()
694                },
695                origin: synthetic_origin.clone(),
696            })?,
697            vec![(
698                CapabilityId::UsedEventStream(
699                    "/svc/fuchsia.component.EventStream".parse().unwrap()
700                ),
701                synthetic_origin.clone()
702            )]
703        );
704
705        Ok(())
706    }
707
708    #[test]
709    fn test_offer_protocol() -> Result<(), Error> {
710        let a: Name = "a".parse().unwrap();
711        let b: Name = "b".parse().unwrap();
712
713        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
714
715        assert_eq!(
716            CapabilityId::from_context_offer_expose(&ContextSpanned {
717                value: ContextOffer {
718                    protocol: Some(ContextSpanned {
719                        value: OneOrMany::One(a.clone()),
720                        origin: synthetic_origin.clone(),
721                    }),
722                    ..ContextOffer::default()
723                },
724                origin: synthetic_origin.clone(),
725            })?,
726            vec![(CapabilityId::Protocol(&a), synthetic_origin.clone())]
727        );
728
729        assert_eq!(
730            CapabilityId::from_context_offer_expose(&ContextSpanned {
731                value: ContextOffer {
732                    protocol: Some(ContextSpanned {
733                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
734                        origin: synthetic_origin.clone(),
735                    }),
736                    ..ContextOffer::default()
737                },
738                origin: synthetic_origin.clone(),
739            })?,
740            vec![
741                (CapabilityId::Protocol(&a), synthetic_origin.clone()),
742                (CapabilityId::Protocol(&b), synthetic_origin)
743            ]
744        );
745
746        Ok(())
747    }
748
749    #[test]
750    fn test_use_protocol() -> Result<(), Error> {
751        let a: Name = "a".parse().unwrap();
752        let b: Name = "b".parse().unwrap();
753
754        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
755
756        assert_eq!(
757            CapabilityId::from_context_use(&ContextSpanned {
758                value: ContextUse {
759                    protocol: Some(ContextSpanned {
760                        value: OneOrMany::One(a.clone()),
761                        origin: synthetic_origin.clone(),
762                    }),
763                    ..ContextUse::default()
764                },
765                origin: synthetic_origin.clone(),
766            })?,
767            vec![(CapabilityId::UsedProtocol("/svc/a".parse().unwrap()), synthetic_origin.clone())]
768        );
769
770        assert_eq!(
771            CapabilityId::from_context_use(&ContextSpanned {
772                value: ContextUse {
773                    protocol: Some(ContextSpanned {
774                        value: OneOrMany::Many(vec![a.clone(), b.clone(),]),
775                        origin: synthetic_origin.clone(),
776                    }),
777                    ..ContextUse::default()
778                },
779                origin: synthetic_origin.clone(),
780            })?,
781            vec![
782                (CapabilityId::UsedProtocol("/svc/a".parse().unwrap()), synthetic_origin.clone()),
783                (CapabilityId::UsedProtocol("/svc/b".parse().unwrap()), synthetic_origin.clone())
784            ]
785        );
786
787        assert_eq!(
788            CapabilityId::from_context_use(&ContextSpanned {
789                value: ContextUse {
790                    protocol: Some(ContextSpanned {
791                        value: OneOrMany::One(a.clone()),
792                        origin: synthetic_origin.clone(),
793                    }),
794                    path: Some(ContextSpanned {
795                        value: "/b".parse().unwrap(),
796                        origin: synthetic_origin.clone(),
797                    }),
798                    ..ContextUse::default()
799                },
800                origin: synthetic_origin.clone(),
801            })?,
802            vec![(CapabilityId::UsedProtocol("/b".parse().unwrap()), synthetic_origin.clone())]
803        );
804
805        Ok(())
806    }
807
808    #[test]
809    fn test_offer_directory() -> Result<(), Error> {
810        let a: Name = "a".parse().unwrap();
811        let b: Name = "b".parse().unwrap();
812
813        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
814
815        assert_eq!(
816            CapabilityId::from_context_offer_expose(&ContextSpanned {
817                value: ContextOffer {
818                    directory: Some(ContextSpanned {
819                        value: OneOrMany::One(a.clone()),
820                        origin: synthetic_origin.clone(),
821                    }),
822                    ..ContextOffer::default()
823                },
824                origin: synthetic_origin.clone(),
825            })?,
826            vec![(CapabilityId::Directory(&a), synthetic_origin.clone())]
827        );
828
829        assert_eq!(
830            CapabilityId::from_context_offer_expose(&ContextSpanned {
831                value: ContextOffer {
832                    directory: Some(ContextSpanned {
833                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
834                        origin: synthetic_origin.clone(),
835                    }),
836                    ..ContextOffer::default()
837                },
838                origin: synthetic_origin.clone(),
839            })?,
840            vec![
841                (CapabilityId::Directory(&a), synthetic_origin.clone()),
842                (CapabilityId::Directory(&b), synthetic_origin.clone())
843            ]
844        );
845
846        Ok(())
847    }
848
849    #[test]
850    fn test_use_directory() -> Result<(), Error> {
851        let a: Name = "a".parse().unwrap();
852
853        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
854
855        assert_eq!(
856            CapabilityId::from_context_use(&ContextSpanned {
857                value: ContextUse {
858                    directory: Some(ContextSpanned {
859                        value: a.clone(),
860                        origin: synthetic_origin.clone(),
861                    }),
862                    path: Some(ContextSpanned {
863                        value: "/b".parse().unwrap(),
864                        origin: synthetic_origin.clone(),
865                    }),
866                    ..ContextUse::default()
867                },
868                origin: synthetic_origin.clone(),
869            })?,
870            vec![(CapabilityId::UsedDirectory("/b".parse().unwrap()), synthetic_origin.clone())]
871        );
872
873        Ok(())
874    }
875
876    #[test]
877    fn test_offer_storage() -> Result<(), Error> {
878        let a: Name = "a".parse().unwrap();
879        let b: Name = "b".parse().unwrap();
880
881        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
882
883        assert_eq!(
884            CapabilityId::from_context_offer_expose(&ContextSpanned {
885                value: ContextOffer {
886                    storage: Some(ContextSpanned {
887                        value: OneOrMany::One(a.clone()),
888                        origin: synthetic_origin.clone(),
889                    }),
890                    ..ContextOffer::default()
891                },
892                origin: synthetic_origin.clone(),
893            })?,
894            vec![(CapabilityId::Storage(&a), synthetic_origin.clone())]
895        );
896
897        assert_eq!(
898            CapabilityId::from_context_offer_expose(&ContextSpanned {
899                value: ContextOffer {
900                    storage: Some(ContextSpanned {
901                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
902                        origin: synthetic_origin.clone(),
903                    }),
904                    ..ContextOffer::default()
905                },
906                origin: synthetic_origin.clone(),
907            })?,
908            vec![
909                (CapabilityId::Storage(&a), synthetic_origin.clone()),
910                (CapabilityId::Storage(&b), synthetic_origin.clone())
911            ]
912        );
913
914        Ok(())
915    }
916
917    #[test]
918    fn test_use_storage() -> Result<(), Error> {
919        let a: Name = "a".parse().unwrap();
920
921        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
922
923        assert_eq!(
924            CapabilityId::from_context_use(&ContextSpanned {
925                value: ContextUse {
926                    storage: Some(ContextSpanned {
927                        value: a.clone(),
928                        origin: synthetic_origin.clone(),
929                    }),
930                    path: Some(ContextSpanned {
931                        value: "/b".parse().unwrap(),
932                        origin: synthetic_origin.clone(),
933                    }),
934                    ..ContextUse::default()
935                },
936                origin: synthetic_origin.clone(),
937            })?,
938            vec![(CapabilityId::UsedStorage("/b".parse().unwrap()), synthetic_origin.clone())]
939        );
940
941        Ok(())
942    }
943
944    #[test]
945    fn test_use_runner() -> Result<(), Error> {
946        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
947
948        assert_eq!(
949            CapabilityId::from_context_use(&ContextSpanned {
950                value: ContextUse {
951                    runner: Some(ContextSpanned {
952                        value: "elf".parse().unwrap(),
953                        origin: synthetic_origin.clone(),
954                    }),
955                    ..ContextUse::default()
956                },
957                origin: synthetic_origin.clone(),
958            })?,
959            vec![(
960                CapabilityId::UsedRunner(BorrowedName::new("elf").unwrap()),
961                synthetic_origin.clone()
962            )]
963        );
964
965        Ok(())
966    }
967
968    #[test]
969    fn test_offer_dictionary() -> Result<(), Error> {
970        let a: Name = "a".parse().unwrap();
971        let b: Name = "b".parse().unwrap();
972
973        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
974
975        assert_eq!(
976            CapabilityId::from_context_offer_expose(&ContextSpanned {
977                value: ContextOffer {
978                    dictionary: Some(ContextSpanned {
979                        value: OneOrMany::One(a.clone()),
980                        origin: synthetic_origin.clone(),
981                    }),
982                    ..ContextOffer::default()
983                },
984                origin: synthetic_origin.clone(),
985            })?,
986            vec![(CapabilityId::Dictionary(&a), synthetic_origin.clone())]
987        );
988
989        assert_eq!(
990            CapabilityId::from_context_offer_expose(&ContextSpanned {
991                value: ContextOffer {
992                    dictionary: Some(ContextSpanned {
993                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
994                        origin: synthetic_origin.clone(),
995                    }),
996                    ..ContextOffer::default()
997                },
998                origin: synthetic_origin.clone(),
999            })?,
1000            vec![
1001                (CapabilityId::Dictionary(&a), synthetic_origin.clone()),
1002                (CapabilityId::Dictionary(&b), synthetic_origin.clone())
1003            ]
1004        );
1005
1006        Ok(())
1007    }
1008
1009    #[test]
1010    fn test_use_dictionary() -> Result<(), Error> {
1011        let a: Name = "a".parse().unwrap();
1012        let b: Name = "b".parse().unwrap();
1013
1014        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
1015
1016        assert_eq!(
1017            CapabilityId::from_context_use(&ContextSpanned {
1018                value: ContextUse {
1019                    dictionary: Some(ContextSpanned {
1020                        value: OneOrMany::One(a.clone()),
1021                        origin: synthetic_origin.clone(),
1022                    }),
1023                    ..ContextUse::default()
1024                },
1025                origin: synthetic_origin.clone(),
1026            })?,
1027            vec![(
1028                CapabilityId::UsedDictionary("/svc/a".parse().unwrap()),
1029                synthetic_origin.clone()
1030            )]
1031        );
1032
1033        assert_eq!(
1034            CapabilityId::from_context_use(&ContextSpanned {
1035                value: ContextUse {
1036                    dictionary: Some(ContextSpanned {
1037                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
1038                        origin: synthetic_origin.clone(),
1039                    }),
1040                    ..ContextUse::default()
1041                },
1042                origin: synthetic_origin.clone(),
1043            })?,
1044            vec![
1045                (CapabilityId::UsedDictionary("/svc/a".parse().unwrap()), synthetic_origin.clone()),
1046                (CapabilityId::UsedDictionary("/svc/b".parse().unwrap()), synthetic_origin.clone())
1047            ]
1048        );
1049
1050        assert_eq!(
1051            CapabilityId::from_context_use(&ContextSpanned {
1052                value: ContextUse {
1053                    dictionary: Some(ContextSpanned {
1054                        value: OneOrMany::One(a.clone()),
1055                        origin: synthetic_origin.clone(),
1056                    }),
1057                    path: Some(ContextSpanned {
1058                        value: "/b".parse().unwrap(),
1059                        origin: synthetic_origin.clone()
1060                    }),
1061                    ..ContextUse::default()
1062                },
1063                origin: synthetic_origin.clone(),
1064            })?,
1065            vec![(CapabilityId::UsedDictionary("/b".parse().unwrap()), synthetic_origin.clone())]
1066        );
1067
1068        Ok(())
1069    }
1070
1071    #[test]
1072    fn test_errors() -> Result<(), Error> {
1073        let synthetic_origin = Arc::new(PathBuf::from("synthetic"));
1074
1075        assert_matches!(
1076            CapabilityId::from_context_offer_expose(&ContextSpanned {
1077                value: ContextOffer::default(),
1078                origin: synthetic_origin
1079            }),
1080            Err(_)
1081        );
1082
1083        Ok(())
1084    }
1085}