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::{
6    AsClause, Capability, CapabilityClause, Error, Location, PathClause, SpannedCapability,
7    SpannedCapabilityClause, SpannedExpose, SpannedOffer, SpannedUse, Use, alias_or_name,
8    byte_index_to_location,
9};
10
11use crate::one_or_many::OneOrMany;
12pub use cm_types::{
13    Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
14    NamespacePath, OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
15};
16use json_spanned_value::Spanned;
17
18use std::fmt;
19
20/// A name/identity of a capability exposed/offered to another component.
21///
22/// Exposed or offered capabilities have an identifier whose format
23/// depends on the capability type. For directories and services this is
24/// a path, while for storage this is a storage name. Paths and storage
25/// names, however, are in different conceptual namespaces, and can't
26/// collide with each other.
27///
28/// This enum allows such names to be specified disambiguating what
29/// namespace they are in.
30#[derive(Debug, PartialEq, Eq, Hash, Clone)]
31pub enum CapabilityId<'a> {
32    Service(&'a BorrowedName),
33    Protocol(&'a BorrowedName),
34    Directory(&'a BorrowedName),
35    // A service in a `use` declaration has a target path in the component's namespace.
36    UsedService(Path),
37    // A protocol in a `use` declaration has a target path in the component's namespace.
38    UsedProtocol(Path),
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<&BorrowedName>` -> `Vec<CapabilityId>` conversion function.
59macro_rules! capability_ids_from_names {
60    ($name:ident, $variant:expr) => {
61        fn $name(names: Vec<&'a BorrowedName>) -> Vec<Self> {
62            names.into_iter().map(|n| $variant(n)).collect()
63        }
64    };
65}
66
67/// Generates a `Vec<Path>` -> `Vec<CapabilityId>` conversion function.
68macro_rules! capability_ids_from_paths {
69    ($name:ident, $variant:expr) => {
70        fn $name(paths: Vec<Path>) -> Vec<Self> {
71            paths.into_iter().map(|p| $variant(p)).collect()
72        }
73    };
74}
75
76impl<'a> CapabilityId<'a> {
77    /// Human readable description of this capability type.
78    pub fn type_str(&self) -> &'static str {
79        match self {
80            CapabilityId::Service(_) => "service",
81            CapabilityId::Protocol(_) => "protocol",
82            CapabilityId::Directory(_) => "directory",
83            CapabilityId::UsedService(_) => "service",
84            CapabilityId::UsedProtocol(_) => "protocol",
85            CapabilityId::UsedDirectory(_) => "directory",
86            CapabilityId::UsedStorage(_) => "storage",
87            CapabilityId::UsedEventStream(_) => "event_stream",
88            CapabilityId::UsedRunner(_) => "runner",
89            CapabilityId::UsedConfiguration(_) => "config",
90            CapabilityId::UsedDictionary(_) => "dictionary",
91            CapabilityId::Storage(_) => "storage",
92            CapabilityId::Runner(_) => "runner",
93            CapabilityId::Resolver(_) => "resolver",
94            CapabilityId::EventStream(_) => "event_stream",
95            CapabilityId::Dictionary(_) => "dictionary",
96            CapabilityId::Configuration(_) => "config",
97        }
98    }
99
100    /// Return the directory containing the capability, if this capability takes a target path.
101    pub fn get_dir_path(&self) -> Option<NamespacePath> {
102        match self {
103            CapabilityId::UsedService(p)
104            | CapabilityId::UsedProtocol(p)
105            | CapabilityId::UsedEventStream(p) => Some(p.parent()),
106            CapabilityId::UsedDirectory(p)
107            | CapabilityId::UsedStorage(p)
108            | CapabilityId::UsedDictionary(p) => Some(p.clone().into()),
109            _ => None,
110        }
111    }
112
113    /// Return the target path of the capability, if this capability has one.
114    pub fn get_target_path(&self) -> Option<NamespacePath> {
115        match self {
116            CapabilityId::UsedService(p)
117            | CapabilityId::UsedProtocol(p)
118            | CapabilityId::UsedEventStream(p)
119            | CapabilityId::UsedDirectory(p)
120            | CapabilityId::UsedStorage(p)
121            | CapabilityId::UsedDictionary(p) => Some(p.clone().into()),
122            _ => None,
123        }
124    }
125
126    /// Given a Use clause, return the set of target identifiers.
127    ///
128    /// When only one capability identifier is specified, the target identifier name is derived
129    /// using the "path" clause. If a "path" clause is not specified, the target identifier is the
130    /// same name as the source.
131    ///
132    /// When multiple capability identifiers are specified, the target names are the same as the
133    /// source names.
134    pub fn from_use(use_: &'a Use) -> Result<Vec<Self>, Error> {
135        // TODO: Validate that exactly one of these is set.
136        let alias = use_.path.as_ref();
137        if let Some(n) = use_.service() {
138            return Ok(Self::used_services_from(Self::get_one_or_many_svc_paths(
139                n,
140                alias,
141                use_.capability_type().unwrap(),
142            )?));
143        } else if let Some(n) = use_.protocol() {
144            return Ok(Self::used_protocols_from(Self::get_one_or_many_svc_paths(
145                n,
146                alias,
147                use_.capability_type().unwrap(),
148            )?));
149        } else if let Some(_) = use_.directory.as_ref() {
150            if use_.path.is_none() {
151                return Err(Error::validate("\"path\" should be present for `use directory`."));
152            }
153            return Ok(vec![CapabilityId::UsedDirectory(use_.path.as_ref().unwrap().clone())]);
154        } else if let Some(_) = use_.storage.as_ref() {
155            if use_.path.is_none() {
156                return Err(Error::validate("\"path\" should be present for `use storage`."));
157            }
158            return Ok(vec![CapabilityId::UsedStorage(use_.path.as_ref().unwrap().clone())]);
159        } else if let Some(_) = use_.event_stream() {
160            if let Some(path) = use_.path() {
161                return Ok(vec![CapabilityId::UsedEventStream(path.clone())]);
162            }
163            return Ok(vec![CapabilityId::UsedEventStream(Path::new(
164                "/svc/fuchsia.component.EventStream",
165            )?)]);
166        } else if let Some(n) = use_.runner() {
167            match n {
168                OneOrMany::One(name) => {
169                    return Ok(vec![CapabilityId::UsedRunner(name)]);
170                }
171                OneOrMany::Many(_) => {
172                    return Err(Error::validate("`use runner` should occur at most once."));
173                }
174            }
175        } else if let Some(_) = use_.config() {
176            return match &use_.key {
177                None => Err(Error::validate("\"key\" should be present for `use config`.")),
178                Some(name) => Ok(vec![CapabilityId::UsedConfiguration(name)]),
179            };
180        } else if let Some(n) = use_.dictionary() {
181            return Ok(Self::used_dictionaries_from(Self::get_one_or_many_svc_paths(
182                n,
183                alias,
184                use_.capability_type().unwrap(),
185            )?));
186        }
187        // Unsupported capability type.
188        let supported_keywords = use_
189            .supported()
190            .into_iter()
191            .map(|k| format!("\"{}\"", k))
192            .collect::<Vec<_>>()
193            .join(", ");
194        Err(Error::validate(format!(
195            "`{}` declaration is missing a capability keyword, one of: {}",
196            use_.decl_type(),
197            supported_keywords,
198        )))
199    }
200
201    /// Given a Use clause, return the set of target identifiers.
202    ///
203    /// When only one capability identifier is specified, the target identifier name is derived
204    /// using the "path" clause. If a "path" clause is not specified, the target identifier is the
205    /// same name as the source.
206    ///
207    /// When multiple capability identifiers are specified, the target names are the same as the
208    /// source names.
209    pub fn from_spanned_use(
210        use_: &'a Spanned<SpannedUse>,
211        filename: Option<&std::path::Path>,
212        file_source: Option<&String>,
213    ) -> Result<Vec<Self>, Error> {
214        // TODO: Validate that exactly one of these is set.
215        let alias = use_.path.as_ref();
216        if let Some(n) = use_.service() {
217            return Ok(Self::used_services_from(Self::get_one_or_many_svc_paths(
218                n,
219                alias.map(|v| &**v),
220                use_.capability_type().unwrap(),
221            )?));
222        } else if let Some(n) = use_.protocol() {
223            return Ok(Self::used_protocols_from(Self::get_one_or_many_svc_paths(
224                n,
225                alias.map(|v| &**v),
226                use_.capability_type().unwrap(),
227            )?));
228        } else if let Some(_) = use_.directory.as_ref() {
229            if use_.path.is_none() {
230                let location =
231                    byte_index_to_location(file_source, use_.directory.as_ref().unwrap().span().0);
232                return Err(Error::validate_with_span(
233                    "\"path\" should be present for `use directory`.",
234                    location,
235                    filename,
236                ));
237            }
238            return Ok(vec![CapabilityId::UsedDirectory(
239                use_.path.as_ref().unwrap().get_ref().clone(),
240            )]);
241        } else if let Some(_) = use_.storage.as_ref() {
242            if use_.path.is_none() {
243                let location =
244                    byte_index_to_location(file_source, use_.storage.as_ref().unwrap().span().0);
245                return Err(Error::validate_with_span(
246                    "\"path\" should be present for `use storage`.",
247                    location,
248                    filename,
249                ));
250            }
251            return Ok(vec![CapabilityId::UsedStorage(
252                use_.path.as_ref().unwrap().get_ref().clone(),
253            )]);
254        } else if let Some(_) = use_.event_stream() {
255            if let Some(path) = use_.path() {
256                return Ok(vec![CapabilityId::UsedEventStream(path.clone())]);
257            }
258            return Ok(vec![CapabilityId::UsedEventStream(Path::new(
259                "/svc/fuchsia.component.EventStream",
260            )?)]);
261        } else if let Some(n) = use_.runner() {
262            match n {
263                OneOrMany::One(name) => {
264                    return Ok(vec![CapabilityId::UsedRunner(name)]);
265                }
266                OneOrMany::Many(_) => {
267                    let location =
268                        byte_index_to_location(file_source, use_.runner.as_ref().unwrap().span().0);
269                    return Err(Error::validate_with_span(
270                        "`use runner` should occur at most once.",
271                        location,
272                        filename,
273                    ));
274                }
275            }
276        } else if let Some(_) = use_.config() {
277            return match &use_.key {
278                None => {
279                    let location =
280                        byte_index_to_location(file_source, use_.config.as_ref().unwrap().span().0);
281                    Err(Error::validate_with_span(
282                        "\"key\" should be present for `use config`.",
283                        location,
284                        filename,
285                    ))
286                }
287                Some(name) => Ok(vec![CapabilityId::UsedConfiguration(name)]),
288            };
289        } else if let Some(n) = use_.dictionary() {
290            return Ok(Self::used_dictionaries_from(Self::get_one_or_many_svc_paths(
291                n,
292                alias.map(|v| &**v),
293                use_.capability_type().unwrap(),
294            )?));
295        }
296        // Unsupported capability type.
297        let supported_keywords = use_
298            .supported()
299            .into_iter()
300            .map(|k| format!("\"{}\"", k))
301            .collect::<Vec<_>>()
302            .join(", ");
303
304        let location = byte_index_to_location(file_source, use_.span().0);
305
306        Err(Error::validate_with_span(
307            format!(
308                "`{}` declaration is missing a capability keyword, one of: {}",
309                use_.decl_type(),
310                supported_keywords,
311            ),
312            location,
313            filename,
314        ))
315    }
316
317    pub fn from_capability(capability: &'a Capability) -> Result<Vec<Self>, Error> {
318        // TODO: Validate that exactly one of these is set.
319        if let Some(n) = capability.service() {
320            if n.is_many() && capability.path.is_some() {
321                return Err(Error::validate(
322                    "\"path\" can only be specified when one `service` is supplied.",
323                ));
324            }
325            return Ok(Self::services_from(Self::get_one_or_many_names_no_span(
326                n,
327                None,
328                capability.capability_type().unwrap(),
329            )?));
330        } else if let Some(n) = capability.protocol() {
331            if n.is_many() && capability.path.is_some() {
332                return Err(Error::validate(
333                    "\"path\" can only be specified when one `protocol` is supplied.",
334                ));
335            }
336            return Ok(Self::protocols_from(Self::get_one_or_many_names_no_span(
337                n,
338                None,
339                capability.capability_type().unwrap(),
340            )?));
341        } else if let Some(n) = capability.directory() {
342            return Ok(Self::directories_from(Self::get_one_or_many_names_no_span(
343                n,
344                None,
345                capability.capability_type().unwrap(),
346            )?));
347        } else if let Some(n) = capability.storage() {
348            if capability.storage_id.is_none() {
349                return Err(Error::validate(
350                    "Storage declaration is missing \"storage_id\", but is required.",
351                ));
352            }
353            return Ok(Self::storages_from(Self::get_one_or_many_names_no_span(
354                n,
355                None,
356                capability.capability_type().unwrap(),
357            )?));
358        } else if let Some(n) = capability.runner() {
359            return Ok(Self::runners_from(Self::get_one_or_many_names_no_span(
360                n,
361                None,
362                capability.capability_type().unwrap(),
363            )?));
364        } else if let Some(n) = capability.resolver() {
365            return Ok(Self::resolvers_from(Self::get_one_or_many_names_no_span(
366                n,
367                None,
368                capability.capability_type().unwrap(),
369            )?));
370        } else if let Some(n) = capability.event_stream() {
371            return Ok(Self::event_streams_from(Self::get_one_or_many_names_no_span(
372                n,
373                None,
374                capability.capability_type().unwrap(),
375            )?));
376        } else if let Some(n) = capability.dictionary() {
377            return Ok(Self::dictionaries_from(Self::get_one_or_many_names_no_span(
378                n,
379                None,
380                capability.capability_type().unwrap(),
381            )?));
382        } else if let Some(n) = capability.config() {
383            return Ok(Self::configurations_from(Self::get_one_or_many_names_no_span(
384                n,
385                None,
386                capability.capability_type().unwrap(),
387            )?));
388        }
389
390        // Unsupported capability type.
391        let supported_keywords = capability
392            .supported()
393            .into_iter()
394            .map(|k| format!("\"{}\"", k))
395            .collect::<Vec<_>>()
396            .join(", ");
397        Err(Error::validate(format!(
398            "`{}` declaration is missing a capability keyword, one of: {}",
399            capability.decl_type(),
400            supported_keywords,
401        )))
402    }
403
404    pub fn from_spanned_capability(
405        capability: &'a Spanned<SpannedCapability>,
406        filename: Option<&std::path::Path>,
407        file_source: Option<&String>,
408    ) -> Result<Vec<Self>, Error> {
409        // TODO: Validate that exactly one of these is set.
410        if let Some(n) = capability.service() {
411            if n.is_many() && capability.path.is_some() {
412                let location =
413                    byte_index_to_location(file_source, capability.path.as_ref().unwrap().span().0);
414                return Err(Error::validate_with_span(
415                    "\"path\" can only be specified when one `service` is supplied.",
416                    location,
417                    filename,
418                ));
419            }
420            return Ok(Self::services_from(Self::get_names(n)?));
421        } else if let Some(n) = capability.protocol() {
422            if n.is_many() && capability.path.is_some() {
423                let location =
424                    byte_index_to_location(file_source, capability.path.as_ref().unwrap().span().0);
425                return Err(Error::validate_with_span(
426                    "\"path\" can only be specified when one `protocol` is supplied.",
427                    location,
428                    filename,
429                ));
430            }
431            return Ok(Self::protocols_from(Self::get_names(n)?));
432        } else if let Some(n) = capability.directory() {
433            return Ok(Self::directories_from(Self::get_names(n)?));
434        } else if let Some(n) = capability.storage() {
435            if capability.storage_id.is_none() {
436                let location = byte_index_to_location(
437                    file_source,
438                    capability.storage.as_ref().unwrap().span().0,
439                );
440                return Err(Error::validate_with_span(
441                    "Storage declaration is missing \"storage_id\", but is required.",
442                    location,
443                    filename,
444                ));
445            }
446            return Ok(Self::storages_from(Self::get_names(n)?));
447        } else if let Some(n) = capability.runner() {
448            return Ok(Self::runners_from(Self::get_names(n)?));
449        } else if let Some(n) = capability.resolver() {
450            return Ok(Self::resolvers_from(Self::get_names(n)?));
451        } else if let Some(n) = capability.event_stream() {
452            return Ok(Self::event_streams_from(Self::get_names(n)?));
453        } else if let Some(n) = capability.dictionary() {
454            return Ok(Self::dictionaries_from(Self::get_names(n)?));
455        } else if let Some(n) = capability.config() {
456            return Ok(Self::configurations_from(Self::get_names(n)?));
457        }
458
459        // Unsupported capability type.
460        let supported_keywords = capability
461            .supported()
462            .into_iter()
463            .map(|k| format!("\"{}\"", k))
464            .collect::<Vec<_>>()
465            .join(", ");
466        let location = byte_index_to_location(file_source, capability.span().0);
467
468        Err(Error::validate_with_span(
469            format!(
470                "`{}` declaration is missing a capability keyword, one of: {}",
471                capability.decl_type(),
472                supported_keywords,
473            ),
474            location,
475            filename,
476        ))
477    }
478
479    /// Given an Offer or Expose clause, return the set of target identifiers.
480    ///
481    /// When only one capability identifier is specified, the target identifier name is derived
482    /// using the "as" clause. If an "as" clause is not specified, the target identifier is the
483    /// same name as the source.
484    ///
485    /// When multiple capability identifiers are specified, the target names are the same as the
486    /// source names.
487    pub fn from_offer_expose<T>(clause: &'a T) -> Result<Vec<Self>, Error>
488    where
489        T: CapabilityClause + AsClause + fmt::Debug,
490    {
491        // TODO: Validate that exactly one of these is set.
492        let alias = clause.r#as();
493        if let Some(n) = clause.service() {
494            return Ok(Self::services_from(Self::get_one_or_many_names_no_span(
495                n,
496                alias,
497                clause.capability_type().unwrap(),
498            )?));
499        } else if let Some(n) = clause.protocol() {
500            return Ok(Self::protocols_from(Self::get_one_or_many_names_no_span(
501                n,
502                alias,
503                clause.capability_type().unwrap(),
504            )?));
505        } else if let Some(n) = clause.directory() {
506            return Ok(Self::directories_from(Self::get_one_or_many_names_no_span(
507                n,
508                alias,
509                clause.capability_type().unwrap(),
510            )?));
511        } else if let Some(n) = clause.storage() {
512            return Ok(Self::storages_from(Self::get_one_or_many_names_no_span(
513                n,
514                alias,
515                clause.capability_type().unwrap(),
516            )?));
517        } else if let Some(n) = clause.runner() {
518            return Ok(Self::runners_from(Self::get_one_or_many_names_no_span(
519                n,
520                alias,
521                clause.capability_type().unwrap(),
522            )?));
523        } else if let Some(n) = clause.resolver() {
524            return Ok(Self::resolvers_from(Self::get_one_or_many_names_no_span(
525                n,
526                alias,
527                clause.capability_type().unwrap(),
528            )?));
529        } else if let Some(event_stream) = clause.event_stream() {
530            return Ok(Self::event_streams_from(Self::get_one_or_many_names_no_span(
531                event_stream,
532                alias,
533                clause.capability_type().unwrap(),
534            )?));
535        } else if let Some(n) = clause.dictionary() {
536            return Ok(Self::dictionaries_from(Self::get_one_or_many_names_no_span(
537                n,
538                alias,
539                clause.capability_type().unwrap(),
540            )?));
541        } else if let Some(n) = clause.config() {
542            return Ok(Self::configurations_from(Self::get_one_or_many_names_no_span(
543                n,
544                alias,
545                clause.capability_type().unwrap(),
546            )?));
547        }
548
549        // Unsupported capability type.
550        let supported_keywords = clause
551            .supported()
552            .into_iter()
553            .map(|k| format!("\"{}\"", k))
554            .collect::<Vec<_>>()
555            .join(", ");
556        Err(Error::validate(format!(
557            "`{}` declaration is missing a capability keyword, one of: {}",
558            clause.decl_type(),
559            supported_keywords,
560        )))
561    }
562
563    /// Given an Offer or Expose clause, return the set of target identifiers.
564    ///
565    /// When only one capability identifier is specified, the target identifier name is derived
566    /// using the "as" clause. If an "as" clause is not specified, the target identifier is the
567    /// same name as the source.
568    ///
569    /// When multiple capability identifiers are specified, the target names are the same as the
570    /// source names.
571    pub fn from_spanned_expose(
572        expose: &'a Spanned<SpannedExpose>,
573        filename: Option<&std::path::Path>,
574        file_source: Option<&String>,
575    ) -> Result<Vec<Self>, Error> {
576        // TODO: Validate that exactly one of these is set.
577        let alias = expose.r#as();
578        let location = byte_index_to_location(file_source, expose.span().0);
579
580        if let Some(n) = expose.service() {
581            return Ok(Self::services_from(Self::get_one_or_many_names(
582                n,
583                alias,
584                expose.capability_type().unwrap(),
585                location,
586                filename,
587            )?));
588        } else if let Some(n) = expose.protocol() {
589            return Ok(Self::protocols_from(Self::get_one_or_many_names(
590                n,
591                alias,
592                expose.capability_type().unwrap(),
593                location,
594                filename,
595            )?));
596        } else if let Some(n) = expose.directory() {
597            return Ok(Self::directories_from(Self::get_one_or_many_names(
598                n,
599                alias,
600                expose.capability_type().unwrap(),
601                location,
602                filename,
603            )?));
604        } else if let Some(n) = expose.storage() {
605            return Ok(Self::storages_from(Self::get_one_or_many_names(
606                n,
607                alias,
608                expose.capability_type().unwrap(),
609                location,
610                filename,
611            )?));
612        } else if let Some(n) = expose.runner() {
613            return Ok(Self::runners_from(Self::get_one_or_many_names(
614                n,
615                alias,
616                expose.capability_type().unwrap(),
617                location,
618                filename,
619            )?));
620        } else if let Some(n) = expose.resolver() {
621            return Ok(Self::resolvers_from(Self::get_one_or_many_names(
622                n,
623                alias,
624                expose.capability_type().unwrap(),
625                location,
626                filename,
627            )?));
628        } else if let Some(event_stream) = expose.event_stream() {
629            return Ok(Self::event_streams_from(Self::get_one_or_many_names(
630                event_stream,
631                alias,
632                expose.capability_type().unwrap(),
633                location,
634                filename,
635            )?));
636        } else if let Some(n) = expose.dictionary() {
637            return Ok(Self::dictionaries_from(Self::get_one_or_many_names(
638                n,
639                alias,
640                expose.capability_type().unwrap(),
641                location,
642                filename,
643            )?));
644        } else if let Some(n) = expose.config() {
645            return Ok(Self::configurations_from(Self::get_one_or_many_names(
646                n,
647                alias,
648                expose.capability_type().unwrap(),
649                location,
650                filename,
651            )?));
652        }
653
654        // Unsupported capability type.
655        let supported_keywords = expose
656            .supported()
657            .into_iter()
658            .map(|k| format!("\"{}\"", k))
659            .collect::<Vec<_>>()
660            .join(", ");
661        Err(Error::validate_with_span(
662            format!(
663                "`{}` declaration is missing a capability keyword, one of: {}",
664                expose.decl_type(),
665                supported_keywords,
666            ),
667            location,
668            filename,
669        ))
670    }
671
672    pub fn from_spanned_offer(
673        offer: &'a Spanned<SpannedOffer>,
674        filename: Option<&std::path::Path>,
675        file_source: Option<&String>,
676    ) -> Result<Vec<Self>, Error> {
677        // TODO: Validate that exactly one of these is set.
678        let alias = offer.r#as();
679        let location = byte_index_to_location(file_source, offer.span().0);
680
681        if let Some(n) = offer.service() {
682            return Ok(Self::services_from(Self::get_one_or_many_names(
683                n,
684                alias,
685                offer.capability_type().unwrap(),
686                location,
687                filename,
688            )?));
689        } else if let Some(n) = offer.protocol() {
690            return Ok(Self::protocols_from(Self::get_one_or_many_names(
691                n,
692                alias,
693                offer.capability_type().unwrap(),
694                location,
695                filename,
696            )?));
697        } else if let Some(n) = offer.directory() {
698            return Ok(Self::directories_from(Self::get_one_or_many_names(
699                n,
700                alias,
701                offer.capability_type().unwrap(),
702                location,
703                filename,
704            )?));
705        } else if let Some(n) = offer.storage() {
706            return Ok(Self::storages_from(Self::get_one_or_many_names(
707                n,
708                alias,
709                offer.capability_type().unwrap(),
710                location,
711                filename,
712            )?));
713        } else if let Some(n) = offer.runner() {
714            return Ok(Self::runners_from(Self::get_one_or_many_names(
715                n,
716                alias,
717                offer.capability_type().unwrap(),
718                location,
719                filename,
720            )?));
721        } else if let Some(n) = offer.resolver() {
722            return Ok(Self::resolvers_from(Self::get_one_or_many_names(
723                n,
724                alias,
725                offer.capability_type().unwrap(),
726                location,
727                filename,
728            )?));
729        } else if let Some(event_stream) = offer.event_stream() {
730            return Ok(Self::event_streams_from(Self::get_one_or_many_names(
731                event_stream,
732                alias,
733                offer.capability_type().unwrap(),
734                location,
735                filename,
736            )?));
737        } else if let Some(n) = offer.dictionary() {
738            return Ok(Self::dictionaries_from(Self::get_one_or_many_names(
739                n,
740                alias,
741                offer.capability_type().unwrap(),
742                location,
743                filename,
744            )?));
745        } else if let Some(n) = offer.config() {
746            return Ok(Self::configurations_from(Self::get_one_or_many_names(
747                n,
748                alias,
749                offer.capability_type().unwrap(),
750                location,
751                filename,
752            )?));
753        }
754
755        // Unsupported capability type.
756        let supported_keywords = offer
757            .supported()
758            .into_iter()
759            .map(|k| format!("\"{}\"", k))
760            .collect::<Vec<_>>()
761            .join(", ");
762        Err(Error::validate_with_span(
763            format!(
764                "`{}` declaration is missing a capability keyword, one of: {}",
765                offer.decl_type(),
766                supported_keywords,
767            ),
768            location,
769            filename,
770        ))
771    }
772
773    /// Returns the target names as a `Vec` from a declaration with `names` and `alias` as a `Vec`.
774    fn get_one_or_many_names_no_span<'b>(
775        names: OneOrMany<&'b BorrowedName>,
776        alias: Option<&'b BorrowedName>,
777        capability_type: &str,
778    ) -> Result<Vec<&'b BorrowedName>, Error> {
779        let names: Vec<&BorrowedName> = names.into_iter().collect();
780        if names.len() == 1 {
781            Ok(vec![alias_or_name(alias, &names[0])])
782        } else {
783            if alias.is_some() {
784                return Err(Error::validate(format!(
785                    "\"as\" can only be specified when one `{}` is supplied.",
786                    capability_type,
787                )));
788            }
789            Ok(names)
790        }
791    }
792
793    /// Returns the target names as a `Vec`.
794    fn get_names<'b>(names: OneOrMany<&'b BorrowedName>) -> Result<Vec<&'b BorrowedName>, Error> {
795        let names: Vec<&BorrowedName> = names.into_iter().collect();
796        Ok(names)
797    }
798
799    /// Returns the target names as a `Vec` from a declaration with `names` and `alias` as a `Vec`.
800    fn get_one_or_many_names<'b>(
801        names: OneOrMany<&'b BorrowedName>,
802        alias: Option<&'b BorrowedName>,
803        capability_type: &str,
804        location: Option<Location>,
805        filepath: Option<&std::path::Path>,
806    ) -> Result<Vec<&'b BorrowedName>, Error> {
807        let names: Vec<&BorrowedName> = names.into_iter().collect();
808        if names.len() == 1 {
809            Ok(vec![alias_or_name(alias, &names[0])])
810        } else {
811            if alias.is_some() {
812                return Err(Error::validate_with_span(
813                    format!(
814                        "\"as\" can only be specified when one `{}` is supplied.",
815                        capability_type,
816                    ),
817                    location,
818                    filepath,
819                ));
820            }
821            Ok(names)
822        }
823    }
824
825    /// Returns the target paths as a `Vec` from a `use` declaration with `names` and `alias`.
826    fn get_one_or_many_svc_paths(
827        names: OneOrMany<&BorrowedName>,
828        alias: Option<&Path>,
829        capability_type: &str,
830    ) -> Result<Vec<Path>, Error> {
831        let names: Vec<_> = names.into_iter().collect();
832        match (names.len(), alias) {
833            (_, None) => {
834                Ok(names.into_iter().map(|n| format!("/svc/{}", n).parse().unwrap()).collect())
835            }
836            (1, Some(alias)) => Ok(vec![alias.clone()]),
837            (_, Some(_)) => {
838                return Err(Error::validate(format!(
839                    "\"path\" can only be specified when one `{}` is supplied.",
840                    capability_type,
841                )));
842            }
843        }
844    }
845
846    capability_ids_from_names!(services_from, CapabilityId::Service);
847    capability_ids_from_names!(protocols_from, CapabilityId::Protocol);
848    capability_ids_from_names!(directories_from, CapabilityId::Directory);
849    capability_ids_from_names!(storages_from, CapabilityId::Storage);
850    capability_ids_from_names!(runners_from, CapabilityId::Runner);
851    capability_ids_from_names!(resolvers_from, CapabilityId::Resolver);
852    capability_ids_from_names!(event_streams_from, CapabilityId::EventStream);
853    capability_ids_from_names!(dictionaries_from, CapabilityId::Dictionary);
854    capability_ids_from_names!(configurations_from, CapabilityId::Configuration);
855    capability_ids_from_paths!(used_services_from, CapabilityId::UsedService);
856    capability_ids_from_paths!(used_protocols_from, CapabilityId::UsedProtocol);
857    capability_ids_from_paths!(used_dictionaries_from, CapabilityId::UsedDictionary);
858}
859
860impl fmt::Display for CapabilityId<'_> {
861    /// Return the string ID of this clause.
862    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
863        match self {
864            CapabilityId::Service(n)
865            | CapabilityId::Storage(n)
866            | CapabilityId::Runner(n)
867            | CapabilityId::UsedRunner(n)
868            | CapabilityId::Resolver(n)
869            | CapabilityId::EventStream(n)
870            | CapabilityId::Configuration(n)
871            | CapabilityId::UsedConfiguration(n)
872            | CapabilityId::Dictionary(n) => write!(f, "{}", n),
873            CapabilityId::UsedService(p)
874            | CapabilityId::UsedProtocol(p)
875            | CapabilityId::UsedDirectory(p)
876            | CapabilityId::UsedStorage(p)
877            | CapabilityId::UsedEventStream(p)
878            | CapabilityId::UsedDictionary(p) => write!(f, "{}", p),
879            CapabilityId::Protocol(p) | CapabilityId::Directory(p) => write!(f, "{}", p),
880        }
881    }
882}