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