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