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