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(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 => cm_rust::UseDecl::Config(cm_rust::UseConfigurationDecl {
864                source: self.source,
865                source_name: self.source_name.expect("name not set"),
866                target_name: self.target_name.expect("target name not set"),
867                availability: self.availability,
868                type_: self.config_type.expect("config_type not set"),
869                default: None,
870                source_dictionary: self.source_dictionary,
871            }),
872            CapabilityTypeName::Dictionary => {
873                cm_rust::UseDecl::Dictionary(cm_rust::UseDictionaryDecl {
874                    source: self.source,
875                    source_name: self.source_name.expect("name not set"),
876                    source_dictionary: self.source_dictionary,
877                    target_path: self.target_path.expect("path not set"),
878                    dependency_type: self.dependency_type,
879                    availability: self.availability,
880                })
881            }
882            CapabilityTypeName::Resolver => unreachable!(),
883        }
884    }
885}
886
887impl From<UseBuilder> for cm_rust::UseDecl {
888    fn from(builder: UseBuilder) -> Self {
889        builder.build()
890    }
891}
892
893/// A convenience builder for constructing [ExposeDecl]s.
894///
895/// To use, call the constructor matching their capability type ([ExposeBuilder::protocol],
896/// [ExposeBuilder::directory], etc.), and then call methods to set properties. When done,
897/// call [ExposeBuilder::build] (or [Into::into]) to generate the [ExposeDecl].
898#[derive(Debug)]
899pub struct ExposeBuilder {
900    source_name: Option<Name>,
901    type_: CapabilityTypeName,
902    source_dictionary: RelativePath,
903    source: Option<cm_rust::ExposeSource>,
904    target: cm_rust::ExposeTarget,
905    target_name: Option<Name>,
906    availability: cm_rust::Availability,
907    rights: Option<fio::Operations>,
908    subdir: RelativePath,
909}
910
911impl ExposeBuilder {
912    pub fn protocol() -> Self {
913        Self::new(CapabilityTypeName::Protocol)
914    }
915
916    pub fn service() -> Self {
917        Self::new(CapabilityTypeName::Service)
918    }
919
920    pub fn directory() -> Self {
921        Self::new(CapabilityTypeName::Directory)
922    }
923
924    pub fn runner() -> Self {
925        Self::new(CapabilityTypeName::Runner)
926    }
927
928    pub fn resolver() -> Self {
929        Self::new(CapabilityTypeName::Resolver)
930    }
931
932    pub fn dictionary() -> Self {
933        Self::new(CapabilityTypeName::Dictionary)
934    }
935
936    pub fn config() -> Self {
937        Self::new(CapabilityTypeName::Config)
938    }
939
940    fn new(type_: CapabilityTypeName) -> Self {
941        Self {
942            type_,
943            source: None,
944            target: cm_rust::ExposeTarget::Parent,
945            source_name: None,
946            target_name: None,
947            source_dictionary: Default::default(),
948            rights: None,
949            subdir: Default::default(),
950            availability: cm_rust::Availability::Required,
951        }
952    }
953
954    pub fn name(mut self, name: &str) -> Self {
955        self.source_name = Some(name.parse().unwrap());
956        if self.target_name.is_some() {
957            return self;
958        }
959        self.target_name = self.source_name.clone();
960        self
961    }
962
963    pub fn target_name(mut self, name: &str) -> Self {
964        self.target_name = Some(name.parse().unwrap());
965        self
966    }
967
968    pub fn from_dictionary(mut self, dictionary: &str) -> Self {
969        assert_matches!(
970            self.type_,
971            CapabilityTypeName::Service
972                | CapabilityTypeName::Protocol
973                | CapabilityTypeName::Directory
974                | CapabilityTypeName::Dictionary
975                | CapabilityTypeName::Runner
976                | CapabilityTypeName::Resolver
977                | CapabilityTypeName::Config
978        );
979        self.source_dictionary = dictionary.parse().unwrap();
980        self
981    }
982
983    pub fn source(mut self, source: cm_rust::ExposeSource) -> Self {
984        self.source = Some(source);
985        self
986    }
987
988    pub fn source_static_child(self, source: &str) -> Self {
989        self.source(cm_rust::ExposeSource::Child(source.parse().unwrap()))
990    }
991
992    pub fn target(mut self, target: cm_rust::ExposeTarget) -> Self {
993        self.target = target;
994        self
995    }
996
997    pub fn availability(mut self, availability: cm_rust::Availability) -> Self {
998        assert_matches!(
999            self.type_,
1000            CapabilityTypeName::Protocol
1001                | CapabilityTypeName::Service
1002                | CapabilityTypeName::Directory
1003                | CapabilityTypeName::Config
1004                | CapabilityTypeName::Dictionary
1005        );
1006        self.availability = availability;
1007        self
1008    }
1009
1010    pub fn rights(mut self, rights: fio::Operations) -> Self {
1011        assert_matches!(self.type_, CapabilityTypeName::Directory);
1012        self.rights = Some(rights);
1013        self
1014    }
1015
1016    pub fn subdir(mut self, subdir: &str) -> Self {
1017        assert_matches!(self.type_, CapabilityTypeName::Directory);
1018        self.subdir = subdir.parse().unwrap();
1019        self
1020    }
1021
1022    pub fn build(self) -> cm_rust::ExposeDecl {
1023        match self.type_ {
1024            CapabilityTypeName::Protocol => {
1025                cm_rust::ExposeDecl::Protocol(cm_rust::ExposeProtocolDecl {
1026                    source: self.source.expect("source not set"),
1027                    source_name: self.source_name.expect("name not set"),
1028                    source_dictionary: self.source_dictionary,
1029                    target: self.target,
1030                    target_name: self.target_name.expect("name not set"),
1031                    availability: self.availability,
1032                })
1033            }
1034            CapabilityTypeName::Service => {
1035                cm_rust::ExposeDecl::Service(cm_rust::ExposeServiceDecl {
1036                    source: self.source.expect("source not set"),
1037                    source_name: self.source_name.expect("name not set"),
1038                    source_dictionary: self.source_dictionary,
1039                    target: self.target,
1040                    target_name: self.target_name.expect("name not set"),
1041                    availability: self.availability,
1042                })
1043            }
1044            CapabilityTypeName::Directory => {
1045                cm_rust::ExposeDecl::Directory(cm_rust::ExposeDirectoryDecl {
1046                    source: self.source.expect("source not set"),
1047                    source_name: self.source_name.expect("name not set"),
1048                    source_dictionary: self.source_dictionary,
1049                    target: self.target,
1050                    target_name: self.target_name.expect("name not set"),
1051                    rights: self.rights,
1052                    subdir: self.subdir,
1053                    availability: self.availability,
1054                })
1055            }
1056            CapabilityTypeName::Runner => cm_rust::ExposeDecl::Runner(cm_rust::ExposeRunnerDecl {
1057                source: self.source.expect("source not set"),
1058                source_name: self.source_name.expect("name not set"),
1059                source_dictionary: self.source_dictionary,
1060                target: self.target,
1061                target_name: self.target_name.expect("name not set"),
1062            }),
1063            CapabilityTypeName::Resolver => {
1064                cm_rust::ExposeDecl::Resolver(cm_rust::ExposeResolverDecl {
1065                    source: self.source.expect("source not set"),
1066                    source_name: self.source_name.expect("name not set"),
1067                    source_dictionary: self.source_dictionary,
1068                    target: self.target,
1069                    target_name: self.target_name.expect("name not set"),
1070                })
1071            }
1072            CapabilityTypeName::Config => {
1073                cm_rust::ExposeDecl::Config(cm_rust::ExposeConfigurationDecl {
1074                    source: self.source.expect("source not set"),
1075                    source_name: self.source_name.expect("name not set"),
1076                    source_dictionary: self.source_dictionary,
1077                    target: self.target,
1078                    target_name: self.target_name.expect("name not set"),
1079                    availability: self.availability,
1080                })
1081            }
1082            CapabilityTypeName::Dictionary => {
1083                cm_rust::ExposeDecl::Dictionary(cm_rust::ExposeDictionaryDecl {
1084                    source: self.source.expect("source not set"),
1085                    source_name: self.source_name.expect("name not set"),
1086                    source_dictionary: self.source_dictionary,
1087                    target: self.target,
1088                    target_name: self.target_name.expect("name not set"),
1089                    availability: self.availability,
1090                })
1091            }
1092            CapabilityTypeName::EventStream | CapabilityTypeName::Storage => unreachable!(),
1093        }
1094    }
1095}
1096
1097impl From<ExposeBuilder> for cm_rust::ExposeDecl {
1098    fn from(builder: ExposeBuilder) -> Self {
1099        builder.build()
1100    }
1101}
1102
1103pub fn offer_source_static_child(name: &str) -> cm_rust::offer::OfferSource {
1104    cm_rust::offer::OfferSource::Child(cm_rust::ChildRef {
1105        name: name.parse().unwrap(),
1106        collection: None,
1107    })
1108}
1109
1110pub fn offer_target_static_child(name: &str) -> cm_rust::offer::OfferTarget {
1111    cm_rust::offer::OfferTarget::Child(cm_rust::ChildRef {
1112        name: name.parse().unwrap(),
1113        collection: None,
1114    })
1115}
1116
1117/// A convenience builder for constructing [OfferDecl]s.
1118///
1119/// To use, call the constructor matching their capability type ([OfferBuilder::protocol],
1120/// [OfferBuilder::directory], etc.), and then call methods to set properties. When done,
1121/// call [OfferBuilder::build] (or [Into::into]) to generate the [OfferDecl].
1122#[derive(Debug)]
1123pub struct OfferBuilder {
1124    source_name: Option<Name>,
1125    type_: CapabilityTypeName,
1126    source_dictionary: RelativePath,
1127    source: Option<cm_rust::offer::OfferSource>,
1128    target: Option<cm_rust::offer::OfferTarget>,
1129    target_name: Option<Name>,
1130    source_instance_filter: Option<Vec<Name>>,
1131    renamed_instances: Option<Vec<cm_rust::NameMapping>>,
1132    rights: Option<fio::Operations>,
1133    subdir: RelativePath,
1134    scope: Option<Box<[cm_rust::EventScope]>>,
1135    dependency_type: cm_rust::DependencyType,
1136    availability: cm_rust::Availability,
1137}
1138
1139impl OfferBuilder {
1140    pub fn protocol() -> Self {
1141        Self::new(CapabilityTypeName::Protocol)
1142    }
1143
1144    pub fn service() -> Self {
1145        Self::new(CapabilityTypeName::Service)
1146    }
1147
1148    pub fn directory() -> Self {
1149        Self::new(CapabilityTypeName::Directory)
1150    }
1151
1152    pub fn storage() -> Self {
1153        Self::new(CapabilityTypeName::Storage)
1154    }
1155
1156    pub fn runner() -> Self {
1157        Self::new(CapabilityTypeName::Runner)
1158    }
1159
1160    pub fn resolver() -> Self {
1161        Self::new(CapabilityTypeName::Resolver)
1162    }
1163
1164    pub fn dictionary() -> Self {
1165        Self::new(CapabilityTypeName::Dictionary)
1166    }
1167
1168    pub fn event_stream() -> Self {
1169        Self::new(CapabilityTypeName::EventStream)
1170    }
1171
1172    pub fn config() -> Self {
1173        Self::new(CapabilityTypeName::Config)
1174    }
1175
1176    fn new(type_: CapabilityTypeName) -> Self {
1177        Self {
1178            type_,
1179            source: None,
1180            target: None,
1181            source_name: None,
1182            target_name: None,
1183            source_dictionary: Default::default(),
1184            source_instance_filter: None,
1185            renamed_instances: None,
1186            rights: None,
1187            subdir: Default::default(),
1188            scope: None,
1189            dependency_type: cm_rust::DependencyType::Strong,
1190            availability: cm_rust::Availability::Required,
1191        }
1192    }
1193
1194    pub fn name(mut self, name: &str) -> Self {
1195        self.source_name = Some(name.parse().unwrap());
1196        if self.target_name.is_some() {
1197            return self;
1198        }
1199        self.target_name = self.source_name.clone();
1200        self
1201    }
1202
1203    pub fn target_name(mut self, name: &str) -> Self {
1204        self.target_name = Some(name.parse().unwrap());
1205        self
1206    }
1207
1208    pub fn from_dictionary(mut self, dictionary: &str) -> Self {
1209        assert_matches!(
1210            self.type_,
1211            CapabilityTypeName::Service
1212                | CapabilityTypeName::Protocol
1213                | CapabilityTypeName::Directory
1214                | CapabilityTypeName::Dictionary
1215                | CapabilityTypeName::Runner
1216                | CapabilityTypeName::Resolver
1217                | CapabilityTypeName::Config
1218        );
1219        self.source_dictionary = dictionary.parse().unwrap();
1220        self
1221    }
1222
1223    pub fn source(mut self, source: cm_rust::offer::OfferSource) -> Self {
1224        self.source = Some(source);
1225        self
1226    }
1227
1228    pub fn source_static_child(self, source: &str) -> Self {
1229        self.source(offer_source_static_child(source))
1230    }
1231
1232    pub fn target(mut self, target: cm_rust::offer::OfferTarget) -> Self {
1233        self.target = Some(target);
1234        self
1235    }
1236
1237    pub fn target_static_child(self, target: &str) -> Self {
1238        self.target(offer_target_static_child(target))
1239    }
1240
1241    pub fn target_capability(self, target: &str) -> Self {
1242        self.target(cm_rust::offer::OfferTarget::Capability(target.parse().unwrap()))
1243    }
1244
1245    pub fn availability(mut self, availability: cm_rust::Availability) -> Self {
1246        assert_matches!(
1247            self.type_,
1248            CapabilityTypeName::Protocol
1249                | CapabilityTypeName::Service
1250                | CapabilityTypeName::Directory
1251                | CapabilityTypeName::Storage
1252                | CapabilityTypeName::EventStream
1253                | CapabilityTypeName::Config
1254                | CapabilityTypeName::Dictionary
1255        );
1256        self.availability = availability;
1257        self
1258    }
1259
1260    pub fn dependency(mut self, dependency: cm_rust::DependencyType) -> Self {
1261        assert_matches!(
1262            self.type_,
1263            CapabilityTypeName::Protocol
1264                | CapabilityTypeName::Directory
1265                | CapabilityTypeName::Dictionary
1266        );
1267        self.dependency_type = dependency;
1268        self
1269    }
1270
1271    pub fn source_instance_filter<'a>(mut self, filter: impl IntoIterator<Item = &'a str>) -> Self {
1272        assert_matches!(self.type_, CapabilityTypeName::Service);
1273        self.source_instance_filter =
1274            Some(filter.into_iter().map(|s| s.parse().unwrap()).collect());
1275        self
1276    }
1277
1278    pub fn renamed_instances<'a, 'b>(
1279        mut self,
1280        mapping: impl IntoIterator<Item = (&'a str, &'b str)>,
1281    ) -> Self {
1282        assert_matches!(self.type_, CapabilityTypeName::Service);
1283        self.renamed_instances = Some(
1284            mapping
1285                .into_iter()
1286                .map(|(s, t)| cm_rust::NameMapping {
1287                    source_name: s.parse().unwrap(),
1288                    target_name: t.parse().unwrap(),
1289                })
1290                .collect(),
1291        );
1292        self
1293    }
1294
1295    pub fn rights(mut self, rights: fio::Operations) -> Self {
1296        assert_matches!(self.type_, CapabilityTypeName::Directory);
1297        self.rights = Some(rights);
1298        self
1299    }
1300
1301    pub fn subdir(mut self, subdir: &str) -> Self {
1302        assert_matches!(self.type_, CapabilityTypeName::Directory);
1303        self.subdir = subdir.parse().unwrap();
1304        self
1305    }
1306
1307    pub fn scope(mut self, scope: Vec<cm_rust::EventScope>) -> Self {
1308        assert_matches!(self.type_, CapabilityTypeName::EventStream);
1309        self.scope = Some(scope.into());
1310        self
1311    }
1312
1313    pub fn build(self) -> cm_rust::offer::OfferDecl {
1314        match self.type_ {
1315            CapabilityTypeName::Protocol => {
1316                cm_rust::offer::OfferDecl::Protocol(cm_rust::offer::OfferProtocolDecl {
1317                    source: self.source.expect("source not set"),
1318                    source_name: self.source_name.expect("name not set"),
1319                    source_dictionary: self.source_dictionary,
1320                    target: self.target.expect("target not set"),
1321                    target_name: self.target_name.expect("name not set"),
1322                    dependency_type: self.dependency_type,
1323                    availability: self.availability,
1324                })
1325            }
1326            CapabilityTypeName::Service => {
1327                cm_rust::offer::OfferDecl::Service(cm_rust::offer::OfferServiceDecl {
1328                    source: self.source.expect("source not set"),
1329                    source_name: self.source_name.expect("name not set"),
1330                    source_dictionary: self.source_dictionary,
1331                    target: self.target.expect("target is not set"),
1332                    target_name: self.target_name.expect("name not set"),
1333                    source_instance_filter: self.source_instance_filter.map(Into::into),
1334                    renamed_instances: self.renamed_instances.map(Into::into),
1335                    availability: self.availability,
1336                    dependency_type: Default::default(),
1337                })
1338            }
1339            CapabilityTypeName::Directory => {
1340                cm_rust::offer::OfferDecl::Directory(cm_rust::offer::OfferDirectoryDecl {
1341                    source: self.source.expect("source not set"),
1342                    source_name: self.source_name.expect("name not set"),
1343                    source_dictionary: self.source_dictionary,
1344                    target: self.target.expect("target is not set"),
1345                    target_name: self.target_name.expect("name not set"),
1346                    rights: self.rights,
1347                    subdir: self.subdir,
1348                    dependency_type: self.dependency_type,
1349                    availability: self.availability,
1350                })
1351            }
1352            CapabilityTypeName::Storage => {
1353                cm_rust::offer::OfferDecl::Storage(cm_rust::offer::OfferStorageDecl {
1354                    source: self.source.expect("source not set"),
1355                    source_name: self.source_name.expect("name not set"),
1356                    target: self.target.expect("target is not set"),
1357                    target_name: self.target_name.expect("name not set"),
1358                    availability: self.availability,
1359                })
1360            }
1361            CapabilityTypeName::EventStream => {
1362                cm_rust::offer::OfferDecl::EventStream(cm_rust::offer::OfferEventStreamDecl {
1363                    source: self.source.expect("source not set"),
1364                    source_name: self.source_name.expect("name not set"),
1365                    target: self.target.expect("target is not set"),
1366                    target_name: self.target_name.expect("name not set"),
1367                    availability: self.availability,
1368                    scope: self.scope,
1369                })
1370            }
1371            CapabilityTypeName::Runner => {
1372                cm_rust::offer::OfferDecl::Runner(cm_rust::offer::OfferRunnerDecl {
1373                    source: self.source.expect("source not set"),
1374                    source_name: self.source_name.expect("name not set"),
1375                    source_dictionary: self.source_dictionary,
1376                    target: self.target.expect("target is not set"),
1377                    target_name: self.target_name.expect("name not set"),
1378                })
1379            }
1380            CapabilityTypeName::Resolver => {
1381                cm_rust::offer::OfferDecl::Resolver(cm_rust::offer::OfferResolverDecl {
1382                    source: self.source.expect("source not set"),
1383                    source_name: self.source_name.expect("name not set"),
1384                    source_dictionary: self.source_dictionary,
1385                    target: self.target.expect("target is not set"),
1386                    target_name: self.target_name.expect("name not set"),
1387                })
1388            }
1389            CapabilityTypeName::Config => {
1390                cm_rust::offer::OfferDecl::Config(cm_rust::offer::OfferConfigurationDecl {
1391                    source: self.source.expect("source not set"),
1392                    source_name: self.source_name.expect("name not set"),
1393                    target: self.target.expect("target not set"),
1394                    target_name: self.target_name.expect("name not set"),
1395                    availability: self.availability,
1396                    source_dictionary: self.source_dictionary,
1397                })
1398            }
1399            CapabilityTypeName::Dictionary => {
1400                cm_rust::offer::OfferDecl::Dictionary(cm_rust::offer::OfferDictionaryDecl {
1401                    source: self.source.expect("source not set"),
1402                    source_name: self.source_name.expect("name not set"),
1403                    source_dictionary: self.source_dictionary,
1404                    target: self.target.expect("target not set"),
1405                    target_name: self.target_name.expect("name not set"),
1406                    dependency_type: self.dependency_type,
1407                    availability: self.availability,
1408                })
1409            }
1410        }
1411    }
1412}
1413
1414impl From<OfferBuilder> for cm_rust::offer::OfferDecl {
1415    fn from(builder: OfferBuilder) -> Self {
1416        builder.build()
1417    }
1418}