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::offer::ContextOffer;
9use crate::types::r#use::ContextUse;
10use crate::{
11    AsClause, AsClauseContext, Capability, CapabilityClause, ContextExpose, ContextSpanned, Error,
12    Origin, PathClause, Use, alias_or_name, alias_or_name_context,
13};
14pub use cm_types::{
15    Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
16    NamespacePath, OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
17};
18
19use std::fmt;
20
21/// A name/identity of a capability exposed/offered to another component.
22///
23/// Exposed or offered capabilities have an identifier whose format
24/// depends on the capability type. For directories and services this is
25/// a path, while for storage this is a storage name. Paths and storage
26/// names, however, are in different conceptual namespaces, and can't
27/// collide with each other.
28///
29/// This enum allows such names to be specified disambiguating what
30/// namespace they are in.
31#[derive(Debug, PartialEq, Eq, Hash, Clone)]
32pub enum CapabilityId<'a> {
33    Service(&'a BorrowedName),
34    Protocol(&'a BorrowedName),
35    Directory(&'a BorrowedName),
36    // A service in a `use` declaration has a target path in the component's namespace.
37    UsedService(Path),
38    // A protocol in a `use` declaration has a target path in the component's namespace.
39    UsedProtocol(Path),
40    // A protocol in a `use` declaration has a numbered handle in the component's namespace.
41    UsedProtocolNumberedHandle(HandleType),
42    // A directory in a `use` declaration has a target path in the component's namespace.
43    UsedDirectory(Path),
44    // A storage in a `use` declaration has a target path in the component's namespace.
45    UsedStorage(Path),
46    // An event stream in a `use` declaration has a target path in the component's namespace.
47    UsedEventStream(Path),
48    // A configuration in a `use` declaration has a target name that matches a config.
49    UsedConfiguration(&'a BorrowedName),
50    UsedRunner(&'a BorrowedName),
51    // A dictionary in a `use` declaration that has a target path in the component's namespace.
52    UsedDictionary(Path),
53    Storage(&'a BorrowedName),
54    Runner(&'a BorrowedName),
55    Resolver(&'a BorrowedName),
56    EventStream(&'a BorrowedName),
57    Dictionary(&'a BorrowedName),
58    Configuration(&'a BorrowedName),
59}
60
61/// Generates a `Vec<&BorrowedName>` -> `Vec<CapabilityId>` conversion function.
62macro_rules! capability_ids_from_names {
63    ($name:ident, $variant:expr) => {
64        fn $name(names: Vec<&'a BorrowedName>) -> Vec<Self> {
65            names.into_iter().map(|n| $variant(n)).collect()
66        }
67    };
68}
69
70/// Generates a `Vec<ContextSpanned<&BorrowedName>>` -> `Vec<(CapabilityId, Origin)>` conversion function.
71macro_rules! capability_ids_from_context_names {
72    ($name:ident, $variant:expr) => {
73        fn $name(names: Vec<ContextSpanned<&'a BorrowedName>>) -> Vec<(Self, Origin)> {
74            names
75                .into_iter()
76                .map(|spanned_name| ($variant(spanned_name.value), spanned_name.origin))
77                .collect()
78        }
79    };
80}
81
82/// Generates a `Vec<Path>` -> `Vec<CapabilityId>` conversion function.
83macro_rules! capability_ids_from_paths {
84    ($name:ident, $variant:expr) => {
85        fn $name(paths: Vec<Path>) -> Vec<Self> {
86            paths.into_iter().map(|p| $variant(p)).collect()
87        }
88    };
89}
90
91/// Generates a `Vec<ContextSpanned<Path>>` -> `Vec<(CapabilityId, Origin)>` conversion function.
92macro_rules! capability_ids_from_context_paths {
93    ($name:ident, $variant:expr) => {
94        fn $name(paths: Vec<ContextSpanned<Path>>) -> Vec<(Self, Origin)> {
95            paths
96                .into_iter()
97                .map(|spanned_path| ($variant(spanned_path.value), spanned_path.origin))
98                .collect()
99        }
100    };
101}
102
103impl<'a> CapabilityId<'a> {
104    /// Human readable description of this capability type.
105    pub fn type_str(&self) -> &'static str {
106        match self {
107            CapabilityId::Service(_) => "service",
108            CapabilityId::Protocol(_) => "protocol",
109            CapabilityId::Directory(_) => "directory",
110            CapabilityId::UsedService(_) => "service",
111            CapabilityId::UsedProtocol(_) => "protocol",
112            CapabilityId::UsedProtocolNumberedHandle(_) => "protocol",
113            CapabilityId::UsedDirectory(_) => "directory",
114            CapabilityId::UsedStorage(_) => "storage",
115            CapabilityId::UsedEventStream(_) => "event_stream",
116            CapabilityId::UsedRunner(_) => "runner",
117            CapabilityId::UsedConfiguration(_) => "config",
118            CapabilityId::UsedDictionary(_) => "dictionary",
119            CapabilityId::Storage(_) => "storage",
120            CapabilityId::Runner(_) => "runner",
121            CapabilityId::Resolver(_) => "resolver",
122            CapabilityId::EventStream(_) => "event_stream",
123            CapabilityId::Dictionary(_) => "dictionary",
124            CapabilityId::Configuration(_) => "config",
125        }
126    }
127
128    /// Return the directory containing the capability, if this capability takes a target path.
129    pub fn get_dir_path(&self) -> Option<NamespacePath> {
130        match self {
131            CapabilityId::UsedService(p)
132            | CapabilityId::UsedProtocol(p)
133            | CapabilityId::UsedEventStream(p) => Some(p.parent()),
134            CapabilityId::UsedDirectory(p)
135            | CapabilityId::UsedStorage(p)
136            | CapabilityId::UsedDictionary(p) => Some(p.clone().into()),
137            _ => None,
138        }
139    }
140
141    /// Return the target path of the capability, if this capability has one.
142    pub fn get_target_path(&self) -> Option<NamespacePath> {
143        match self {
144            CapabilityId::UsedService(p)
145            | CapabilityId::UsedProtocol(p)
146            | CapabilityId::UsedEventStream(p)
147            | CapabilityId::UsedDirectory(p)
148            | CapabilityId::UsedStorage(p)
149            | CapabilityId::UsedDictionary(p) => Some(p.clone().into()),
150            _ => None,
151        }
152    }
153
154    /// Given a Use clause, return the set of target identifiers.
155    ///
156    /// When only one capability identifier is specified, the target identifier name is derived
157    /// using the "path" clause. If a "path" clause is not specified, the target identifier is the
158    /// same name as the source.
159    ///
160    /// When multiple capability identifiers are specified, the target names are the same as the
161    /// source names.
162    pub fn from_use(use_: &'a Use) -> Result<Vec<Self>, Error> {
163        // TODO: Validate that exactly one of these is set.
164        let alias = use_.path.as_ref();
165        if let Some(n) = use_.service() {
166            return Ok(Self::used_services_from(Self::get_one_or_many_svc_paths(
167                n,
168                alias,
169                use_.capability_type().unwrap(),
170            )?));
171        } else if let Some(n) = use_.protocol() {
172            if let Some(numbered_handle) = use_.numbered_handle {
173                return Ok(n
174                    .iter()
175                    .map(|_| CapabilityId::UsedProtocolNumberedHandle(numbered_handle))
176                    .collect());
177            }
178            return Ok(Self::used_protocols_from(Self::get_one_or_many_svc_paths(
179                n,
180                alias,
181                use_.capability_type().unwrap(),
182            )?));
183        } else if let Some(_) = use_.directory.as_ref() {
184            if use_.path.is_none() {
185                return Err(Error::validate("\"path\" should be present for `use directory`."));
186            }
187            return Ok(vec![CapabilityId::UsedDirectory(use_.path.as_ref().unwrap().clone())]);
188        } else if let Some(_) = use_.storage.as_ref() {
189            if use_.path.is_none() {
190                return Err(Error::validate("\"path\" should be present for `use storage`."));
191            }
192            return Ok(vec![CapabilityId::UsedStorage(use_.path.as_ref().unwrap().clone())]);
193        } else if let Some(_) = use_.event_stream() {
194            if let Some(path) = use_.path() {
195                return Ok(vec![CapabilityId::UsedEventStream(path.clone())]);
196            }
197            return Ok(vec![CapabilityId::UsedEventStream(Path::new(
198                "/svc/fuchsia.component.EventStream",
199            )?)]);
200        } else if let Some(n) = use_.runner() {
201            match n {
202                OneOrMany::One(name) => {
203                    return Ok(vec![CapabilityId::UsedRunner(name)]);
204                }
205                OneOrMany::Many(_) => {
206                    return Err(Error::validate("`use runner` should occur at most once."));
207                }
208            }
209        } else if let Some(_) = use_.config() {
210            return match &use_.key {
211                None => Err(Error::validate("\"key\" should be present for `use config`.")),
212                Some(name) => Ok(vec![CapabilityId::UsedConfiguration(name)]),
213            };
214        } else if let Some(n) = use_.dictionary() {
215            return Ok(Self::used_dictionaries_from(Self::get_one_or_many_svc_paths(
216                n,
217                alias,
218                use_.capability_type().unwrap(),
219            )?));
220        }
221        // Unsupported capability type.
222        let supported_keywords = use_
223            .supported()
224            .into_iter()
225            .map(|k| format!("\"{}\"", k))
226            .collect::<Vec<_>>()
227            .join(", ");
228        Err(Error::validate(format!(
229            "`{}` declaration is missing a capability keyword, one of: {}",
230            use_.decl_type(),
231            supported_keywords,
232        )))
233    }
234
235    pub fn from_capability(capability: &'a Capability) -> Result<Vec<Self>, Error> {
236        // TODO: Validate that exactly one of these is set.
237        if let Some(n) = capability.service() {
238            if n.is_many() && capability.path.is_some() {
239                return Err(Error::validate(
240                    "\"path\" can only be specified when one `service` is supplied.",
241                ));
242            }
243            return Ok(Self::services_from(Self::get_one_or_many_names_no_span(
244                n,
245                None,
246                capability.capability_type().unwrap(),
247            )?));
248        } else if let Some(n) = capability.protocol() {
249            if n.is_many() && capability.path.is_some() {
250                return Err(Error::validate(
251                    "\"path\" can only be specified when one `protocol` is supplied.",
252                ));
253            }
254            return Ok(Self::protocols_from(Self::get_one_or_many_names_no_span(
255                n,
256                None,
257                capability.capability_type().unwrap(),
258            )?));
259        } else if let Some(n) = capability.directory() {
260            return Ok(Self::directories_from(Self::get_one_or_many_names_no_span(
261                n,
262                None,
263                capability.capability_type().unwrap(),
264            )?));
265        } else if let Some(n) = capability.storage() {
266            if capability.storage_id.is_none() {
267                return Err(Error::validate(
268                    "Storage declaration is missing \"storage_id\", but is required.",
269                ));
270            }
271            return Ok(Self::storages_from(Self::get_one_or_many_names_no_span(
272                n,
273                None,
274                capability.capability_type().unwrap(),
275            )?));
276        } else if let Some(n) = capability.runner() {
277            return Ok(Self::runners_from(Self::get_one_or_many_names_no_span(
278                n,
279                None,
280                capability.capability_type().unwrap(),
281            )?));
282        } else if let Some(n) = capability.resolver() {
283            return Ok(Self::resolvers_from(Self::get_one_or_many_names_no_span(
284                n,
285                None,
286                capability.capability_type().unwrap(),
287            )?));
288        } else if let Some(n) = capability.event_stream() {
289            return Ok(Self::event_streams_from(Self::get_one_or_many_names_no_span(
290                n,
291                None,
292                capability.capability_type().unwrap(),
293            )?));
294        } else if let Some(n) = capability.dictionary() {
295            return Ok(Self::dictionaries_from(Self::get_one_or_many_names_no_span(
296                n,
297                None,
298                capability.capability_type().unwrap(),
299            )?));
300        } else if let Some(n) = capability.config() {
301            return Ok(Self::configurations_from(Self::get_one_or_many_names_no_span(
302                n,
303                None,
304                capability.capability_type().unwrap(),
305            )?));
306        }
307
308        // Unsupported capability type.
309        let supported_keywords = capability
310            .supported()
311            .into_iter()
312            .map(|k| format!("\"{}\"", k))
313            .collect::<Vec<_>>()
314            .join(", ");
315        Err(Error::validate(format!(
316            "`{}` declaration is missing a capability keyword, one of: {}",
317            capability.decl_type(),
318            supported_keywords,
319        )))
320    }
321
322    /// Given an Offer or Expose clause, return the set of target identifiers.
323    ///
324    /// When only one capability identifier is specified, the target identifier name is derived
325    /// using the "as" clause. If an "as" clause is not specified, the target identifier is the
326    /// same name as the source.
327    ///
328    /// When multiple capability identifiers are specified, the target names are the same as the
329    /// source names.
330    pub fn from_offer_expose<T>(clause: &'a T) -> Result<Vec<Self>, Error>
331    where
332        T: CapabilityClause + AsClause + fmt::Debug,
333    {
334        // TODO: Validate that exactly one of these is set.
335        let alias = clause.r#as();
336        if let Some(n) = clause.service() {
337            return Ok(Self::services_from(Self::get_one_or_many_names_no_span(
338                n,
339                alias,
340                clause.capability_type().unwrap(),
341            )?));
342        } else if let Some(n) = clause.protocol() {
343            return Ok(Self::protocols_from(Self::get_one_or_many_names_no_span(
344                n,
345                alias,
346                clause.capability_type().unwrap(),
347            )?));
348        } else if let Some(n) = clause.directory() {
349            return Ok(Self::directories_from(Self::get_one_or_many_names_no_span(
350                n,
351                alias,
352                clause.capability_type().unwrap(),
353            )?));
354        } else if let Some(n) = clause.storage() {
355            return Ok(Self::storages_from(Self::get_one_or_many_names_no_span(
356                n,
357                alias,
358                clause.capability_type().unwrap(),
359            )?));
360        } else if let Some(n) = clause.runner() {
361            return Ok(Self::runners_from(Self::get_one_or_many_names_no_span(
362                n,
363                alias,
364                clause.capability_type().unwrap(),
365            )?));
366        } else if let Some(n) = clause.resolver() {
367            return Ok(Self::resolvers_from(Self::get_one_or_many_names_no_span(
368                n,
369                alias,
370                clause.capability_type().unwrap(),
371            )?));
372        } else if let Some(event_stream) = clause.event_stream() {
373            return Ok(Self::event_streams_from(Self::get_one_or_many_names_no_span(
374                event_stream,
375                alias,
376                clause.capability_type().unwrap(),
377            )?));
378        } else if let Some(n) = clause.dictionary() {
379            return Ok(Self::dictionaries_from(Self::get_one_or_many_names_no_span(
380                n,
381                alias,
382                clause.capability_type().unwrap(),
383            )?));
384        } else if let Some(n) = clause.config() {
385            return Ok(Self::configurations_from(Self::get_one_or_many_names_no_span(
386                n,
387                alias,
388                clause.capability_type().unwrap(),
389            )?));
390        }
391
392        // Unsupported capability type.
393        let supported_keywords = clause
394            .supported()
395            .into_iter()
396            .map(|k| format!("\"{}\"", k))
397            .collect::<Vec<_>>()
398            .join(", ");
399        Err(Error::validate(format!(
400            "`{}` declaration is missing a capability keyword, one of: {}",
401            clause.decl_type(),
402            supported_keywords,
403        )))
404    }
405
406    pub fn from_context_capability(
407        capability_input: &'a ContextSpanned<ContextCapability>,
408    ) -> Result<Vec<(Self, Origin)>, Error> {
409        let capability = &capability_input.value;
410        let origin = &capability_input.origin;
411
412        if let Some(n) = capability.service() {
413            if n.value.is_many()
414                && let Some(cs_path) = &capability.path
415            {
416                return Err(Error::validate_context(
417                    "\"path\" can only be specified when one `service` is supplied.",
418                    Some(cs_path.origin.clone()),
419                ));
420            }
421            return Ok(Self::services_from_context(Self::get_one_or_many_names_context(
422                n,
423                None,
424                capability.capability_type(None).unwrap(),
425            )?));
426        } else if let Some(n) = capability.protocol() {
427            if n.value.is_many()
428                && let Some(cs_path) = &capability.path
429            {
430                return Err(Error::validate_context(
431                    "\"path\" can only be specified when one `protocol` is supplied.",
432                    Some(cs_path.origin.clone()),
433                ));
434            }
435            return Ok(Self::protocols_from_context(Self::get_one_or_many_names_context(
436                n,
437                None,
438                capability.capability_type(None).unwrap(),
439            )?));
440        } else if let Some(n) = capability.directory() {
441            return Ok(Self::directories_from_context(Self::get_one_or_many_names_context(
442                n,
443                None,
444                capability.capability_type(None).unwrap(),
445            )?));
446        } else if let Some(cs_storage) = capability.storage() {
447            if capability.storage_id.is_none() {
448                return Err(Error::validate_context(
449                    "Storage declaration is missing \"storage_id\", but is required.",
450                    Some(cs_storage.origin),
451                ));
452            }
453            return Ok(Self::storages_from_context(Self::get_one_or_many_names_context(
454                cs_storage,
455                None,
456                capability.capability_type(None).unwrap(),
457            )?));
458        } else if let Some(n) = capability.runner() {
459            return Ok(Self::runners_from_context(Self::get_one_or_many_names_context(
460                n,
461                None,
462                capability.capability_type(None).unwrap(),
463            )?));
464        } else if let Some(n) = capability.resolver() {
465            return Ok(Self::resolvers_from_context(Self::get_one_or_many_names_context(
466                n,
467                None,
468                capability.capability_type(None).unwrap(),
469            )?));
470        } else if let Some(n) = capability.event_stream() {
471            return Ok(Self::event_streams_from_context(Self::get_one_or_many_names_context(
472                n,
473                None,
474                capability.capability_type(None).unwrap(),
475            )?));
476        } else if let Some(n) = capability.dictionary() {
477            return Ok(Self::dictionaries_from_context(Self::get_one_or_many_names_context(
478                n,
479                None,
480                capability.capability_type(None).unwrap(),
481            )?));
482        } else if let Some(n) = capability.config() {
483            return Ok(Self::configurations_from_context(Self::get_one_or_many_names_context(
484                n,
485                None,
486                capability.capability_type(None).unwrap(),
487            )?));
488        }
489
490        // Unsupported capability type.
491        let supported_keywords = capability
492            .supported()
493            .into_iter()
494            .map(|k| format!("\"{}\"", k))
495            .collect::<Vec<_>>()
496            .join(", ");
497        Err(Error::validate_context(
498            format!(
499                "`{}` declaration is missing a capability keyword, one of: {}",
500                capability.decl_type(),
501                supported_keywords,
502            ),
503            Some(origin.clone()),
504        ))
505    }
506
507    pub fn from_context_offer(
508        offer_input: &'a ContextSpanned<ContextOffer>,
509    ) -> Result<Vec<(Self, Origin)>, Error> {
510        let offer = &offer_input.value;
511        let origin = &offer_input.origin;
512
513        let alias = offer.r#as();
514
515        if let Some(n) = offer.service() {
516            return Ok(Self::services_from_context(Self::get_one_or_many_names_context(
517                n,
518                alias,
519                offer.capability_type(Some(origin.clone())).unwrap(),
520            )?));
521        } else if let Some(n) = offer.protocol() {
522            return Ok(Self::protocols_from_context(Self::get_one_or_many_names_context(
523                n,
524                alias,
525                offer.capability_type(Some(origin.clone())).unwrap(),
526            )?));
527        } else if let Some(n) = offer.directory() {
528            return Ok(Self::directories_from_context(Self::get_one_or_many_names_context(
529                n,
530                alias,
531                offer.capability_type(Some(origin.clone())).unwrap(),
532            )?));
533        } else if let Some(n) = offer.storage() {
534            return Ok(Self::storages_from_context(Self::get_one_or_many_names_context(
535                n,
536                alias,
537                offer.capability_type(Some(origin.clone())).unwrap(),
538            )?));
539        } else if let Some(n) = offer.runner() {
540            return Ok(Self::runners_from_context(Self::get_one_or_many_names_context(
541                n,
542                alias,
543                offer.capability_type(Some(origin.clone())).unwrap(),
544            )?));
545        } else if let Some(n) = offer.resolver() {
546            return Ok(Self::resolvers_from_context(Self::get_one_or_many_names_context(
547                n,
548                alias,
549                offer.capability_type(Some(origin.clone())).unwrap(),
550            )?));
551        } else if let Some(event_stream) = offer.event_stream() {
552            return Ok(Self::event_streams_from_context(Self::get_one_or_many_names_context(
553                event_stream,
554                alias,
555                offer.capability_type(Some(origin.clone())).unwrap(),
556            )?));
557        } else if let Some(n) = offer.dictionary() {
558            return Ok(Self::dictionaries_from_context(Self::get_one_or_many_names_context(
559                n,
560                alias,
561                offer.capability_type(Some(origin.clone())).unwrap(),
562            )?));
563        } else if let Some(n) = offer.config() {
564            return Ok(Self::configurations_from_context(Self::get_one_or_many_names_context(
565                n,
566                alias,
567                offer.capability_type(Some(origin.clone())).unwrap(),
568            )?));
569        }
570
571        // Unsupported capability type.
572        let supported_keywords = offer
573            .supported()
574            .into_iter()
575            .map(|k| format!("\"{}\"", k))
576            .collect::<Vec<_>>()
577            .join(", ");
578        Err(Error::validate_context(
579            format!(
580                "`{}` declaration is missing a capability keyword, one of: {}",
581                offer.decl_type(),
582                supported_keywords,
583            ),
584            Some(origin.clone()),
585        ))
586    }
587
588    pub fn from_context_expose(
589        expose_input: &'a ContextSpanned<ContextExpose>,
590    ) -> Result<Vec<(Self, Origin)>, Error> {
591        let expose = &expose_input.value;
592        let origin = &expose_input.origin;
593
594        let alias = expose.r#as();
595
596        if let Some(n) = expose.service() {
597            return Ok(Self::services_from_context(Self::get_one_or_many_names_context(
598                n,
599                alias,
600                expose.capability_type(Some(origin.clone())).unwrap(),
601            )?));
602        } else if let Some(n) = expose.protocol() {
603            return Ok(Self::protocols_from_context(Self::get_one_or_many_names_context(
604                n,
605                alias,
606                expose.capability_type(Some(origin.clone())).unwrap(),
607            )?));
608        } else if let Some(n) = expose.directory() {
609            return Ok(Self::directories_from_context(Self::get_one_or_many_names_context(
610                n,
611                alias,
612                expose.capability_type(Some(origin.clone())).unwrap(),
613            )?));
614        } else if let Some(n) = expose.storage() {
615            return Ok(Self::storages_from_context(Self::get_one_or_many_names_context(
616                n,
617                alias,
618                expose.capability_type(Some(origin.clone())).unwrap(),
619            )?));
620        } else if let Some(n) = expose.runner() {
621            return Ok(Self::runners_from_context(Self::get_one_or_many_names_context(
622                n,
623                alias,
624                expose.capability_type(Some(origin.clone())).unwrap(),
625            )?));
626        } else if let Some(n) = expose.resolver() {
627            return Ok(Self::resolvers_from_context(Self::get_one_or_many_names_context(
628                n,
629                alias,
630                expose.capability_type(Some(origin.clone())).unwrap(),
631            )?));
632        } else if let Some(event_stream) = expose.event_stream() {
633            return Ok(Self::event_streams_from_context(Self::get_one_or_many_names_context(
634                event_stream,
635                alias,
636                expose.capability_type(Some(origin.clone())).unwrap(),
637            )?));
638        } else if let Some(n) = expose.dictionary() {
639            return Ok(Self::dictionaries_from_context(Self::get_one_or_many_names_context(
640                n,
641                alias,
642                expose.capability_type(Some(origin.clone())).unwrap(),
643            )?));
644        } else if let Some(n) = expose.config() {
645            return Ok(Self::configurations_from_context(Self::get_one_or_many_names_context(
646                n,
647                alias,
648                expose.capability_type(Some(origin.clone())).unwrap(),
649            )?));
650        }
651
652        // Unsupported capability type.
653        let supported_keywords = expose
654            .supported()
655            .into_iter()
656            .map(|k| format!("\"{}\"", k))
657            .collect::<Vec<_>>()
658            .join(", ");
659        Err(Error::validate_context(
660            format!(
661                "`{}` declaration is missing a capability keyword, one of: {}",
662                expose.decl_type(),
663                supported_keywords,
664            ),
665            Some(origin.clone()),
666        ))
667    }
668
669    /// Given a ContextUse clause, return the set of target identifiers.
670    ///
671    /// When only one capability identifier is specified, the target identifier name is derived
672    /// using the "path" clause. If a "path" clause is not specified, the target identifier is the
673    /// same name as the source.
674    ///
675    /// When multiple capability identifiers are specified, the target names are the same as the
676    /// source names.
677    pub fn from_context_use(
678        use_input: &'a ContextSpanned<ContextUse>,
679    ) -> Result<Vec<(Self, Origin)>, Error> {
680        let use_ = &use_input.value;
681        let origin = &use_input.origin;
682
683        let alias = use_.path.as_ref();
684
685        if let Some(n) = option_one_or_many_as_ref_context(&use_.service) {
686            return Ok(Self::used_services_from_context(Self::get_one_or_many_svc_paths_context(
687                n,
688                alias,
689                use_input.capability_type(Some(origin.clone())).unwrap(),
690            )?));
691        } else if let Some(n) = option_one_or_many_as_ref_context(&use_.protocol) {
692            return Ok(Self::used_protocols_from_context(Self::get_one_or_many_svc_paths_context(
693                n,
694                alias,
695                use_input.capability_type(Some(origin.clone())).unwrap(),
696            )?));
697        } else if let Some(_) = &use_.directory {
698            if use_.path.is_none() {
699                return Err(Error::validate_context(
700                    "\"path\" should be present for `use directory`.",
701                    Some(origin.clone()),
702                ));
703            }
704            return Ok(vec![(
705                CapabilityId::UsedDirectory(use_.path.as_ref().unwrap().value.clone()),
706                origin.clone(),
707            )]);
708        } else if let Some(_) = &use_.storage {
709            if use_.path.is_none() {
710                return Err(Error::validate_context(
711                    "\"path\" should be present for `use storage`.",
712                    Some(origin.clone()),
713                ));
714            }
715            return Ok(vec![(
716                CapabilityId::UsedStorage(use_.path.as_ref().unwrap().value.clone()),
717                origin.clone(),
718            )]);
719        } else if let Some(_) = &use_.event_stream {
720            if let Some(path) = &use_.path {
721                return Ok(vec![(
722                    CapabilityId::UsedEventStream(path.value.clone()),
723                    origin.clone(),
724                )]);
725            }
726            return Ok(vec![(
727                CapabilityId::UsedEventStream(Path::new("/svc/fuchsia.component.EventStream")?),
728                origin.clone(),
729            )]);
730        } else if let Some(n) = &use_.runner {
731            return Ok(vec![(CapabilityId::UsedRunner(&n.value), n.origin.clone())]);
732        } else if let Some(_) = &use_.config {
733            return match &use_.key {
734                None => Err(Error::validate_context(
735                    "\"key\" should be present for `use config`.",
736                    Some(origin.clone()),
737                )),
738                Some(name) => {
739                    Ok(vec![(CapabilityId::UsedConfiguration(&name.value), origin.clone())])
740                }
741            };
742        } else if let Some(n) = option_one_or_many_as_ref_context(&use_.dictionary) {
743            return Ok(Self::used_dictionaries_from_context(
744                Self::get_one_or_many_svc_paths_context(
745                    n,
746                    alias,
747                    use_input.capability_type(Some(origin.clone())).unwrap(),
748                )?,
749            ));
750        }
751
752        // Unsupported capability type.
753        let supported_keywords = use_input
754            .supported()
755            .into_iter()
756            .map(|k| format!("\"{}\"", k))
757            .collect::<Vec<_>>()
758            .join(", ");
759
760        Err(Error::validate_context(
761            format!(
762                "`{}` declaration is missing a capability keyword, one of: {}",
763                use_input.decl_type(),
764                supported_keywords,
765            ),
766            Some(origin.clone()),
767        ))
768    }
769
770    /// Returns the target names as a `Vec` from a declaration with `names` and `alias` as a `Vec`.
771    fn get_one_or_many_names_no_span<'b>(
772        names: OneOrMany<&'b BorrowedName>,
773        alias: Option<&'b BorrowedName>,
774        capability_type: &str,
775    ) -> Result<Vec<&'b BorrowedName>, Error> {
776        let names: Vec<&BorrowedName> = names.into_iter().collect();
777        if names.len() == 1 {
778            Ok(vec![alias_or_name(alias, &names[0])])
779        } else {
780            if alias.is_some() {
781                return Err(Error::validate(format!(
782                    "\"as\" can only be specified when one `{}` is supplied.",
783                    capability_type,
784                )));
785            }
786            Ok(names)
787        }
788    }
789
790    /// Returns the target names as a `Vec` from a declaration with `names` and `alias` as a `Vec`.
791    fn get_one_or_many_names_context<'b>(
792        name_wrapper: ContextSpanned<OneOrMany<&'b BorrowedName>>,
793        alias: Option<ContextSpanned<&'b BorrowedName>>,
794        capability_type: &str,
795    ) -> Result<Vec<ContextSpanned<&'b BorrowedName>>, Error> {
796        let names_origin = name_wrapper.origin;
797        let names_vec: Vec<&'b BorrowedName> = name_wrapper.value.into_iter().collect();
798        let num_names = names_vec.len();
799
800        if num_names > 1 && alias.is_some() {
801            return Err(Error::validate_contexts(
802                format!("\"as\" can only be specified when one `{}` is supplied.", capability_type),
803                vec![alias.map(|s| s.origin).unwrap_or(names_origin)],
804            ));
805        }
806
807        if num_names == 1 {
808            let final_name_span = alias_or_name_context(alias, names_vec[0], names_origin);
809            return Ok(vec![final_name_span]);
810        }
811
812        let final_names = names_vec
813            .into_iter()
814            .map(|name| ContextSpanned { value: name, origin: names_origin.clone() })
815            .collect();
816
817        Ok(final_names)
818    }
819
820    /// Returns the target paths as a `Vec` from a `use` declaration with `names` and `alias`.
821    fn get_one_or_many_svc_paths(
822        names: OneOrMany<&BorrowedName>,
823        alias: Option<&Path>,
824        capability_type: &str,
825    ) -> Result<Vec<Path>, Error> {
826        let names: Vec<_> = names.into_iter().collect();
827        match (names.len(), alias) {
828            (_, None) => {
829                Ok(names.into_iter().map(|n| format!("/svc/{}", n).parse().unwrap()).collect())
830            }
831            (1, Some(alias)) => Ok(vec![alias.clone()]),
832            (_, Some(_)) => {
833                return Err(Error::validate(format!(
834                    "\"path\" can only be specified when one `{}` is supplied.",
835                    capability_type,
836                )));
837            }
838        }
839    }
840
841    fn get_one_or_many_svc_paths_context(
842        names: ContextSpanned<OneOrMany<&BorrowedName>>,
843        alias: Option<&ContextSpanned<Path>>,
844        capability_type: &str,
845    ) -> Result<Vec<ContextSpanned<Path>>, Error> {
846        let names_origin = &names.origin;
847        let names_vec: Vec<_> = names.value.into_iter().collect();
848
849        match (names_vec.len(), alias) {
850            (_, None) => {
851                let generated_paths = names_vec
852                    .into_iter()
853                    .map(|n| {
854                        let new_path: Path = format!("/svc/{}", n).parse().unwrap();
855                        ContextSpanned { value: new_path, origin: names_origin.clone() }
856                    })
857                    .collect();
858                Ok(generated_paths)
859            }
860
861            (1, Some(spanned_alias)) => Ok(vec![spanned_alias.clone()]),
862
863            (_, Some(spanned_alias)) => Err(Error::validate_contexts(
864                format!(
865                    "\"path\" can only be specified when one `{}` is supplied.",
866                    capability_type,
867                ),
868                vec![spanned_alias.origin.clone()],
869            )),
870        }
871    }
872
873    capability_ids_from_names!(services_from, CapabilityId::Service);
874    capability_ids_from_names!(protocols_from, CapabilityId::Protocol);
875    capability_ids_from_names!(directories_from, CapabilityId::Directory);
876    capability_ids_from_names!(storages_from, CapabilityId::Storage);
877    capability_ids_from_names!(runners_from, CapabilityId::Runner);
878    capability_ids_from_names!(resolvers_from, CapabilityId::Resolver);
879    capability_ids_from_names!(event_streams_from, CapabilityId::EventStream);
880    capability_ids_from_names!(dictionaries_from, CapabilityId::Dictionary);
881    capability_ids_from_names!(configurations_from, CapabilityId::Configuration);
882
883    capability_ids_from_paths!(used_services_from, CapabilityId::UsedService);
884    capability_ids_from_paths!(used_protocols_from, CapabilityId::UsedProtocol);
885    capability_ids_from_paths!(used_dictionaries_from, CapabilityId::UsedDictionary);
886
887    capability_ids_from_context_names!(services_from_context, CapabilityId::Service);
888    capability_ids_from_context_names!(protocols_from_context, CapabilityId::Protocol);
889    capability_ids_from_context_names!(directories_from_context, CapabilityId::Directory);
890    capability_ids_from_context_names!(storages_from_context, CapabilityId::Storage);
891    capability_ids_from_context_names!(runners_from_context, CapabilityId::Runner);
892    capability_ids_from_context_names!(resolvers_from_context, CapabilityId::Resolver);
893    capability_ids_from_context_names!(event_streams_from_context, CapabilityId::EventStream);
894    capability_ids_from_context_names!(dictionaries_from_context, CapabilityId::Dictionary);
895    capability_ids_from_context_names!(configurations_from_context, CapabilityId::Configuration);
896
897    capability_ids_from_context_paths!(used_services_from_context, CapabilityId::UsedService);
898    capability_ids_from_context_paths!(used_protocols_from_context, CapabilityId::UsedProtocol);
899    capability_ids_from_context_paths!(
900        used_dictionaries_from_context,
901        CapabilityId::UsedDictionary
902    );
903}
904
905impl fmt::Display for CapabilityId<'_> {
906    /// Return the string ID of this clause.
907    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
908        match self {
909            CapabilityId::Service(n)
910            | CapabilityId::Storage(n)
911            | CapabilityId::Runner(n)
912            | CapabilityId::UsedRunner(n)
913            | CapabilityId::Resolver(n)
914            | CapabilityId::EventStream(n)
915            | CapabilityId::Configuration(n)
916            | CapabilityId::UsedConfiguration(n)
917            | CapabilityId::Dictionary(n) => write!(f, "{}", n),
918            CapabilityId::UsedService(p)
919            | CapabilityId::UsedProtocol(p)
920            | CapabilityId::UsedDirectory(p)
921            | CapabilityId::UsedStorage(p)
922            | CapabilityId::UsedEventStream(p)
923            | CapabilityId::UsedDictionary(p) => write!(f, "{}", p),
924            CapabilityId::UsedProtocolNumberedHandle(p) => write!(f, "{}", p),
925            CapabilityId::Protocol(p) | CapabilityId::Directory(p) => write!(f, "{}", p),
926        }
927    }
928}
929
930#[cfg(test)]
931mod tests {
932    use super::*;
933    use crate::error::Location;
934    use crate::types::offer::Offer;
935    use crate::types::r#use::Use;
936    use assert_matches::assert_matches;
937    use std::path::PathBuf;
938    use std::sync::Arc;
939
940    #[test]
941    fn test_offer_service() -> Result<(), Error> {
942        let a: Name = "a".parse().unwrap();
943        let b: Name = "b".parse().unwrap();
944        assert_eq!(
945            CapabilityId::from_offer_expose(&Offer {
946                service: Some(OneOrMany::One(a.clone())),
947                ..Offer::default()
948            },)?,
949            vec![CapabilityId::Service(&a)]
950        );
951
952        let synthetic_origin = Origin {
953            file: Arc::new(PathBuf::from("synthetic")),
954            location: Location { line: 0, column: 0 },
955        };
956
957        assert_eq!(
958            CapabilityId::from_context_offer(&ContextSpanned {
959                value: ContextOffer {
960                    service: Some(ContextSpanned {
961                        value: OneOrMany::One(a.clone()),
962                        origin: synthetic_origin.clone(),
963                    }),
964                    ..ContextOffer::default()
965                },
966                origin: synthetic_origin.clone(),
967            })?,
968            vec![(CapabilityId::Service(&a), synthetic_origin.clone())]
969        );
970
971        assert_eq!(
972            CapabilityId::from_offer_expose(&Offer {
973                service: Some(OneOrMany::Many(vec![a.clone(), b.clone()],)),
974                ..Offer::default()
975            },)?,
976            vec![CapabilityId::Service(&a), CapabilityId::Service(&b)]
977        );
978
979        assert_eq!(
980            CapabilityId::from_context_offer(&ContextSpanned {
981                value: ContextOffer {
982                    service: Some(ContextSpanned {
983                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
984                        origin: synthetic_origin.clone(),
985                    }),
986                    ..ContextOffer::default()
987                },
988                origin: synthetic_origin.clone(),
989            })?,
990            vec![
991                (CapabilityId::Service(&a), synthetic_origin.clone()),
992                (CapabilityId::Service(&b), synthetic_origin.clone())
993            ]
994        );
995
996        // "as" aliasing.
997        assert_eq!(
998            CapabilityId::from_offer_expose(&Offer {
999                service: Some(OneOrMany::One(a.clone())),
1000                r#as: Some(b.clone()),
1001                ..Offer::default()
1002            },)?,
1003            vec![CapabilityId::Service(&b)]
1004        );
1005
1006        assert_eq!(
1007            CapabilityId::from_context_offer(&ContextSpanned {
1008                value: ContextOffer {
1009                    service: Some(ContextSpanned {
1010                        value: OneOrMany::One(a.clone()),
1011                        origin: synthetic_origin.clone(),
1012                    }),
1013                    r#as: Some(ContextSpanned {
1014                        value: b.clone(),
1015                        origin: synthetic_origin.clone()
1016                    }),
1017                    ..ContextOffer::default()
1018                },
1019                origin: synthetic_origin.clone(),
1020            })?,
1021            vec![(CapabilityId::Service(&b), synthetic_origin)]
1022        );
1023
1024        Ok(())
1025    }
1026
1027    #[test]
1028    fn test_use_service() -> Result<(), Error> {
1029        let a: Name = "a".parse().unwrap();
1030        let b: Name = "b".parse().unwrap();
1031
1032        let synthetic_origin = Origin {
1033            file: Arc::new(PathBuf::from("synthetic")),
1034            location: Location { line: 0, column: 0 },
1035        };
1036
1037        assert_eq!(
1038            CapabilityId::from_use(&Use {
1039                service: Some(OneOrMany::One(a.clone())),
1040                ..Use::default()
1041            },)?,
1042            vec![CapabilityId::UsedService("/svc/a".parse().unwrap())]
1043        );
1044
1045        assert_eq!(
1046            CapabilityId::from_context_use(&ContextSpanned {
1047                value: ContextUse {
1048                    service: Some(ContextSpanned {
1049                        value: OneOrMany::One(a.clone()),
1050                        origin: synthetic_origin.clone(),
1051                    }),
1052                    ..ContextUse::default()
1053                },
1054                origin: synthetic_origin.clone(),
1055            })?,
1056            vec![(CapabilityId::UsedService("/svc/a".parse().unwrap()), synthetic_origin.clone())]
1057        );
1058
1059        assert_eq!(
1060            CapabilityId::from_use(&Use {
1061                service: Some(OneOrMany::Many(vec![a.clone(), b.clone(),],)),
1062                ..Use::default()
1063            },)?,
1064            vec![
1065                CapabilityId::UsedService("/svc/a".parse().unwrap()),
1066                CapabilityId::UsedService("/svc/b".parse().unwrap())
1067            ]
1068        );
1069
1070        assert_eq!(
1071            CapabilityId::from_context_use(&ContextSpanned {
1072                value: ContextUse {
1073                    service: Some(ContextSpanned {
1074                        value: OneOrMany::Many(vec![a.clone(), b.clone(),]),
1075                        origin: synthetic_origin.clone(),
1076                    }),
1077                    ..ContextUse::default()
1078                },
1079                origin: synthetic_origin.clone(),
1080            })?,
1081            vec![
1082                (CapabilityId::UsedService("/svc/a".parse().unwrap()), synthetic_origin.clone()),
1083                (CapabilityId::UsedService("/svc/b".parse().unwrap()), synthetic_origin.clone())
1084            ]
1085        );
1086
1087        assert_eq!(
1088            CapabilityId::from_use(&Use {
1089                service: Some(OneOrMany::One(a.clone())),
1090                path: Some("/b".parse().unwrap()),
1091                ..Use::default()
1092            },)?,
1093            vec![CapabilityId::UsedService("/b".parse().unwrap())]
1094        );
1095
1096        assert_eq!(
1097            CapabilityId::from_context_use(&ContextSpanned {
1098                value: ContextUse {
1099                    service: Some(ContextSpanned {
1100                        value: OneOrMany::One(a.clone()),
1101                        origin: synthetic_origin.clone(),
1102                    }),
1103                    path: Some(ContextSpanned {
1104                        value: "/b".parse().unwrap(),
1105                        origin: synthetic_origin.clone(),
1106                    }),
1107                    ..ContextUse::default()
1108                },
1109                origin: synthetic_origin.clone(),
1110            })?,
1111            vec![(CapabilityId::UsedService("/b".parse().unwrap()), synthetic_origin.clone())]
1112        );
1113
1114        Ok(())
1115    }
1116
1117    #[test]
1118    fn test_use_event_stream() -> Result<(), Error> {
1119        let synthetic_origin = Origin {
1120            file: Arc::new(PathBuf::from("synthetic")),
1121            location: Location { line: 0, column: 0 },
1122        };
1123
1124        assert_eq!(
1125            CapabilityId::from_use(&Use {
1126                event_stream: Some(OneOrMany::One(Name::new("test".to_string()).unwrap())),
1127                path: Some(cm_types::Path::new("/svc/myevent".to_string()).unwrap()),
1128                ..Use::default()
1129            },)?,
1130            vec![CapabilityId::UsedEventStream("/svc/myevent".parse().unwrap()),]
1131        );
1132
1133        assert_eq!(
1134            CapabilityId::from_context_use(&ContextSpanned {
1135                value: ContextUse {
1136                    event_stream: Some(ContextSpanned {
1137                        value: OneOrMany::One(Name::new("test".to_string()).unwrap()),
1138                        origin: synthetic_origin.clone(),
1139                    }),
1140                    path: Some(ContextSpanned {
1141                        value: cm_types::Path::new("/svc/myevent".to_string()).unwrap(),
1142                        origin: synthetic_origin.clone(),
1143                    }),
1144                    ..ContextUse::default()
1145                },
1146                origin: synthetic_origin.clone(),
1147            })?,
1148            vec![(
1149                CapabilityId::UsedEventStream("/svc/myevent".parse().unwrap()),
1150                synthetic_origin.clone()
1151            )]
1152        );
1153
1154        assert_eq!(
1155            CapabilityId::from_use(&Use {
1156                event_stream: Some(OneOrMany::One(Name::new("test".to_string()).unwrap())),
1157                ..Use::default()
1158            },)?,
1159            vec![CapabilityId::UsedEventStream(
1160                "/svc/fuchsia.component.EventStream".parse().unwrap()
1161            ),]
1162        );
1163
1164        assert_eq!(
1165            CapabilityId::from_context_use(&ContextSpanned {
1166                value: ContextUse {
1167                    event_stream: Some(ContextSpanned {
1168                        value: OneOrMany::One(Name::new("test".to_string()).unwrap()),
1169                        origin: synthetic_origin.clone(),
1170                    }),
1171                    ..ContextUse::default()
1172                },
1173                origin: synthetic_origin.clone(),
1174            })?,
1175            vec![(
1176                CapabilityId::UsedEventStream(
1177                    "/svc/fuchsia.component.EventStream".parse().unwrap()
1178                ),
1179                synthetic_origin.clone()
1180            )]
1181        );
1182
1183        Ok(())
1184    }
1185
1186    #[test]
1187    fn test_offer_protocol() -> Result<(), Error> {
1188        let a: Name = "a".parse().unwrap();
1189        let b: Name = "b".parse().unwrap();
1190
1191        let synthetic_origin = Origin {
1192            file: Arc::new(PathBuf::from("synthetic")),
1193            location: Location { line: 0, column: 0 },
1194        };
1195
1196        assert_eq!(
1197            CapabilityId::from_offer_expose(&Offer {
1198                protocol: Some(OneOrMany::One(a.clone())),
1199                ..Offer::default()
1200            },)?,
1201            vec![CapabilityId::Protocol(&a)]
1202        );
1203
1204        assert_eq!(
1205            CapabilityId::from_context_offer(&ContextSpanned {
1206                value: ContextOffer {
1207                    protocol: Some(ContextSpanned {
1208                        value: OneOrMany::One(a.clone()),
1209                        origin: synthetic_origin.clone(),
1210                    }),
1211                    ..ContextOffer::default()
1212                },
1213                origin: synthetic_origin.clone(),
1214            })?,
1215            vec![(CapabilityId::Protocol(&a), synthetic_origin.clone())]
1216        );
1217
1218        assert_eq!(
1219            CapabilityId::from_offer_expose(&Offer {
1220                protocol: Some(OneOrMany::Many(vec![a.clone(), b.clone()],)),
1221                ..Offer::default()
1222            },)?,
1223            vec![CapabilityId::Protocol(&a), CapabilityId::Protocol(&b)]
1224        );
1225
1226        assert_eq!(
1227            CapabilityId::from_context_offer(&ContextSpanned {
1228                value: ContextOffer {
1229                    protocol: Some(ContextSpanned {
1230                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
1231                        origin: synthetic_origin.clone(),
1232                    }),
1233                    ..ContextOffer::default()
1234                },
1235                origin: synthetic_origin.clone(),
1236            })?,
1237            vec![
1238                (CapabilityId::Protocol(&a), synthetic_origin.clone()),
1239                (CapabilityId::Protocol(&b), synthetic_origin)
1240            ]
1241        );
1242
1243        Ok(())
1244    }
1245
1246    #[test]
1247    fn test_use_protocol() -> Result<(), Error> {
1248        let a: Name = "a".parse().unwrap();
1249        let b: Name = "b".parse().unwrap();
1250
1251        let synthetic_origin = Origin {
1252            file: Arc::new(PathBuf::from("synthetic")),
1253            location: Location { line: 0, column: 0 },
1254        };
1255
1256        assert_eq!(
1257            CapabilityId::from_use(&Use {
1258                protocol: Some(OneOrMany::One(a.clone())),
1259                ..Use::default()
1260            },)?,
1261            vec![CapabilityId::UsedProtocol("/svc/a".parse().unwrap())]
1262        );
1263
1264        assert_eq!(
1265            CapabilityId::from_context_use(&ContextSpanned {
1266                value: ContextUse {
1267                    protocol: Some(ContextSpanned {
1268                        value: OneOrMany::One(a.clone()),
1269                        origin: synthetic_origin.clone(),
1270                    }),
1271                    ..ContextUse::default()
1272                },
1273                origin: synthetic_origin.clone(),
1274            })?,
1275            vec![(CapabilityId::UsedProtocol("/svc/a".parse().unwrap()), synthetic_origin.clone())]
1276        );
1277        assert_eq!(
1278            CapabilityId::from_use(&Use {
1279                protocol: Some(OneOrMany::Many(vec![a.clone(), b.clone(),],)),
1280                ..Use::default()
1281            },)?,
1282            vec![
1283                CapabilityId::UsedProtocol("/svc/a".parse().unwrap()),
1284                CapabilityId::UsedProtocol("/svc/b".parse().unwrap())
1285            ]
1286        );
1287
1288        assert_eq!(
1289            CapabilityId::from_context_use(&ContextSpanned {
1290                value: ContextUse {
1291                    protocol: Some(ContextSpanned {
1292                        value: OneOrMany::Many(vec![a.clone(), b.clone(),]),
1293                        origin: synthetic_origin.clone(),
1294                    }),
1295                    ..ContextUse::default()
1296                },
1297                origin: synthetic_origin.clone(),
1298            })?,
1299            vec![
1300                (CapabilityId::UsedProtocol("/svc/a".parse().unwrap()), synthetic_origin.clone()),
1301                (CapabilityId::UsedProtocol("/svc/b".parse().unwrap()), synthetic_origin.clone())
1302            ]
1303        );
1304
1305        assert_eq!(
1306            CapabilityId::from_use(&Use {
1307                protocol: Some(OneOrMany::One(a.clone())),
1308                path: Some("/b".parse().unwrap()),
1309                ..Use::default()
1310            },)?,
1311            vec![CapabilityId::UsedProtocol("/b".parse().unwrap())]
1312        );
1313
1314        assert_eq!(
1315            CapabilityId::from_context_use(&ContextSpanned {
1316                value: ContextUse {
1317                    protocol: Some(ContextSpanned {
1318                        value: OneOrMany::One(a.clone()),
1319                        origin: synthetic_origin.clone(),
1320                    }),
1321                    path: Some(ContextSpanned {
1322                        value: "/b".parse().unwrap(),
1323                        origin: synthetic_origin.clone(),
1324                    }),
1325                    ..ContextUse::default()
1326                },
1327                origin: synthetic_origin.clone(),
1328            })?,
1329            vec![(CapabilityId::UsedProtocol("/b".parse().unwrap()), synthetic_origin.clone())]
1330        );
1331
1332        Ok(())
1333    }
1334
1335    #[test]
1336    fn test_offer_directory() -> Result<(), Error> {
1337        let a: Name = "a".parse().unwrap();
1338        let b: Name = "b".parse().unwrap();
1339
1340        let synthetic_origin = Origin {
1341            file: Arc::new(PathBuf::from("synthetic")),
1342            location: Location { line: 0, column: 0 },
1343        };
1344
1345        assert_eq!(
1346            CapabilityId::from_offer_expose(&Offer {
1347                directory: Some(OneOrMany::One(a.clone())),
1348                ..Offer::default()
1349            },)?,
1350            vec![CapabilityId::Directory(&a)]
1351        );
1352
1353        assert_eq!(
1354            CapabilityId::from_context_offer(&ContextSpanned {
1355                value: ContextOffer {
1356                    directory: Some(ContextSpanned {
1357                        value: OneOrMany::One(a.clone()),
1358                        origin: synthetic_origin.clone(),
1359                    }),
1360                    ..ContextOffer::default()
1361                },
1362                origin: synthetic_origin.clone(),
1363            })?,
1364            vec![(CapabilityId::Directory(&a), synthetic_origin.clone())]
1365        );
1366
1367        assert_eq!(
1368            CapabilityId::from_offer_expose(&Offer {
1369                directory: Some(OneOrMany::Many(vec![a.clone(), b.clone()])),
1370                ..Offer::default()
1371            },)?,
1372            vec![CapabilityId::Directory(&a), CapabilityId::Directory(&b),]
1373        );
1374
1375        assert_eq!(
1376            CapabilityId::from_context_offer(&ContextSpanned {
1377                value: ContextOffer {
1378                    directory: Some(ContextSpanned {
1379                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
1380                        origin: synthetic_origin.clone(),
1381                    }),
1382                    ..ContextOffer::default()
1383                },
1384                origin: synthetic_origin.clone(),
1385            })?,
1386            vec![
1387                (CapabilityId::Directory(&a), synthetic_origin.clone()),
1388                (CapabilityId::Directory(&b), synthetic_origin.clone())
1389            ]
1390        );
1391
1392        Ok(())
1393    }
1394
1395    #[test]
1396    fn test_use_directory() -> Result<(), Error> {
1397        let a: Name = "a".parse().unwrap();
1398
1399        let synthetic_origin = Origin {
1400            file: Arc::new(PathBuf::from("synthetic")),
1401            location: Location { line: 0, column: 0 },
1402        };
1403
1404        assert_eq!(
1405            CapabilityId::from_use(&Use {
1406                directory: Some(a.clone()),
1407                path: Some("/b".parse().unwrap()),
1408                ..Use::default()
1409            },)?,
1410            vec![CapabilityId::UsedDirectory("/b".parse().unwrap())]
1411        );
1412
1413        assert_eq!(
1414            CapabilityId::from_context_use(&ContextSpanned {
1415                value: ContextUse {
1416                    directory: Some(ContextSpanned {
1417                        value: a.clone(),
1418                        origin: synthetic_origin.clone(),
1419                    }),
1420                    path: Some(ContextSpanned {
1421                        value: "/b".parse().unwrap(),
1422                        origin: synthetic_origin.clone(),
1423                    }),
1424                    ..ContextUse::default()
1425                },
1426                origin: synthetic_origin.clone(),
1427            })?,
1428            vec![(CapabilityId::UsedDirectory("/b".parse().unwrap()), synthetic_origin.clone())]
1429        );
1430
1431        Ok(())
1432    }
1433
1434    #[test]
1435    fn test_offer_storage() -> Result<(), Error> {
1436        let a: Name = "a".parse().unwrap();
1437        let b: Name = "b".parse().unwrap();
1438
1439        let synthetic_origin = Origin {
1440            file: Arc::new(PathBuf::from("synthetic")),
1441            location: Location { line: 0, column: 0 },
1442        };
1443
1444        assert_eq!(
1445            CapabilityId::from_offer_expose(&Offer {
1446                storage: Some(OneOrMany::One(a.clone())),
1447                ..Offer::default()
1448            },)?,
1449            vec![CapabilityId::Storage(&a)]
1450        );
1451
1452        assert_eq!(
1453            CapabilityId::from_context_offer(&ContextSpanned {
1454                value: ContextOffer {
1455                    storage: Some(ContextSpanned {
1456                        value: OneOrMany::One(a.clone()),
1457                        origin: synthetic_origin.clone(),
1458                    }),
1459                    ..ContextOffer::default()
1460                },
1461                origin: synthetic_origin.clone(),
1462            })?,
1463            vec![(CapabilityId::Storage(&a), synthetic_origin.clone())]
1464        );
1465
1466        assert_eq!(
1467            CapabilityId::from_offer_expose(&Offer {
1468                storage: Some(OneOrMany::Many(vec![a.clone(), b.clone()])),
1469                ..Offer::default()
1470            },)?,
1471            vec![CapabilityId::Storage(&a), CapabilityId::Storage(&b),]
1472        );
1473
1474        assert_eq!(
1475            CapabilityId::from_context_offer(&ContextSpanned {
1476                value: ContextOffer {
1477                    storage: Some(ContextSpanned {
1478                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
1479                        origin: synthetic_origin.clone(),
1480                    }),
1481                    ..ContextOffer::default()
1482                },
1483                origin: synthetic_origin.clone(),
1484            })?,
1485            vec![
1486                (CapabilityId::Storage(&a), synthetic_origin.clone()),
1487                (CapabilityId::Storage(&b), synthetic_origin.clone())
1488            ]
1489        );
1490
1491        Ok(())
1492    }
1493
1494    #[test]
1495    fn test_use_storage() -> Result<(), Error> {
1496        let a: Name = "a".parse().unwrap();
1497
1498        let synthetic_origin = Origin {
1499            file: Arc::new(PathBuf::from("synthetic")),
1500            location: Location { line: 0, column: 0 },
1501        };
1502
1503        assert_eq!(
1504            CapabilityId::from_use(&Use {
1505                storage: Some(a.clone()),
1506                path: Some("/b".parse().unwrap()),
1507                ..Use::default()
1508            },)?,
1509            vec![CapabilityId::UsedStorage("/b".parse().unwrap())]
1510        );
1511
1512        assert_eq!(
1513            CapabilityId::from_context_use(&ContextSpanned {
1514                value: ContextUse {
1515                    storage: Some(ContextSpanned {
1516                        value: a.clone(),
1517                        origin: synthetic_origin.clone(),
1518                    }),
1519                    path: Some(ContextSpanned {
1520                        value: "/b".parse().unwrap(),
1521                        origin: synthetic_origin.clone(),
1522                    }),
1523                    ..ContextUse::default()
1524                },
1525                origin: synthetic_origin.clone(),
1526            })?,
1527            vec![(CapabilityId::UsedStorage("/b".parse().unwrap()), synthetic_origin.clone())]
1528        );
1529
1530        Ok(())
1531    }
1532
1533    #[test]
1534    fn test_use_runner() -> Result<(), Error> {
1535        let synthetic_origin = Origin {
1536            file: Arc::new(PathBuf::from("synthetic")),
1537            location: Location { line: 0, column: 0 },
1538        };
1539
1540        assert_eq!(
1541            CapabilityId::from_use(&Use {
1542                runner: Some("elf".parse().unwrap()),
1543                ..Use::default()
1544            })?,
1545            vec![CapabilityId::UsedRunner(BorrowedName::new("elf").unwrap())]
1546        );
1547
1548        assert_eq!(
1549            CapabilityId::from_context_use(&ContextSpanned {
1550                value: ContextUse {
1551                    runner: Some(ContextSpanned {
1552                        value: "elf".parse().unwrap(),
1553                        origin: synthetic_origin.clone(),
1554                    }),
1555                    ..ContextUse::default()
1556                },
1557                origin: synthetic_origin.clone(),
1558            })?,
1559            vec![(
1560                CapabilityId::UsedRunner(BorrowedName::new("elf").unwrap()),
1561                synthetic_origin.clone()
1562            )]
1563        );
1564
1565        Ok(())
1566    }
1567
1568    #[test]
1569    fn test_offer_dictionary() -> Result<(), Error> {
1570        let a: Name = "a".parse().unwrap();
1571        let b: Name = "b".parse().unwrap();
1572
1573        let synthetic_origin = Origin {
1574            file: Arc::new(PathBuf::from("synthetic")),
1575            location: Location { line: 0, column: 0 },
1576        };
1577
1578        assert_eq!(
1579            CapabilityId::from_offer_expose(&Offer {
1580                dictionary: Some(OneOrMany::One(a.clone())),
1581                ..Offer::default()
1582            },)?,
1583            vec![CapabilityId::Dictionary(&a)]
1584        );
1585
1586        assert_eq!(
1587            CapabilityId::from_context_offer(&ContextSpanned {
1588                value: ContextOffer {
1589                    dictionary: Some(ContextSpanned {
1590                        value: OneOrMany::One(a.clone()),
1591                        origin: synthetic_origin.clone(),
1592                    }),
1593                    ..ContextOffer::default()
1594                },
1595                origin: synthetic_origin.clone(),
1596            })?,
1597            vec![(CapabilityId::Dictionary(&a), synthetic_origin.clone())]
1598        );
1599
1600        assert_eq!(
1601            CapabilityId::from_offer_expose(&Offer {
1602                dictionary: Some(OneOrMany::Many(vec![a.clone(), b.clone()],)),
1603                ..Offer::default()
1604            },)?,
1605            vec![CapabilityId::Dictionary(&a), CapabilityId::Dictionary(&b)]
1606        );
1607
1608        assert_eq!(
1609            CapabilityId::from_context_offer(&ContextSpanned {
1610                value: ContextOffer {
1611                    dictionary: Some(ContextSpanned {
1612                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
1613                        origin: synthetic_origin.clone(),
1614                    }),
1615                    ..ContextOffer::default()
1616                },
1617                origin: synthetic_origin.clone(),
1618            })?,
1619            vec![
1620                (CapabilityId::Dictionary(&a), synthetic_origin.clone()),
1621                (CapabilityId::Dictionary(&b), synthetic_origin.clone())
1622            ]
1623        );
1624
1625        Ok(())
1626    }
1627
1628    #[test]
1629    fn test_use_dictionary() -> Result<(), Error> {
1630        let a: Name = "a".parse().unwrap();
1631        let b: Name = "b".parse().unwrap();
1632
1633        let synthetic_origin = Origin {
1634            file: Arc::new(PathBuf::from("synthetic")),
1635            location: Location { line: 0, column: 0 },
1636        };
1637
1638        assert_eq!(
1639            CapabilityId::from_use(&Use {
1640                dictionary: Some(OneOrMany::One(a.clone())),
1641                ..Use::default()
1642            },)?,
1643            vec![CapabilityId::UsedDictionary("/svc/a".parse().unwrap())]
1644        );
1645
1646        assert_eq!(
1647            CapabilityId::from_context_use(&ContextSpanned {
1648                value: ContextUse {
1649                    dictionary: Some(ContextSpanned {
1650                        value: OneOrMany::One(a.clone()),
1651                        origin: synthetic_origin.clone(),
1652                    }),
1653                    ..ContextUse::default()
1654                },
1655                origin: synthetic_origin.clone(),
1656            })?,
1657            vec![(
1658                CapabilityId::UsedDictionary("/svc/a".parse().unwrap()),
1659                synthetic_origin.clone()
1660            )]
1661        );
1662
1663        assert_eq!(
1664            CapabilityId::from_use(&Use {
1665                dictionary: Some(OneOrMany::Many(vec![a.clone(), b.clone(),],)),
1666                ..Use::default()
1667            },)?,
1668            vec![
1669                CapabilityId::UsedDictionary("/svc/a".parse().unwrap()),
1670                CapabilityId::UsedDictionary("/svc/b".parse().unwrap())
1671            ]
1672        );
1673
1674        assert_eq!(
1675            CapabilityId::from_context_use(&ContextSpanned {
1676                value: ContextUse {
1677                    dictionary: Some(ContextSpanned {
1678                        value: OneOrMany::Many(vec![a.clone(), b.clone()]),
1679                        origin: synthetic_origin.clone(),
1680                    }),
1681                    ..ContextUse::default()
1682                },
1683                origin: synthetic_origin.clone(),
1684            })?,
1685            vec![
1686                (CapabilityId::UsedDictionary("/svc/a".parse().unwrap()), synthetic_origin.clone()),
1687                (CapabilityId::UsedDictionary("/svc/b".parse().unwrap()), synthetic_origin.clone())
1688            ]
1689        );
1690
1691        assert_eq!(
1692            CapabilityId::from_use(&Use {
1693                dictionary: Some(OneOrMany::One(a.clone())),
1694                path: Some("/b".parse().unwrap()),
1695                ..Use::default()
1696            },)?,
1697            vec![CapabilityId::UsedDictionary("/b".parse().unwrap())]
1698        );
1699
1700        assert_eq!(
1701            CapabilityId::from_context_use(&ContextSpanned {
1702                value: ContextUse {
1703                    dictionary: Some(ContextSpanned {
1704                        value: OneOrMany::One(a.clone()),
1705                        origin: synthetic_origin.clone(),
1706                    }),
1707                    path: Some(ContextSpanned {
1708                        value: "/b".parse().unwrap(),
1709                        origin: synthetic_origin.clone()
1710                    }),
1711                    ..ContextUse::default()
1712                },
1713                origin: synthetic_origin.clone(),
1714            })?,
1715            vec![(CapabilityId::UsedDictionary("/b".parse().unwrap()), synthetic_origin.clone())]
1716        );
1717
1718        Ok(())
1719    }
1720
1721    #[test]
1722    fn test_errors() -> Result<(), Error> {
1723        assert_matches!(CapabilityId::from_offer_expose(&Offer::default()), Err(_));
1724
1725        let synthetic_origin = Origin {
1726            file: Arc::new(PathBuf::from("synthetic")),
1727            location: Location { line: 0, column: 0 },
1728        };
1729
1730        assert_matches!(
1731            CapabilityId::from_context_offer(&ContextSpanned {
1732                value: ContextOffer::default(),
1733                origin: synthetic_origin
1734            }),
1735            Err(_)
1736        );
1737
1738        Ok(())
1739    }
1740}