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