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