Skip to main content

cm_rust_testing/
lib.rs

1// Copyright 2021 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 anyhow::{Context, Error};
6use assert_matches::assert_matches;
7use cm_rust::{CapabilityTypeName, ComponentDecl, FidlIntoNative, push_box};
8use cm_types::{LongName, Name, Path, RelativePath, Url};
9use derivative::Derivative;
10use fidl_fuchsia_component_decl as fdecl;
11use fidl_fuchsia_data as fdata;
12use fidl_fuchsia_io as fio;
13use std::collections::BTreeMap;
14use std::path::PathBuf;
15use std::sync::Arc;
16
17/// Name of the test runner.
18///
19/// Several functions assume the existence of a runner with this name.
20pub const TEST_RUNNER_NAME: &str = "test_runner";
21
22/// Deserialize `object` into a cml::Document and then translate the result
23/// to ComponentDecl.
24pub fn new_decl_from_json(object: serde_json::Value) -> Result<ComponentDecl, Error> {
25    let json_str = serde_json::to_string(&object).context("failed to stringify json value")?;
26
27    let dummy_path = Arc::new(PathBuf::from("programmatic_manifest.cml"));
28
29    let doc = cml::types::document::parse_and_hydrate(dummy_path, &json_str)
30        .context("failed to parse and hydrate manifest")?;
31
32    let cm =
33        cml::compile(&doc, cml::CompileOptions::default()).context("failed to compile manifest")?;
34    Ok(cm.fidl_into_native())
35}
36
37/// Builder for constructing a ComponentDecl.
38#[derive(Debug, Clone)]
39pub struct ComponentDeclBuilder {
40    result: ComponentDecl,
41}
42
43impl ComponentDeclBuilder {
44    /// An empty ComponentDeclBuilder, with no program.
45    pub fn new_empty_component() -> Self {
46        ComponentDeclBuilder { result: Default::default() }
47    }
48
49    /// A ComponentDeclBuilder prefilled with a program and using a runner named "test_runner",
50    /// which we assume is offered to us.
51    pub fn new() -> Self {
52        Self::new_empty_component().program_runner(TEST_RUNNER_NAME)
53    }
54
55    /// Add a child element.
56    pub fn child(mut self, decl: impl Into<cm_rust::ChildDecl>) -> Self {
57        push_box(&mut self.result.children, decl.into());
58        self
59    }
60
61    /// Add a child with default properties.
62    pub fn child_default(self, name: &str) -> Self {
63        self.child(ChildBuilder::new().name(name))
64    }
65
66    // Add a collection element.
67    pub fn collection(mut self, decl: impl Into<cm_rust::CollectionDecl>) -> Self {
68        push_box(&mut self.result.collections, decl.into());
69        self
70    }
71
72    /// Add a collection with default properties.
73    pub fn collection_default(self, name: &str) -> Self {
74        self.collection(CollectionBuilder::new().name(name))
75    }
76
77    /// Add a "program" clause, using the given runner.
78    pub fn program_runner(self, runner: &str) -> Self {
79        assert!(self.result.program.is_none(), "tried to add program twice");
80        self.program(cm_rust::ProgramDecl {
81            runner: Some(runner.parse().unwrap()),
82            info: fdata::Dictionary { entries: Some(vec![]), ..Default::default() },
83        })
84    }
85
86    pub fn program(mut self, program: cm_rust::ProgramDecl) -> Self {
87        self.result.program = Some(program);
88        self
89    }
90
91    /// Add an offer decl.
92    pub fn offer(mut self, offer: impl Into<cm_rust::offer::OfferDecl>) -> Self {
93        push_box(&mut self.result.offers, offer.into());
94        self
95    }
96
97    /// Add an expose decl.
98    pub fn expose(mut self, expose: impl Into<cm_rust::ExposeDecl>) -> Self {
99        push_box(&mut self.result.exposes, expose.into());
100        self
101    }
102
103    /// Add a use decl.
104    pub fn use_(mut self, use_: impl Into<cm_rust::UseDecl>) -> Self {
105        let use_ = use_.into();
106        if let cm_rust::UseDecl::Runner(_) = &use_ {
107            assert!(
108                self.result.program.as_ref().and_then(|p| p.runner.as_ref()).is_none(),
109                "tried to add a use decl for a runner while program.runner is set"
110            );
111        }
112        push_box(&mut self.result.uses, use_);
113        self
114    }
115
116    // Add a use decl for fuchsia.component.Realm.
117    pub fn use_realm(self) -> Self {
118        self.use_(
119            UseBuilder::protocol()
120                .name("fuchsia.component.Realm")
121                .source(cm_rust::UseSource::Framework),
122        )
123    }
124
125    /// Add a capability declaration.
126    pub fn capability(mut self, capability: impl Into<cm_rust::CapabilityDecl>) -> Self {
127        push_box(&mut self.result.capabilities, capability.into());
128        self
129    }
130
131    /// Add a default protocol declaration.
132    pub fn protocol_default(self, name: &str) -> Self {
133        self.capability(CapabilityBuilder::protocol().name(name))
134    }
135
136    /// Add a default dictionary declaration.
137    pub fn dictionary_default(self, name: &str) -> Self {
138        self.capability(CapabilityBuilder::dictionary().name(name))
139    }
140
141    /// Add a default runner declaration.
142    pub fn runner_default(self, name: &str) -> Self {
143        self.capability(CapabilityBuilder::runner().name(name))
144    }
145
146    /// Add a default resolver declaration.
147    pub fn resolver_default(self, name: &str) -> Self {
148        self.capability(CapabilityBuilder::resolver().name(name))
149    }
150
151    /// Add a default service declaration.
152    pub fn service_default(self, name: &str) -> Self {
153        self.capability(CapabilityBuilder::service().name(name))
154    }
155
156    /// Add an environment declaration.
157    pub fn environment(mut self, environment: impl Into<cm_rust::EnvironmentDecl>) -> Self {
158        push_box(&mut self.result.environments, environment.into());
159        self
160    }
161
162    /// Add a config declaration.
163    pub fn config(mut self, config: cm_rust::ConfigDecl) -> Self {
164        self.result.config = Some(config);
165        self
166    }
167
168    /// Generate the final ComponentDecl.
169    pub fn build(self) -> ComponentDecl {
170        self.result
171    }
172}
173
174/// A convenience builder for constructing ChildDecls.
175#[derive(Debug, Derivative)]
176#[derivative(Default)]
177pub struct ChildBuilder {
178    name: Option<LongName>,
179    url: Option<Url>,
180    #[derivative(Default(value = "fdecl::StartupMode::Lazy"))]
181    startup: fdecl::StartupMode,
182    on_terminate: Option<fdecl::OnTerminate>,
183    environment: Option<Name>,
184}
185
186impl ChildBuilder {
187    pub fn new() -> Self {
188        Self::default()
189    }
190
191    /// Defaults url to `"test:///{name}"`.
192    pub fn name(mut self, name: &str) -> Self {
193        self.name = Some(name.parse().unwrap());
194        if self.url.is_none() {
195            self.url = Some(format!("test:///{name}").parse().unwrap());
196        }
197        self
198    }
199
200    pub fn url(mut self, url: &str) -> Self {
201        self.url = Some(url.parse().unwrap());
202        self
203    }
204
205    pub fn startup(mut self, startup: fdecl::StartupMode) -> Self {
206        self.startup = startup;
207        self
208    }
209
210    pub fn eager(self) -> Self {
211        self.startup(fdecl::StartupMode::Eager)
212    }
213
214    pub fn on_terminate(mut self, on_terminate: fdecl::OnTerminate) -> Self {
215        self.on_terminate = Some(on_terminate);
216        self
217    }
218
219    pub fn environment(mut self, environment: &str) -> Self {
220        self.environment = Some(environment.parse().unwrap());
221        self
222    }
223
224    pub fn build(self) -> cm_rust::ChildDecl {
225        cm_rust::ChildDecl {
226            name: self.name.expect("name not set"),
227            url: self.url.expect("url not set"),
228            startup: self.startup,
229            on_terminate: self.on_terminate,
230            environment: self.environment,
231            config_overrides: None,
232        }
233    }
234}
235
236impl From<ChildBuilder> for cm_rust::ChildDecl {
237    fn from(builder: ChildBuilder) -> Self {
238        builder.build()
239    }
240}
241
242/// A convenience builder for constructing CollectionDecls.
243#[derive(Debug, Derivative)]
244#[derivative(Default)]
245pub struct CollectionBuilder {
246    name: Option<Name>,
247    #[derivative(Default(value = "fdecl::Durability::Transient"))]
248    durability: fdecl::Durability,
249    environment: Option<Name>,
250    #[derivative(Default(value = "cm_types::AllowedOffers::StaticOnly"))]
251    allowed_offers: cm_types::AllowedOffers,
252    allow_long_names: bool,
253    persistent_storage: Option<bool>,
254}
255
256impl CollectionBuilder {
257    pub fn new() -> Self {
258        Self::default()
259    }
260
261    pub fn name(mut self, name: &str) -> Self {
262        self.name = Some(name.parse().unwrap());
263        self
264    }
265
266    pub fn durability(mut self, durability: fdecl::Durability) -> Self {
267        self.durability = durability;
268        self
269    }
270
271    pub fn environment(mut self, environment: &str) -> Self {
272        self.environment = Some(environment.parse().unwrap());
273        self
274    }
275
276    pub fn allowed_offers(mut self, allowed_offers: cm_types::AllowedOffers) -> Self {
277        self.allowed_offers = allowed_offers;
278        self
279    }
280
281    pub fn allow_long_names(mut self) -> Self {
282        self.allow_long_names = true;
283        self
284    }
285
286    pub fn persistent_storage(mut self, persistent_storage: bool) -> Self {
287        self.persistent_storage = Some(persistent_storage);
288        self
289    }
290
291    pub fn build(self) -> cm_rust::CollectionDecl {
292        cm_rust::CollectionDecl {
293            name: self.name.expect("name not set"),
294            durability: self.durability,
295            environment: self.environment,
296            allowed_offers: self.allowed_offers,
297            allow_long_names: self.allow_long_names,
298            persistent_storage: self.persistent_storage,
299        }
300    }
301}
302
303impl From<CollectionBuilder> for cm_rust::CollectionDecl {
304    fn from(builder: CollectionBuilder) -> Self {
305        builder.build()
306    }
307}
308
309/// A convenience builder for constructing EnvironmentDecls.
310#[derive(Debug, Derivative)]
311#[derivative(Default)]
312pub struct EnvironmentBuilder {
313    name: Option<Name>,
314    #[derivative(Default(value = "fdecl::EnvironmentExtends::Realm"))]
315    extends: fdecl::EnvironmentExtends,
316    runners: Vec<cm_rust::RunnerRegistration>,
317    resolvers: Vec<cm_rust::ResolverRegistration>,
318    debug_capabilities: Vec<cm_rust::DebugRegistration>,
319    stop_timeout_ms: Option<u32>,
320}
321
322impl EnvironmentBuilder {
323    pub fn new() -> Self {
324        Self::default()
325    }
326
327    pub fn name(mut self, name: &str) -> Self {
328        self.name = Some(name.parse().unwrap());
329        self
330    }
331
332    pub fn extends(mut self, extends: fdecl::EnvironmentExtends) -> Self {
333        self.extends = extends;
334        self
335    }
336
337    pub fn runner(mut self, runner: cm_rust::RunnerRegistration) -> Self {
338        self.runners.push(runner);
339        self
340    }
341
342    pub fn resolver(mut self, resolver: cm_rust::ResolverRegistration) -> Self {
343        self.resolvers.push(resolver);
344        self
345    }
346
347    pub fn debug(mut self, debug: cm_rust::DebugRegistration) -> Self {
348        self.debug_capabilities.push(debug);
349        self
350    }
351
352    pub fn stop_timeout(mut self, timeout_ms: u32) -> Self {
353        self.stop_timeout_ms = Some(timeout_ms);
354        self
355    }
356
357    pub fn build(self) -> cm_rust::EnvironmentDecl {
358        cm_rust::EnvironmentDecl {
359            name: self.name.expect("name not set"),
360            extends: self.extends,
361            runners: self.runners.into(),
362            resolvers: self.resolvers.into(),
363            debug_capabilities: self.debug_capabilities.into(),
364            stop_timeout_ms: self.stop_timeout_ms,
365        }
366    }
367}
368
369impl From<EnvironmentBuilder> for cm_rust::EnvironmentDecl {
370    fn from(builder: EnvironmentBuilder) -> Self {
371        builder.build()
372    }
373}
374
375/// A convenience builder for constructing [CapabilityDecl]s.
376///
377/// To use, call the constructor matching their capability type ([CapabilityBuilder::protocol],
378/// [CapabilityBuilder::directory], etc., and then call methods to set properties. When done,
379/// call [CapabilityBuilder::build] (or [Into::into]) to generate the [CapabilityDecl].
380#[derive(Debug)]
381pub struct CapabilityBuilder {
382    name: Option<Name>,
383    type_: CapabilityTypeName,
384    path: Option<Path>,
385    dictionary_source: Option<cm_rust::DictionarySource>,
386    source_dictionary: Option<RelativePath>,
387    rights: fio::Operations,
388    subdir: RelativePath,
389    backing_dir: Option<Name>,
390    storage_source: Option<cm_rust::StorageDirectorySource>,
391    storage_id: fdecl::StorageId,
392    value: Option<cm_rust::ConfigValue>,
393    delivery: cm_rust::DeliveryType,
394}
395
396impl CapabilityBuilder {
397    pub fn protocol() -> Self {
398        Self::new(CapabilityTypeName::Protocol)
399    }
400
401    pub fn service() -> Self {
402        Self::new(CapabilityTypeName::Service)
403    }
404
405    pub fn directory() -> Self {
406        Self::new(CapabilityTypeName::Directory)
407    }
408
409    pub fn storage() -> Self {
410        Self::new(CapabilityTypeName::Storage)
411    }
412
413    pub fn runner() -> Self {
414        Self::new(CapabilityTypeName::Runner)
415    }
416
417    pub fn resolver() -> Self {
418        Self::new(CapabilityTypeName::Resolver)
419    }
420
421    pub fn dictionary() -> Self {
422        Self::new(CapabilityTypeName::Dictionary)
423    }
424
425    pub fn config() -> Self {
426        Self::new(CapabilityTypeName::Config)
427    }
428
429    pub fn name(mut self, name: &str) -> Self {
430        self.name = Some(name.parse().unwrap());
431        if self.path.is_some() {
432            return self;
433        }
434        match self.type_ {
435            CapabilityTypeName::Protocol => {
436                self.path = Some(format!("/svc/{name}").parse().unwrap());
437            }
438            CapabilityTypeName::Service => {
439                self.path = Some(format!("/svc/{name}").parse().unwrap());
440            }
441            CapabilityTypeName::Runner => {
442                self.path = Some("/svc/fuchsia.component.runner.ComponentRunner".parse().unwrap());
443            }
444            CapabilityTypeName::Resolver => {
445                self.path = Some("/svc/fuchsia.component.resolution.Resolver".parse().unwrap());
446            }
447            CapabilityTypeName::Dictionary
448            | CapabilityTypeName::Storage
449            | CapabilityTypeName::Config
450            | CapabilityTypeName::Directory => {}
451            CapabilityTypeName::EventStream => unreachable!(),
452        }
453        self
454    }
455
456    fn new(type_: CapabilityTypeName) -> Self {
457        Self {
458            type_,
459            name: None,
460            path: None,
461            dictionary_source: None,
462            source_dictionary: None,
463            rights: fio::R_STAR_DIR,
464            subdir: Default::default(),
465            backing_dir: None,
466            storage_source: None,
467            storage_id: fdecl::StorageId::StaticInstanceIdOrMoniker,
468            value: None,
469            delivery: Default::default(),
470        }
471    }
472
473    pub fn path(mut self, path: &str) -> Self {
474        assert_matches!(
475            self.type_,
476            CapabilityTypeName::Protocol
477                | CapabilityTypeName::Service
478                | CapabilityTypeName::Directory
479                | CapabilityTypeName::Dictionary
480                | CapabilityTypeName::Runner
481                | CapabilityTypeName::Resolver
482        );
483        if self.type_ == CapabilityTypeName::Dictionary {
484            if self.dictionary_source.is_some() || self.source_dictionary.is_some() {
485                panic!("Dictionary path is incompatible with source_dictionary");
486            }
487        }
488        self.path = Some(path.parse().unwrap());
489        self
490    }
491
492    pub fn rights(mut self, rights: fio::Operations) -> Self {
493        assert_matches!(self.type_, CapabilityTypeName::Directory);
494        self.rights = rights;
495        self
496    }
497
498    pub fn backing_dir(mut self, backing_dir: &str) -> Self {
499        assert_matches!(self.type_, CapabilityTypeName::Storage);
500        self.backing_dir = Some(backing_dir.parse().unwrap());
501        self
502    }
503
504    pub fn value(mut self, value: cm_rust::ConfigValue) -> Self {
505        assert_matches!(self.type_, CapabilityTypeName::Config);
506        self.value = Some(value);
507        self
508    }
509
510    pub fn source(mut self, source: cm_rust::StorageDirectorySource) -> Self {
511        assert_matches!(self.type_, CapabilityTypeName::Storage);
512        self.storage_source = Some(source);
513        self
514    }
515
516    pub fn subdir(mut self, subdir: &str) -> Self {
517        assert_matches!(self.type_, CapabilityTypeName::Storage);
518        self.subdir = subdir.parse().unwrap();
519        self
520    }
521
522    pub fn storage_id(mut self, storage_id: fdecl::StorageId) -> Self {
523        assert_matches!(self.type_, CapabilityTypeName::Storage);
524        self.storage_id = storage_id;
525        self
526    }
527
528    pub fn delivery(mut self, delivery: cm_rust::DeliveryType) -> Self {
529        assert_matches!(self.type_, CapabilityTypeName::Protocol);
530        self.delivery = delivery;
531        self
532    }
533
534    pub fn build(self) -> cm_rust::CapabilityDecl {
535        match self.type_ {
536            CapabilityTypeName::Protocol => {
537                cm_rust::CapabilityDecl::Protocol(cm_rust::ProtocolDecl {
538                    name: self.name.expect("name not set"),
539                    source_path: Some(self.path.expect("path not set")),
540                    delivery: self.delivery,
541                })
542            }
543            CapabilityTypeName::Service => cm_rust::CapabilityDecl::Service(cm_rust::ServiceDecl {
544                name: self.name.expect("name not set"),
545                source_path: Some(self.path.expect("path not set")),
546            }),
547            CapabilityTypeName::Runner => cm_rust::CapabilityDecl::Runner(cm_rust::RunnerDecl {
548                name: self.name.expect("name not set"),
549                source_path: Some(self.path.expect("path not set")),
550            }),
551            CapabilityTypeName::Resolver => {
552                cm_rust::CapabilityDecl::Resolver(cm_rust::ResolverDecl {
553                    name: self.name.expect("name not set"),
554                    source_path: Some(self.path.expect("path not set")),
555                })
556            }
557            CapabilityTypeName::Dictionary => {
558                cm_rust::CapabilityDecl::Dictionary(cm_rust::DictionaryDecl {
559                    name: self.name.expect("name not set"),
560                    source_path: self.path,
561                })
562            }
563            CapabilityTypeName::Storage => cm_rust::CapabilityDecl::Storage(cm_rust::StorageDecl {
564                name: self.name.expect("name not set"),
565                backing_dir: self.backing_dir.expect("backing_dir not set"),
566                source: self.storage_source.expect("source not set"),
567                subdir: self.subdir,
568                storage_id: self.storage_id,
569            }),
570            CapabilityTypeName::Directory => {
571                cm_rust::CapabilityDecl::Directory(cm_rust::DirectoryDecl {
572                    name: self.name.expect("name not set"),
573                    source_path: Some(self.path.expect("path not set")),
574                    rights: self.rights,
575                })
576            }
577            CapabilityTypeName::Config => {
578                cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
579                    name: self.name.expect("name not set"),
580                    value: self.value.expect("value not set"),
581                })
582            }
583            CapabilityTypeName::EventStream => unreachable!(),
584        }
585    }
586}
587
588impl From<CapabilityBuilder> for cm_rust::CapabilityDecl {
589    fn from(builder: CapabilityBuilder) -> Self {
590        builder.build()
591    }
592}
593
594/// A convenience builder for constructing [UseDecl]s.
595///
596/// To use, call the constructor matching their capability type ([UseBuilder::protocol],
597/// [UseBuilder::directory], etc.), and then call methods to set properties. When done,
598/// call [UseBuilder::build] (or [Into::into]) to generate the [UseDecl].
599#[derive(Debug)]
600pub struct UseBuilder {
601    source_name: Option<Name>,
602    type_: CapabilityTypeName,
603    source_dictionary: RelativePath,
604    source: cm_rust::UseSource,
605    target_name: Option<Name>,
606    target_path: Option<Path>,
607    #[cfg(fuchsia_api_level_at_least = "29")]
608    numbered_handle: Option<cm_types::HandleType>,
609    dependency_type: cm_rust::DependencyType,
610    availability: cm_rust::Availability,
611    rights: fio::Operations,
612    subdir: RelativePath,
613    scope: Option<Box<[cm_rust::EventScope]>>,
614    filter: Option<BTreeMap<String, cm_rust::DictionaryValue>>,
615    config_type: Option<cm_rust::ConfigValueType>,
616}
617
618impl UseBuilder {
619    pub fn protocol() -> Self {
620        Self::new(CapabilityTypeName::Protocol)
621    }
622
623    pub fn service() -> Self {
624        Self::new(CapabilityTypeName::Service)
625    }
626
627    pub fn directory() -> Self {
628        Self::new(CapabilityTypeName::Directory)
629    }
630
631    pub fn storage() -> Self {
632        Self::new(CapabilityTypeName::Storage)
633    }
634
635    pub fn runner() -> Self {
636        Self::new(CapabilityTypeName::Runner)
637    }
638
639    pub fn event_stream() -> Self {
640        Self::new(CapabilityTypeName::EventStream)
641    }
642
643    pub fn config() -> Self {
644        Self::new(CapabilityTypeName::Config)
645    }
646
647    pub fn dictionary() -> Self {
648        Self::new(CapabilityTypeName::Dictionary)
649    }
650
651    fn new(type_: CapabilityTypeName) -> Self {
652        Self {
653            type_,
654            source: cm_rust::UseSource::Parent,
655            source_name: None,
656            target_name: None,
657            target_path: None,
658            #[cfg(fuchsia_api_level_at_least = "29")]
659            numbered_handle: None,
660            source_dictionary: Default::default(),
661            rights: fio::R_STAR_DIR,
662            subdir: Default::default(),
663            dependency_type: cm_rust::DependencyType::Strong,
664            availability: cm_rust::Availability::Required,
665            scope: None,
666            filter: None,
667            config_type: None,
668        }
669    }
670
671    pub fn config_type(mut self, type_: cm_rust::ConfigValueType) -> Self {
672        self.config_type = Some(type_);
673        self
674    }
675
676    pub fn name(mut self, name: &str) -> Self {
677        self.source_name = Some(name.parse().unwrap());
678        if self.target_path.is_some() || self.target_name.is_some() {
679            return self;
680        }
681        match self.type_ {
682            CapabilityTypeName::Protocol | CapabilityTypeName::Service => {
683                self.target_path = Some(format!("/svc/{name}").parse().unwrap());
684            }
685            CapabilityTypeName::EventStream => {
686                self.target_path = Some("/svc/fuchsia.component.EventStream".parse().unwrap());
687            }
688            CapabilityTypeName::Runner | CapabilityTypeName::Config => {
689                self.target_name = self.source_name.clone();
690            }
691            CapabilityTypeName::Dictionary
692            | CapabilityTypeName::Storage
693            | CapabilityTypeName::Directory => {}
694            CapabilityTypeName::Resolver => unreachable!(),
695        }
696        self
697    }
698
699    pub fn path(mut self, path: &str) -> Self {
700        assert_matches!(
701            self.type_,
702            CapabilityTypeName::Protocol
703                | CapabilityTypeName::Service
704                | CapabilityTypeName::Directory
705                | CapabilityTypeName::EventStream
706                | CapabilityTypeName::Storage
707                | CapabilityTypeName::Dictionary
708        );
709        self.target_path = Some(path.parse().unwrap());
710        self
711    }
712
713    #[cfg(fuchsia_api_level_at_least = "29")]
714    pub fn numbered_handle(mut self, h: impl Into<cm_types::HandleType>) -> Self {
715        assert_matches!(self.type_, CapabilityTypeName::Protocol);
716        self.numbered_handle = Some(h.into());
717        self.target_path = None;
718        self
719    }
720
721    pub fn target_name(mut self, name: &str) -> Self {
722        assert_matches!(self.type_, CapabilityTypeName::Runner | CapabilityTypeName::Config);
723        self.target_name = Some(name.parse().unwrap());
724        self
725    }
726
727    pub fn from_dictionary(mut self, dictionary: &str) -> Self {
728        assert_matches!(
729            self.type_,
730            CapabilityTypeName::Service
731                | CapabilityTypeName::Protocol
732                | CapabilityTypeName::Directory
733                | CapabilityTypeName::Runner
734                | CapabilityTypeName::Config
735        );
736        self.source_dictionary = dictionary.parse().unwrap();
737        self
738    }
739
740    pub fn source(mut self, source: cm_rust::UseSource) -> Self {
741        assert_matches!(self.type_, t if t != CapabilityTypeName::Storage);
742        self.source = source;
743        self
744    }
745
746    pub fn source_static_child(self, source: &str) -> Self {
747        self.source(cm_rust::UseSource::Child(source.parse().unwrap()))
748    }
749
750    pub fn availability(mut self, availability: cm_rust::Availability) -> Self {
751        assert_matches!(
752            self.type_,
753            CapabilityTypeName::Protocol
754                | CapabilityTypeName::Service
755                | CapabilityTypeName::Directory
756                | CapabilityTypeName::EventStream
757                | CapabilityTypeName::Storage
758                | CapabilityTypeName::Config
759        );
760        self.availability = availability;
761        self
762    }
763
764    pub fn dependency(mut self, dependency: cm_rust::DependencyType) -> Self {
765        assert_matches!(
766            self.type_,
767            CapabilityTypeName::Protocol
768                | CapabilityTypeName::Service
769                | CapabilityTypeName::Directory
770        );
771        self.dependency_type = dependency;
772        self
773    }
774
775    pub fn rights(mut self, rights: fio::Operations) -> Self {
776        assert_matches!(self.type_, CapabilityTypeName::Directory);
777        self.rights = rights;
778        self
779    }
780
781    pub fn subdir(mut self, subdir: &str) -> Self {
782        assert_matches!(self.type_, CapabilityTypeName::Directory);
783        self.subdir = subdir.parse().unwrap();
784        self
785    }
786
787    pub fn scope(mut self, scope: Vec<cm_rust::EventScope>) -> Self {
788        assert_matches!(self.type_, CapabilityTypeName::EventStream);
789        self.scope = Some(scope.into());
790        self
791    }
792
793    pub fn filter(mut self, filter: BTreeMap<String, cm_rust::DictionaryValue>) -> Self {
794        assert_matches!(self.type_, CapabilityTypeName::EventStream);
795        self.filter = Some(filter);
796        self
797    }
798
799    pub fn build(self) -> cm_rust::UseDecl {
800        match self.type_ {
801            CapabilityTypeName::Protocol => {
802                #[cfg(not(fuchsia_api_level_at_least = "29"))]
803                let has_numbered_handle = false;
804                #[cfg(fuchsia_api_level_at_least = "29")]
805                let has_numbered_handle = self.numbered_handle.is_some();
806                if !has_numbered_handle && self.target_path.is_none() {
807                    panic!("path not set");
808                }
809                if has_numbered_handle && self.target_path.is_some() {
810                    panic!("path and numbered_handle both set");
811                }
812                cm_rust::UseDecl::Protocol(cm_rust::UseProtocolDecl {
813                    source: self.source,
814                    source_name: self.source_name.expect("name not set"),
815                    source_dictionary: self.source_dictionary,
816                    target_path: self.target_path,
817                    #[cfg(fuchsia_api_level_at_least = "29")]
818                    numbered_handle: self.numbered_handle,
819                    dependency_type: self.dependency_type,
820                    availability: self.availability,
821                })
822            }
823            CapabilityTypeName::Service => cm_rust::UseDecl::Service(cm_rust::UseServiceDecl {
824                source: self.source,
825                source_name: self.source_name.expect("name not set"),
826                source_dictionary: self.source_dictionary,
827                target_path: self.target_path.expect("path not set"),
828                dependency_type: self.dependency_type,
829                availability: self.availability,
830            }),
831            CapabilityTypeName::Directory => {
832                cm_rust::UseDecl::Directory(cm_rust::UseDirectoryDecl {
833                    source: self.source,
834                    source_name: self.source_name.expect("name not set"),
835                    source_dictionary: self.source_dictionary,
836                    target_path: self.target_path.expect("path not set"),
837                    rights: self.rights,
838                    subdir: self.subdir,
839                    dependency_type: self.dependency_type,
840                    availability: self.availability,
841                })
842            }
843            CapabilityTypeName::Storage => cm_rust::UseDecl::Storage(cm_rust::UseStorageDecl {
844                source_name: self.source_name.expect("name not set"),
845                target_path: self.target_path.expect("path not set"),
846                availability: self.availability,
847            }),
848            CapabilityTypeName::EventStream => {
849                cm_rust::UseDecl::EventStream(Box::new(cm_rust::UseEventStreamDecl {
850                    source: self.source,
851                    source_name: self.source_name.expect("name not set"),
852                    target_path: self.target_path.expect("path not set"),
853                    availability: self.availability,
854                    scope: self.scope,
855                    filter: self.filter,
856                }))
857            }
858            CapabilityTypeName::Runner => cm_rust::UseDecl::Runner(cm_rust::UseRunnerDecl {
859                source: self.source,
860                source_name: self.source_name.expect("name not set"),
861                source_dictionary: self.source_dictionary,
862            }),
863            CapabilityTypeName::Config => {
864                cm_rust::UseDecl::Config(Box::new(cm_rust::UseConfigurationDecl {
865                    source: self.source,
866                    source_name: self.source_name.expect("name not set"),
867                    target_name: self.target_name.expect("target name not set"),
868                    availability: self.availability,
869                    type_: self.config_type.expect("config_type not set"),
870                    default: None,
871                    source_dictionary: self.source_dictionary,
872                }))
873            }
874            CapabilityTypeName::Dictionary => {
875                cm_rust::UseDecl::Dictionary(cm_rust::UseDictionaryDecl {
876                    source: self.source,
877                    source_name: self.source_name.expect("name not set"),
878                    source_dictionary: self.source_dictionary,
879                    target_path: self.target_path.expect("path not set"),
880                    dependency_type: self.dependency_type,
881                    availability: self.availability,
882                })
883            }
884            CapabilityTypeName::Resolver => unreachable!(),
885        }
886    }
887}
888
889impl From<UseBuilder> for cm_rust::UseDecl {
890    fn from(builder: UseBuilder) -> Self {
891        builder.build()
892    }
893}
894
895/// A convenience builder for constructing [ExposeDecl]s.
896///
897/// To use, call the constructor matching their capability type ([ExposeBuilder::protocol],
898/// [ExposeBuilder::directory], etc.), and then call methods to set properties. When done,
899/// call [ExposeBuilder::build] (or [Into::into]) to generate the [ExposeDecl].
900#[derive(Debug)]
901pub struct ExposeBuilder {
902    source_name: Option<Name>,
903    type_: CapabilityTypeName,
904    source_dictionary: RelativePath,
905    source: Option<cm_rust::ExposeSource>,
906    target: cm_rust::ExposeTarget,
907    target_name: Option<Name>,
908    availability: cm_rust::Availability,
909    rights: Option<fio::Operations>,
910    subdir: RelativePath,
911}
912
913impl ExposeBuilder {
914    pub fn protocol() -> Self {
915        Self::new(CapabilityTypeName::Protocol)
916    }
917
918    pub fn service() -> Self {
919        Self::new(CapabilityTypeName::Service)
920    }
921
922    pub fn directory() -> Self {
923        Self::new(CapabilityTypeName::Directory)
924    }
925
926    pub fn runner() -> Self {
927        Self::new(CapabilityTypeName::Runner)
928    }
929
930    pub fn resolver() -> Self {
931        Self::new(CapabilityTypeName::Resolver)
932    }
933
934    pub fn dictionary() -> Self {
935        Self::new(CapabilityTypeName::Dictionary)
936    }
937
938    pub fn config() -> Self {
939        Self::new(CapabilityTypeName::Config)
940    }
941
942    fn new(type_: CapabilityTypeName) -> Self {
943        Self {
944            type_,
945            source: None,
946            target: cm_rust::ExposeTarget::Parent,
947            source_name: None,
948            target_name: None,
949            source_dictionary: Default::default(),
950            rights: None,
951            subdir: Default::default(),
952            availability: cm_rust::Availability::Required,
953        }
954    }
955
956    pub fn name(mut self, name: &str) -> Self {
957        self.source_name = Some(name.parse().unwrap());
958        if self.target_name.is_some() {
959            return self;
960        }
961        self.target_name = self.source_name.clone();
962        self
963    }
964
965    pub fn target_name(mut self, name: &str) -> Self {
966        self.target_name = Some(name.parse().unwrap());
967        self
968    }
969
970    pub fn from_dictionary(mut self, dictionary: &str) -> Self {
971        assert_matches!(
972            self.type_,
973            CapabilityTypeName::Service
974                | CapabilityTypeName::Protocol
975                | CapabilityTypeName::Directory
976                | CapabilityTypeName::Dictionary
977                | CapabilityTypeName::Runner
978                | CapabilityTypeName::Resolver
979                | CapabilityTypeName::Config
980        );
981        self.source_dictionary = dictionary.parse().unwrap();
982        self
983    }
984
985    pub fn source(mut self, source: cm_rust::ExposeSource) -> Self {
986        self.source = Some(source);
987        self
988    }
989
990    pub fn source_static_child(self, source: &str) -> Self {
991        self.source(cm_rust::ExposeSource::Child(source.parse().unwrap()))
992    }
993
994    pub fn target(mut self, target: cm_rust::ExposeTarget) -> Self {
995        self.target = target;
996        self
997    }
998
999    pub fn availability(mut self, availability: cm_rust::Availability) -> Self {
1000        assert_matches!(
1001            self.type_,
1002            CapabilityTypeName::Protocol
1003                | CapabilityTypeName::Service
1004                | CapabilityTypeName::Directory
1005                | CapabilityTypeName::Config
1006                | CapabilityTypeName::Dictionary
1007        );
1008        self.availability = availability;
1009        self
1010    }
1011
1012    pub fn rights(mut self, rights: fio::Operations) -> Self {
1013        assert_matches!(self.type_, CapabilityTypeName::Directory);
1014        self.rights = Some(rights);
1015        self
1016    }
1017
1018    pub fn subdir(mut self, subdir: &str) -> Self {
1019        assert_matches!(self.type_, CapabilityTypeName::Directory);
1020        self.subdir = subdir.parse().unwrap();
1021        self
1022    }
1023
1024    pub fn build(self) -> cm_rust::ExposeDecl {
1025        match self.type_ {
1026            CapabilityTypeName::Protocol => {
1027                cm_rust::ExposeDecl::Protocol(cm_rust::ExposeProtocolDecl {
1028                    source: self.source.expect("source not set"),
1029                    source_name: self.source_name.expect("name not set"),
1030                    source_dictionary: self.source_dictionary,
1031                    target: self.target,
1032                    target_name: self.target_name.expect("name not set"),
1033                    availability: self.availability,
1034                })
1035            }
1036            CapabilityTypeName::Service => {
1037                cm_rust::ExposeDecl::Service(cm_rust::ExposeServiceDecl {
1038                    source: self.source.expect("source not set"),
1039                    source_name: self.source_name.expect("name not set"),
1040                    source_dictionary: self.source_dictionary,
1041                    target: self.target,
1042                    target_name: self.target_name.expect("name not set"),
1043                    availability: self.availability,
1044                })
1045            }
1046            CapabilityTypeName::Directory => {
1047                cm_rust::ExposeDecl::Directory(cm_rust::ExposeDirectoryDecl {
1048                    source: self.source.expect("source not set"),
1049                    source_name: self.source_name.expect("name not set"),
1050                    source_dictionary: self.source_dictionary,
1051                    target: self.target,
1052                    target_name: self.target_name.expect("name not set"),
1053                    rights: self.rights,
1054                    subdir: self.subdir,
1055                    availability: self.availability,
1056                })
1057            }
1058            CapabilityTypeName::Runner => cm_rust::ExposeDecl::Runner(cm_rust::ExposeRunnerDecl {
1059                source: self.source.expect("source not set"),
1060                source_name: self.source_name.expect("name not set"),
1061                source_dictionary: self.source_dictionary,
1062                target: self.target,
1063                target_name: self.target_name.expect("name not set"),
1064            }),
1065            CapabilityTypeName::Resolver => {
1066                cm_rust::ExposeDecl::Resolver(cm_rust::ExposeResolverDecl {
1067                    source: self.source.expect("source not set"),
1068                    source_name: self.source_name.expect("name not set"),
1069                    source_dictionary: self.source_dictionary,
1070                    target: self.target,
1071                    target_name: self.target_name.expect("name not set"),
1072                })
1073            }
1074            CapabilityTypeName::Config => {
1075                cm_rust::ExposeDecl::Config(cm_rust::ExposeConfigurationDecl {
1076                    source: self.source.expect("source not set"),
1077                    source_name: self.source_name.expect("name not set"),
1078                    source_dictionary: self.source_dictionary,
1079                    target: self.target,
1080                    target_name: self.target_name.expect("name not set"),
1081                    availability: self.availability,
1082                })
1083            }
1084            CapabilityTypeName::Dictionary => {
1085                cm_rust::ExposeDecl::Dictionary(cm_rust::ExposeDictionaryDecl {
1086                    source: self.source.expect("source not set"),
1087                    source_name: self.source_name.expect("name not set"),
1088                    source_dictionary: self.source_dictionary,
1089                    target: self.target,
1090                    target_name: self.target_name.expect("name not set"),
1091                    availability: self.availability,
1092                })
1093            }
1094            CapabilityTypeName::EventStream | CapabilityTypeName::Storage => unreachable!(),
1095        }
1096    }
1097}
1098
1099impl From<ExposeBuilder> for cm_rust::ExposeDecl {
1100    fn from(builder: ExposeBuilder) -> Self {
1101        builder.build()
1102    }
1103}
1104
1105pub fn offer_source_static_child(name: &str) -> cm_rust::offer::OfferSource {
1106    cm_rust::offer::OfferSource::Child(cm_rust::ChildRef {
1107        name: name.parse().unwrap(),
1108        collection: None,
1109    })
1110}
1111
1112pub fn offer_target_static_child(name: &str) -> cm_rust::offer::OfferTarget {
1113    cm_rust::offer::OfferTarget::Child(cm_rust::ChildRef {
1114        name: name.parse().unwrap(),
1115        collection: None,
1116    })
1117}
1118
1119/// A convenience builder for constructing [OfferDecl]s.
1120///
1121/// To use, call the constructor matching their capability type ([OfferBuilder::protocol],
1122/// [OfferBuilder::directory], etc.), and then call methods to set properties. When done,
1123/// call [OfferBuilder::build] (or [Into::into]) to generate the [OfferDecl].
1124#[derive(Debug)]
1125pub struct OfferBuilder {
1126    source_name: Option<Name>,
1127    type_: CapabilityTypeName,
1128    source_dictionary: RelativePath,
1129    source: Option<cm_rust::offer::OfferSource>,
1130    target: Option<cm_rust::offer::OfferTarget>,
1131    target_name: Option<Name>,
1132    source_instance_filter: Option<Vec<Name>>,
1133    renamed_instances: Option<Vec<cm_rust::NameMapping>>,
1134    rights: Option<fio::Operations>,
1135    subdir: RelativePath,
1136    scope: Option<Box<[cm_rust::EventScope]>>,
1137    dependency_type: cm_rust::DependencyType,
1138    availability: cm_rust::Availability,
1139}
1140
1141impl OfferBuilder {
1142    pub fn protocol() -> Self {
1143        Self::new(CapabilityTypeName::Protocol)
1144    }
1145
1146    pub fn service() -> Self {
1147        Self::new(CapabilityTypeName::Service)
1148    }
1149
1150    pub fn directory() -> Self {
1151        Self::new(CapabilityTypeName::Directory)
1152    }
1153
1154    pub fn storage() -> Self {
1155        Self::new(CapabilityTypeName::Storage)
1156    }
1157
1158    pub fn runner() -> Self {
1159        Self::new(CapabilityTypeName::Runner)
1160    }
1161
1162    pub fn resolver() -> Self {
1163        Self::new(CapabilityTypeName::Resolver)
1164    }
1165
1166    pub fn dictionary() -> Self {
1167        Self::new(CapabilityTypeName::Dictionary)
1168    }
1169
1170    pub fn event_stream() -> Self {
1171        Self::new(CapabilityTypeName::EventStream)
1172    }
1173
1174    pub fn config() -> Self {
1175        Self::new(CapabilityTypeName::Config)
1176    }
1177
1178    fn new(type_: CapabilityTypeName) -> Self {
1179        Self {
1180            type_,
1181            source: None,
1182            target: None,
1183            source_name: None,
1184            target_name: None,
1185            source_dictionary: Default::default(),
1186            source_instance_filter: None,
1187            renamed_instances: None,
1188            rights: None,
1189            subdir: Default::default(),
1190            scope: None,
1191            dependency_type: cm_rust::DependencyType::Strong,
1192            availability: cm_rust::Availability::Required,
1193        }
1194    }
1195
1196    pub fn name(mut self, name: &str) -> Self {
1197        self.source_name = Some(name.parse().unwrap());
1198        if self.target_name.is_some() {
1199            return self;
1200        }
1201        self.target_name = self.source_name.clone();
1202        self
1203    }
1204
1205    pub fn target_name(mut self, name: &str) -> Self {
1206        self.target_name = Some(name.parse().unwrap());
1207        self
1208    }
1209
1210    pub fn from_dictionary(mut self, dictionary: &str) -> Self {
1211        assert_matches!(
1212            self.type_,
1213            CapabilityTypeName::Service
1214                | CapabilityTypeName::Protocol
1215                | CapabilityTypeName::Directory
1216                | CapabilityTypeName::Dictionary
1217                | CapabilityTypeName::Runner
1218                | CapabilityTypeName::Resolver
1219                | CapabilityTypeName::Config
1220        );
1221        self.source_dictionary = dictionary.parse().unwrap();
1222        self
1223    }
1224
1225    pub fn source(mut self, source: cm_rust::offer::OfferSource) -> Self {
1226        self.source = Some(source);
1227        self
1228    }
1229
1230    pub fn source_static_child(self, source: &str) -> Self {
1231        self.source(offer_source_static_child(source))
1232    }
1233
1234    pub fn target(mut self, target: cm_rust::offer::OfferTarget) -> Self {
1235        self.target = Some(target);
1236        self
1237    }
1238
1239    pub fn target_static_child(self, target: &str) -> Self {
1240        self.target(offer_target_static_child(target))
1241    }
1242
1243    pub fn target_capability(self, target: &str) -> Self {
1244        self.target(cm_rust::offer::OfferTarget::Capability(target.parse().unwrap()))
1245    }
1246
1247    pub fn availability(mut self, availability: cm_rust::Availability) -> Self {
1248        assert_matches!(
1249            self.type_,
1250            CapabilityTypeName::Protocol
1251                | CapabilityTypeName::Service
1252                | CapabilityTypeName::Directory
1253                | CapabilityTypeName::Storage
1254                | CapabilityTypeName::EventStream
1255                | CapabilityTypeName::Config
1256                | CapabilityTypeName::Dictionary
1257        );
1258        self.availability = availability;
1259        self
1260    }
1261
1262    pub fn dependency(mut self, dependency: cm_rust::DependencyType) -> Self {
1263        assert_matches!(
1264            self.type_,
1265            CapabilityTypeName::Protocol
1266                | CapabilityTypeName::Directory
1267                | CapabilityTypeName::Dictionary
1268        );
1269        self.dependency_type = dependency;
1270        self
1271    }
1272
1273    pub fn source_instance_filter<'a>(mut self, filter: impl IntoIterator<Item = &'a str>) -> Self {
1274        assert_matches!(self.type_, CapabilityTypeName::Service);
1275        self.source_instance_filter =
1276            Some(filter.into_iter().map(|s| s.parse().unwrap()).collect());
1277        self
1278    }
1279
1280    pub fn renamed_instances<'a, 'b>(
1281        mut self,
1282        mapping: impl IntoIterator<Item = (&'a str, &'b str)>,
1283    ) -> Self {
1284        assert_matches!(self.type_, CapabilityTypeName::Service);
1285        self.renamed_instances = Some(
1286            mapping
1287                .into_iter()
1288                .map(|(s, t)| cm_rust::NameMapping {
1289                    source_name: s.parse().unwrap(),
1290                    target_name: t.parse().unwrap(),
1291                })
1292                .collect(),
1293        );
1294        self
1295    }
1296
1297    pub fn rights(mut self, rights: fio::Operations) -> Self {
1298        assert_matches!(self.type_, CapabilityTypeName::Directory);
1299        self.rights = Some(rights);
1300        self
1301    }
1302
1303    pub fn subdir(mut self, subdir: &str) -> Self {
1304        assert_matches!(self.type_, CapabilityTypeName::Directory);
1305        self.subdir = subdir.parse().unwrap();
1306        self
1307    }
1308
1309    pub fn scope(mut self, scope: Vec<cm_rust::EventScope>) -> Self {
1310        assert_matches!(self.type_, CapabilityTypeName::EventStream);
1311        self.scope = Some(scope.into());
1312        self
1313    }
1314
1315    pub fn build(self) -> cm_rust::offer::OfferDecl {
1316        match self.type_ {
1317            CapabilityTypeName::Protocol => {
1318                cm_rust::offer::OfferDecl::Protocol(cm_rust::offer::OfferProtocolDecl {
1319                    source: self.source.expect("source not set"),
1320                    source_name: self.source_name.expect("name not set"),
1321                    source_dictionary: self.source_dictionary,
1322                    target: self.target.expect("target not set"),
1323                    target_name: self.target_name.expect("name not set"),
1324                    dependency_type: self.dependency_type,
1325                    availability: self.availability,
1326                })
1327            }
1328            CapabilityTypeName::Service => {
1329                cm_rust::offer::OfferDecl::Service(Box::new(cm_rust::offer::OfferServiceDecl {
1330                    source: self.source.expect("source not set"),
1331                    source_name: self.source_name.expect("name not set"),
1332                    source_dictionary: self.source_dictionary,
1333                    target: self.target.expect("target is not set"),
1334                    target_name: self.target_name.expect("name not set"),
1335                    source_instance_filter: self.source_instance_filter.map(Into::into),
1336                    renamed_instances: self.renamed_instances.map(Into::into),
1337                    availability: self.availability,
1338                    dependency_type: Default::default(),
1339                }))
1340            }
1341            CapabilityTypeName::Directory => {
1342                cm_rust::offer::OfferDecl::Directory(Box::new(cm_rust::offer::OfferDirectoryDecl {
1343                    source: self.source.expect("source not set"),
1344                    source_name: self.source_name.expect("name not set"),
1345                    source_dictionary: self.source_dictionary,
1346                    target: self.target.expect("target is not set"),
1347                    target_name: self.target_name.expect("name not set"),
1348                    rights: self.rights,
1349                    subdir: self.subdir,
1350                    dependency_type: self.dependency_type,
1351                    availability: self.availability,
1352                }))
1353            }
1354            CapabilityTypeName::Storage => {
1355                cm_rust::offer::OfferDecl::Storage(cm_rust::offer::OfferStorageDecl {
1356                    source: self.source.expect("source not set"),
1357                    source_name: self.source_name.expect("name not set"),
1358                    target: self.target.expect("target is not set"),
1359                    target_name: self.target_name.expect("name not set"),
1360                    availability: self.availability,
1361                })
1362            }
1363            CapabilityTypeName::EventStream => cm_rust::offer::OfferDecl::EventStream(Box::new(
1364                cm_rust::offer::OfferEventStreamDecl {
1365                    source: self.source.expect("source not set"),
1366                    source_name: self.source_name.expect("name not set"),
1367                    target: self.target.expect("target is not set"),
1368                    target_name: self.target_name.expect("name not set"),
1369                    availability: self.availability,
1370                    scope: self.scope,
1371                },
1372            )),
1373            CapabilityTypeName::Runner => {
1374                cm_rust::offer::OfferDecl::Runner(cm_rust::offer::OfferRunnerDecl {
1375                    source: self.source.expect("source not set"),
1376                    source_name: self.source_name.expect("name not set"),
1377                    source_dictionary: self.source_dictionary,
1378                    target: self.target.expect("target is not set"),
1379                    target_name: self.target_name.expect("name not set"),
1380                })
1381            }
1382            CapabilityTypeName::Resolver => {
1383                cm_rust::offer::OfferDecl::Resolver(cm_rust::offer::OfferResolverDecl {
1384                    source: self.source.expect("source not set"),
1385                    source_name: self.source_name.expect("name not set"),
1386                    source_dictionary: self.source_dictionary,
1387                    target: self.target.expect("target is not set"),
1388                    target_name: self.target_name.expect("name not set"),
1389                })
1390            }
1391            CapabilityTypeName::Config => {
1392                cm_rust::offer::OfferDecl::Config(cm_rust::offer::OfferConfigurationDecl {
1393                    source: self.source.expect("source not set"),
1394                    source_name: self.source_name.expect("name not set"),
1395                    target: self.target.expect("target not set"),
1396                    target_name: self.target_name.expect("name not set"),
1397                    availability: self.availability,
1398                    source_dictionary: self.source_dictionary,
1399                })
1400            }
1401            CapabilityTypeName::Dictionary => {
1402                cm_rust::offer::OfferDecl::Dictionary(cm_rust::offer::OfferDictionaryDecl {
1403                    source: self.source.expect("source not set"),
1404                    source_name: self.source_name.expect("name not set"),
1405                    source_dictionary: self.source_dictionary,
1406                    target: self.target.expect("target not set"),
1407                    target_name: self.target_name.expect("name not set"),
1408                    dependency_type: self.dependency_type,
1409                    availability: self.availability,
1410                })
1411            }
1412        }
1413    }
1414}
1415
1416impl From<OfferBuilder> for cm_rust::offer::OfferDecl {
1417    fn from(builder: OfferBuilder) -> Self {
1418        builder.build()
1419    }
1420}