1use 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
15pub const TEST_RUNNER_NAME: &str = "test_runner";
19
20pub 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#[derive(Debug, Clone)]
37pub struct ComponentDeclBuilder {
38 result: ComponentDecl,
39}
40
41impl ComponentDeclBuilder {
42 pub fn new_empty_component() -> Self {
44 ComponentDeclBuilder { result: Default::default() }
45 }
46
47 pub fn new() -> Self {
50 Self::new_empty_component().program_runner(TEST_RUNNER_NAME)
51 }
52
53 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 pub fn child_default(self, name: &str) -> Self {
61 self.child(ChildBuilder::new().name(name))
62 }
63
64 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 pub fn collection_default(self, name: &str) -> Self {
72 self.collection(CollectionBuilder::new().name(name))
73 }
74
75 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 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 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 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 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 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 pub fn protocol_default(self, name: &str) -> Self {
131 self.capability(CapabilityBuilder::protocol().name(name))
132 }
133
134 pub fn dictionary_default(self, name: &str) -> Self {
136 self.capability(CapabilityBuilder::dictionary().name(name))
137 }
138
139 pub fn runner_default(self, name: &str) -> Self {
141 self.capability(CapabilityBuilder::runner().name(name))
142 }
143
144 pub fn resolver_default(self, name: &str) -> Self {
146 self.capability(CapabilityBuilder::resolver().name(name))
147 }
148
149 pub fn service_default(self, name: &str) -> Self {
151 self.capability(CapabilityBuilder::service().name(name))
152 }
153
154 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 pub fn config(mut self, config: cm_rust::ConfigDecl) -> Self {
162 self.result.config = Some(config);
163 self
164 }
165
166 pub fn build(self) -> ComponentDecl {
168 self.result
169 }
170}
171
172#[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 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#[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#[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#[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#[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#[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#[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}