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::sync::Arc;
17
18/// A name/identity of a capability exposed/offered to another component.
19///
20/// Exposed or offered capabilities have an identifier whose format
21/// depends on the capability type. For directories and services this is
22/// a path, while for storage this is a storage name. Paths and storage
23/// names, however, are in different conceptual namespaces, and can't
24/// collide with each other.
25///
26/// This enum allows such names to be specified disambiguating what
27/// namespace they are in.
28#[derive(Debug, PartialEq, Eq, Hash, Clone)]
29pub enum CapabilityId<'a> {
30    Service(&'a BorrowedName),
31    Protocol(&'a BorrowedName),
32    Directory(&'a BorrowedName),
33    // A service in a `use` declaration has a target path in the component's namespace.
34    UsedService(Path),
35    // A protocol in a `use` declaration has a target path in the component's namespace.
36    UsedProtocol(Path),
37    // A protocol in a `use` declaration has a numbered handle in the component's namespace.
38    UsedProtocolNumberedHandle(HandleType),
39    // A directory in a `use` declaration has a target path in the component's namespace.
40    UsedDirectory(Path),
41    // A storage in a `use` declaration has a target path in the component's namespace.
42    UsedStorage(Path),
43    // An event stream in a `use` declaration has a target path in the component's namespace.
44    UsedEventStream(Path),
45    // A configuration in a `use` declaration has a target name that matches a config.
46    UsedConfiguration(&'a BorrowedName),
47    UsedRunner(&'a BorrowedName),
48    // A dictionary in a `use` declaration that has a target path in the component's namespace.
49    UsedDictionary(Path),
50    Storage(&'a BorrowedName),
51    Runner(&'a BorrowedName),
52    Resolver(&'a BorrowedName),
53    EventStream(&'a BorrowedName),
54    Dictionary(&'a BorrowedName),
55    Configuration(&'a BorrowedName),
56}
57
58/// Generates a `Vec<ContextSpanned<&BorrowedName>>` -> `Vec<(CapabilityId, Arc<std::path::Path>)>` conversion function.
59macro_rules! capability_ids_from_context_names {
60    ($name:ident, $variant:expr) => {
61        fn $name(
62            names: Vec<ContextSpanned<&'a BorrowedName>>,
63        ) -> Vec<(Self, Arc<std::path::Path>)> {
64            names
65                .into_iter()
66                .map(|spanned_name| ($variant(spanned_name.value), spanned_name.origin))
67                .collect()
68        }
69    };
70}
71
72/// Generates a `Vec<ContextSpanned<Path>>` -> `Vec<(CapabilityId, Arc<std::path::Path>)>` conversion function.
73macro_rules! capability_ids_from_context_paths {
74    ($name:ident, $variant:expr) => {
75        fn $name(paths: Vec<ContextSpanned<Path>>) -> Vec<(Self, Arc<std::path::Path>)> {
76            paths
77                .into_iter()
78                .map(|spanned_path| ($variant(spanned_path.value), spanned_path.origin))
79                .collect()
80        }
81    };
82}
83
84impl<'a> CapabilityId<'a> {
85    /// Human readable description of this capability type.
86    pub fn type_str(&self) -> &'static str {
87        match self {
88            CapabilityId::Service(_) => "service",
89            CapabilityId::Protocol(_) => "protocol",
90            CapabilityId::Directory(_) => "directory",
91            CapabilityId::UsedService(_) => "service",
92            CapabilityId::UsedProtocol(_) => "protocol",
93            CapabilityId::UsedProtocolNumberedHandle(_) => "protocol",
94            CapabilityId::UsedDirectory(_) => "directory",
95            CapabilityId::UsedStorage(_) => "storage",
96            CapabilityId::UsedEventStream(_) => "event_stream",
97            CapabilityId::UsedRunner(_) => "runner",
98            CapabilityId::UsedConfiguration(_) => "config",
99            CapabilityId::UsedDictionary(_) => "dictionary",
100            CapabilityId::Storage(_) => "storage",
101            CapabilityId::Runner(_) => "runner",
102            CapabilityId::Resolver(_) => "resolver",
103            CapabilityId::EventStream(_) => "event_stream",
104            CapabilityId::Dictionary(_) => "dictionary",
105            CapabilityId::Configuration(_) => "config",
106        }
107    }
108
109    /// Return the directory containing the capability, if this capability takes a target path.
110    pub fn get_dir_path(&self) -> Option<NamespacePath> {
111        match self {
112            CapabilityId::UsedService(p)
113            | CapabilityId::UsedProtocol(p)
114            | CapabilityId::UsedEventStream(p) => Some(p.parent()),
115            CapabilityId::UsedDirectory(p)
116            | CapabilityId::UsedStorage(p)
117            | CapabilityId::UsedDictionary(p) => Some(p.clone().into()),
118            _ => None,
119        }
120    }
121
122    /// Return the target path of the capability, if this capability has one.
123    pub fn get_target_path(&self) -> Option<NamespacePath> {
124        match self {
125            CapabilityId::UsedService(p)
126            | CapabilityId::UsedProtocol(p)
127            | CapabilityId::UsedEventStream(p)
128            | CapabilityId::UsedDirectory(p)
129            | CapabilityId::UsedStorage(p)
130            | CapabilityId::UsedDictionary(p) => Some(p.clone().into()),
131            _ => None,
132        }
133    }
134
135    pub fn from_context_capability(
136        capability_input: &'a ContextSpanned<ContextCapability>,
137    ) -> Result<Vec<(Self, Arc<std::path::Path>)>, Error> {
138        let capability = &capability_input.value;
139        let origin = &capability_input.origin;
140
141        if let Some(n) = capability.service() {
142            if n.value.is_many()
143                && let Some(cs_path) = &capability.path
144            {
145                return Err(Error::validate_context(
146                    "\"path\" can only be specified when one `service` is supplied.",
147                    Some(cs_path.origin.clone()),
148                ));
149            }
150            return Ok(Self::services_from_context(Self::get_one_or_many_names_context(
151                n,
152                None,
153                capability.capability_type(None).unwrap(),
154            )?));
155        } else if let Some(n) = capability.protocol() {
156            if n.value.is_many()
157                && let Some(cs_path) = &capability.path
158            {
159                return Err(Error::validate_context(
160                    "\"path\" can only be specified when one `protocol` is supplied.",
161                    Some(cs_path.origin.clone()),
162                ));
163            }
164            return Ok(Self::protocols_from_context(Self::get_one_or_many_names_context(
165                n,
166                None,
167                capability.capability_type(None).unwrap(),
168            )?));
169        } else if let Some(n) = capability.directory() {
170            return Ok(Self::directories_from_context(Self::get_one_or_many_names_context(
171                n,
172                None,
173                capability.capability_type(None).unwrap(),
174            )?));
175        } else if let Some(cs_storage) = capability.storage() {
176            if capability.storage_id.is_none() {
177                return Err(Error::validate_context(
178                    "Storage declaration is missing \"storage_id\", but is required.",
179                    Some(cs_storage.origin),
180                ));
181            }
182            return Ok(Self::storages_from_context(Self::get_one_or_many_names_context(
183                cs_storage,
184                None,
185                capability.capability_type(None).unwrap(),
186            )?));
187        } else if let Some(n) = capability.runner() {
188            return Ok(Self::runners_from_context(Self::get_one_or_many_names_context(
189                n,
190                None,
191                capability.capability_type(None).unwrap(),
192            )?));
193        } else if let Some(n) = capability.resolver() {
194            return Ok(Self::resolvers_from_context(Self::get_one_or_many_names_context(
195                n,
196                None,
197                capability.capability_type(None).unwrap(),
198            )?));
199        } else if let Some(n) = capability.event_stream() {
200            return Ok(Self::event_streams_from_context(Self::get_one_or_many_names_context(
201                n,
202                None,
203                capability.capability_type(None).unwrap(),
204            )?));
205        } else if let Some(n) = capability.dictionary() {
206            return Ok(Self::dictionaries_from_context(Self::get_one_or_many_names_context(
207                n,
208                None,
209                capability.capability_type(None).unwrap(),
210            )?));
211        } else if let Some(n) = capability.config() {
212            return Ok(Self::configurations_from_context(Self::get_one_or_many_names_context(
213                n,
214                None,
215                capability.capability_type(None).unwrap(),
216            )?));
217        }
218
219        // Unsupported capability type.
220        let supported_keywords = capability
221            .supported()
222            .iter()
223            .map(|k| format!("\"{}\"", k))
224            .collect::<Vec<_>>()
225            .join(", ");
226        Err(Error::validate_context(
227            format!(
228                "`{}` declaration is missing a capability keyword, one of: {}",
229                capability.decl_type(),
230                supported_keywords,
231            ),
232            Some(origin.clone()),
233        ))
234    }
235
236    pub fn from_context_offer_expose<T>(
237        clause_input: &'a ContextSpanned<T>,
238    ) -> Result<Vec<(Self, Arc<std::path::Path>)>, Error>
239    where
240        T: ContextCapabilityClause + AsClauseContext + fmt::Debug,
241    {
242        let clause = &clause_input.value;
243        let origin = &clause_input.origin;
244
245        let alias = clause.r#as();
246
247        if let Some(n) = clause.service() {
248            return Ok(Self::services_from_context(Self::get_one_or_many_names_context(
249                n,
250                alias,
251                clause.capability_type(Some(origin.clone())).unwrap(),
252            )?));
253        } else if let Some(n) = clause.protocol() {
254            return Ok(Self::protocols_from_context(Self::get_one_or_many_names_context(
255                n,
256                alias,
257                clause.capability_type(Some(origin.clone())).unwrap(),
258            )?));
259        } else if let Some(n) = clause.directory() {
260            return Ok(Self::directories_from_context(Self::get_one_or_many_names_context(
261                n,
262                alias,
263                clause.capability_type(Some(origin.clone())).unwrap(),
264            )?));
265        } else if let Some(n) = clause.storage() {
266            return Ok(Self::storages_from_context(Self::get_one_or_many_names_context(
267                n,
268                alias,
269                clause.capability_type(Some(origin.clone())).unwrap(),
270            )?));
271        } else if let Some(n) = clause.runner() {
272            return Ok(Self::runners_from_context(Self::get_one_or_many_names_context(
273                n,
274                alias,
275                clause.capability_type(Some(origin.clone())).unwrap(),
276            )?));
277        } else if let Some(n) = clause.resolver() {
278            return Ok(Self::resolvers_from_context(Self::get_one_or_many_names_context(
279                n,
280                alias,
281                clause.capability_type(Some(origin.clone())).unwrap(),
282            )?));
283        } else if let Some(event_stream) = clause.event_stream() {
284            return Ok(Self::event_streams_from_context(Self::get_one_or_many_names_context(
285                event_stream,
286                alias,
287                clause.capability_type(Some(origin.clone())).unwrap(),
288            )?));
289        } else if let Some(n) = clause.dictionary() {
290            return Ok(Self::dictionaries_from_context(Self::get_one_or_many_names_context(
291                n,
292                alias,
293                clause.capability_type(Some(origin.clone())).unwrap(),
294            )?));
295        } else if let Some(n) = clause.config() {
296            return Ok(Self::configurations_from_context(Self::get_one_or_many_names_context(
297                n,
298                alias,
299                clause.capability_type(Some(origin.clone())).unwrap(),
300            )?));
301        }
302
303        // Unsupported capability type.
304        let supported_keywords =
305            clause.supported().iter().map(|k| format!("\"{}\"", k)).collect::<Vec<_>>().join(", ");
306        Err(Error::validate_context(
307            format!(
308                "`{}` declaration is missing a capability keyword, one of: {}",
309                clause.decl_type(),
310                supported_keywords,
311            ),
312            Some(origin.clone()),
313        ))
314    }
315
316    /// Given a ContextUse clause, return the set of target identifiers.
317    ///
318    /// When only one capability identifier is specified, the target identifier name is derived
319    /// using the "path" clause. If a "path" clause is not specified, the target identifier is the
320    /// same name as the source.
321    ///
322    /// When multiple capability identifiers are specified, the target names are the same as the
323    /// source names.
324    pub fn from_context_use(
325        use_input: &'a ContextSpanned<ContextUse>,
326    ) -> Result<Vec<(Self, Arc<std::path::Path>)>, Error> {
327        let use_ = &use_input.value;
328        let origin = &use_input.origin;
329
330        let alias = use_.path.as_ref();
331
332        if let Some(n) = option_one_or_many_as_ref_context(&use_.service) {
333            return Ok(Self::used_services_from_context(Self::get_one_or_many_svc_paths_context(
334                n,
335                alias,
336                use_input.capability_type(Some(origin.clone())).unwrap(),
337            )?));
338        } else if let Some(n) = option_one_or_many_as_ref_context(&use_.protocol) {
339            if let Some(numbered_handle) = &use_.numbered_handle {
340                return Ok(n
341                    .value
342                    .iter()
343                    .map(|_| {
344                        (
345                            CapabilityId::UsedProtocolNumberedHandle(numbered_handle.value),
346                            n.origin.clone(),
347                        )
348                    })
349                    .collect());
350            }
351
352            return Ok(Self::used_protocols_from_context(Self::get_one_or_many_svc_paths_context(
353                n,
354                alias,
355                use_input.capability_type(Some(origin.clone())).unwrap(),
356            )?));
357        } else if let Some(_) = &use_.directory {
358            if use_.path.is_none() {
359                return Err(Error::validate_context(
360                    "\"path\" should be present for `use directory`.",
361                    Some(origin.clone()),
362                ));
363            }
364            return Ok(vec![(
365                CapabilityId::UsedDirectory(use_.path.as_ref().unwrap().value.clone()),
366                origin.clone(),
367            )]);
368        } else if let Some(_) = &use_.storage {
369            if use_.path.is_none() {
370                return Err(Error::validate_context(
371                    "\"path\" should be present for `use storage`.",
372                    Some(origin.clone()),
373                ));
374            }
375            return Ok(vec![(
376                CapabilityId::UsedStorage(use_.path.as_ref().unwrap().value.clone()),
377                origin.clone(),
378            )]);
379        } else if let Some(_) = &use_.event_stream {
380            if let Some(path) = &use_.path {
381                return Ok(vec![(
382                    CapabilityId::UsedEventStream(path.value.clone()),
383                    origin.clone(),
384                )]);
385            }
386            return Ok(vec![(
387                CapabilityId::UsedEventStream(Path::new("/svc/fuchsia.component.EventStream")?),
388                origin.clone(),
389            )]);
390        } else if let Some(n) = &use_.runner {
391            return Ok(vec![(CapabilityId::UsedRunner(&n.value), n.origin.clone())]);
392        } else if let Some(_) = &use_.config {
393            return match &use_.key {
394                None => Err(Error::validate_context(
395                    "\"key\" should be present for `use config`.",
396                    Some(origin.clone()),
397                )),
398                Some(name) => {
399                    Ok(vec![(CapabilityId::UsedConfiguration(&name.value), origin.clone())])
400                }
401            };
402        } else if let Some(n) = option_one_or_many_as_ref_context(&use_.dictionary) {
403            return Ok(Self::used_dictionaries_from_context(
404                Self::get_one_or_many_svc_paths_context(
405                    n,
406                    alias,
407                    use_input.capability_type(Some(origin.clone())).unwrap(),
408                )?,
409            ));
410        }
411
412        // Unsupported capability type.
413        let supported_keywords = use_input
414            .supported()
415            .iter()
416            .map(|k| format!("\"{}\"", k))
417            .collect::<Vec<_>>()
418            .join(", ");
419
420        Err(Error::validate_context(
421            format!(
422                "`{}` declaration is missing a capability keyword, one of: {}",
423                use_input.decl_type(),
424                supported_keywords,
425            ),
426            Some(origin.clone()),
427        ))
428    }
429
430    /// Returns the target names as a `Vec` from a declaration with `names` and `alias` as a `Vec`.
431    fn get_one_or_many_names_context<'b>(
432        name_wrapper: ContextSpanned<OneOrMany<&'b BorrowedName>>,
433        alias: Option<ContextSpanned<&'b BorrowedName>>,
434        capability_type: &str,
435    ) -> Result<Vec<ContextSpanned<&'b BorrowedName>>, Error> {
436        let names_origin = name_wrapper.origin;
437        let names_vec: Vec<&'b BorrowedName> = name_wrapper.value.into_iter().collect();
438        let num_names = names_vec.len();
439
440        if num_names > 1 && alias.is_some() {
441            return Err(Error::validate_contexts(
442                format!("\"as\" can only be specified when one `{}` is supplied.", capability_type),
443                vec![alias.map(|s| s.origin).unwrap_or(names_origin)],
444            ));
445        }
446
447        if num_names == 1 {
448            let final_name_span = alias_or_name_context(alias, names_vec[0], names_origin);
449            return Ok(vec![final_name_span]);
450        }
451
452        let final_names = names_vec
453            .into_iter()
454            .map(|name| ContextSpanned { value: name, origin: names_origin.clone() })
455            .collect();
456
457        Ok(final_names)
458    }
459
460    fn get_one_or_many_svc_paths_context(
461        names: ContextSpanned<OneOrMany<&BorrowedName>>,
462        alias: Option<&ContextSpanned<Path>>,
463        capability_type: &str,
464    ) -> Result<Vec<ContextSpanned<Path>>, Error> {
465        let names_origin = &names.origin;
466        let names_vec: Vec<_> = names.value.into_iter().collect();
467
468        match (names_vec.len(), alias) {
469            (_, None) => {
470                let generated_paths = names_vec
471                    .into_iter()
472                    .map(|n| {
473                        let new_path: Path = format!("/svc/{}", n).parse().unwrap();
474                        ContextSpanned { value: new_path, origin: names_origin.clone() }
475                    })
476                    .collect();
477                Ok(generated_paths)
478            }
479
480            (1, Some(spanned_alias)) => Ok(vec![spanned_alias.clone()]),
481
482            (_, Some(spanned_alias)) => Err(Error::validate_contexts(
483                format!(
484                    "\"path\" can only be specified when one `{}` is supplied.",
485                    capability_type,
486                ),
487                vec![spanned_alias.origin.clone()],
488            )),
489        }
490    }
491
492    capability_ids_from_context_names!(services_from_context, CapabilityId::Service);
493    capability_ids_from_context_names!(protocols_from_context, CapabilityId::Protocol);
494    capability_ids_from_context_names!(directories_from_context, CapabilityId::Directory);
495    capability_ids_from_context_names!(storages_from_context, CapabilityId::Storage);
496    capability_ids_from_context_names!(runners_from_context, CapabilityId::Runner);
497    capability_ids_from_context_names!(resolvers_from_context, CapabilityId::Resolver);
498    capability_ids_from_context_names!(event_streams_from_context, CapabilityId::EventStream);
499    capability_ids_from_context_names!(dictionaries_from_context, CapabilityId::Dictionary);
500    capability_ids_from_context_names!(configurations_from_context, CapabilityId::Configuration);
501
502    capability_ids_from_context_paths!(used_services_from_context, CapabilityId::UsedService);
503    capability_ids_from_context_paths!(used_protocols_from_context, CapabilityId::UsedProtocol);
504    capability_ids_from_context_paths!(
505        used_dictionaries_from_context,
506        CapabilityId::UsedDictionary
507    );
508}
509
510impl fmt::Display for CapabilityId<'_> {
511    /// Return the string ID of this clause.
512    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513        match self {
514            CapabilityId::Service(n)
515            | CapabilityId::Storage(n)
516            | CapabilityId::Runner(n)
517            | CapabilityId::UsedRunner(n)
518            | CapabilityId::Resolver(n)
519            | CapabilityId::EventStream(n)
520            | CapabilityId::Configuration(n)
521            | CapabilityId::UsedConfiguration(n)
522            | CapabilityId::Dictionary(n) => write!(f, "{}", n),
523            CapabilityId::UsedService(p)
524            | CapabilityId::UsedProtocol(p)
525            | CapabilityId::UsedDirectory(p)
526            | CapabilityId::UsedStorage(p)
527            | CapabilityId::UsedEventStream(p)
528            | CapabilityId::UsedDictionary(p) => write!(f, "{}", p),
529            CapabilityId::UsedProtocolNumberedHandle(p) => write!(f, "{}", p),
530            CapabilityId::Protocol(p) | CapabilityId::Directory(p) => write!(f, "{}", p),
531        }
532    }
533}
534
535#[cfg(test)]
536mod tests {
537    use super::*;
538    use crate::types::offer::ContextOffer;
539    use assert_matches::assert_matches;
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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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<std::path::Path> = Arc::from(std::path::Path::new("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}