1use anyhow::{Context, Error};
6use assert_matches::assert_matches;
7use cm_rust::{CapabilityTypeName, ComponentDecl, FidlIntoNative};
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 self.result.children.push(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 self.result.collections.push(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 self.result.offers.push(offer.into());
84 self
85 }
86
87 pub fn expose(mut self, expose: impl Into<cm_rust::ExposeDecl>) -> Self {
89 self.result.exposes.push(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 self.result.uses.push(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 self.result.capabilities.push(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 self.result.environments.push(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,
352 resolvers: self.resolvers,
353 debug_capabilities: self.debug_capabilities,
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 dependency_type: cm_rust::DependencyType,
598 availability: cm_rust::Availability,
599 rights: fio::Operations,
600 subdir: RelativePath,
601 scope: Option<Vec<cm_rust::EventScope>>,
602 filter: Option<BTreeMap<String, cm_rust::DictionaryValue>>,
603 config_type: Option<cm_rust::ConfigValueType>,
604}
605
606impl UseBuilder {
607 pub fn protocol() -> Self {
608 Self::new(CapabilityTypeName::Protocol)
609 }
610
611 pub fn service() -> Self {
612 Self::new(CapabilityTypeName::Service)
613 }
614
615 pub fn directory() -> Self {
616 Self::new(CapabilityTypeName::Directory)
617 }
618
619 pub fn storage() -> Self {
620 Self::new(CapabilityTypeName::Storage)
621 }
622
623 pub fn runner() -> Self {
624 Self::new(CapabilityTypeName::Runner)
625 }
626
627 pub fn event_stream() -> Self {
628 Self::new(CapabilityTypeName::EventStream)
629 }
630
631 pub fn config() -> Self {
632 Self::new(CapabilityTypeName::Config)
633 }
634
635 fn new(type_: CapabilityTypeName) -> Self {
636 Self {
637 type_,
638 source: cm_rust::UseSource::Parent,
639 source_name: None,
640 target_name: None,
641 target_path: None,
642 source_dictionary: Default::default(),
643 rights: fio::R_STAR_DIR,
644 subdir: Default::default(),
645 dependency_type: cm_rust::DependencyType::Strong,
646 availability: cm_rust::Availability::Required,
647 scope: None,
648 filter: None,
649 config_type: None,
650 }
651 }
652
653 pub fn config_type(mut self, type_: cm_rust::ConfigValueType) -> Self {
654 self.config_type = Some(type_);
655 self
656 }
657
658 pub fn name(mut self, name: &str) -> Self {
659 self.source_name = Some(name.parse().unwrap());
660 if self.target_path.is_some() || self.target_name.is_some() {
661 return self;
662 }
663 match self.type_ {
664 CapabilityTypeName::Protocol | CapabilityTypeName::Service => {
665 self.target_path = Some(format!("/svc/{name}").parse().unwrap());
666 }
667 CapabilityTypeName::EventStream => {
668 self.target_path = Some("/svc/fuchsia.component.EventStream".parse().unwrap());
669 }
670 CapabilityTypeName::Runner | CapabilityTypeName::Config => {
671 self.target_name = self.source_name.clone();
672 }
673 CapabilityTypeName::Storage | CapabilityTypeName::Directory => {}
674 CapabilityTypeName::Dictionary | CapabilityTypeName::Resolver => unreachable!(),
675 }
676 self
677 }
678
679 pub fn path(mut self, path: &str) -> Self {
680 assert_matches!(
681 self.type_,
682 CapabilityTypeName::Protocol
683 | CapabilityTypeName::Service
684 | CapabilityTypeName::Directory
685 | CapabilityTypeName::EventStream
686 | CapabilityTypeName::Storage
687 );
688 self.target_path = Some(path.parse().unwrap());
689 self
690 }
691
692 pub fn target_name(mut self, name: &str) -> Self {
693 assert_matches!(self.type_, CapabilityTypeName::Runner | CapabilityTypeName::Config);
694 self.target_name = Some(name.parse().unwrap());
695 self
696 }
697
698 pub fn from_dictionary(mut self, dictionary: &str) -> Self {
699 assert_matches!(
700 self.type_,
701 CapabilityTypeName::Service
702 | CapabilityTypeName::Protocol
703 | CapabilityTypeName::Directory
704 | CapabilityTypeName::Runner
705 );
706 self.source_dictionary = dictionary.parse().unwrap();
707 self
708 }
709
710 pub fn source(mut self, source: cm_rust::UseSource) -> Self {
711 assert_matches!(self.type_, t if t != CapabilityTypeName::Storage);
712 self.source = source;
713 self
714 }
715
716 pub fn source_static_child(self, source: &str) -> Self {
717 self.source(cm_rust::UseSource::Child(source.parse().unwrap()))
718 }
719
720 pub fn availability(mut self, availability: cm_rust::Availability) -> Self {
721 assert_matches!(
722 self.type_,
723 CapabilityTypeName::Protocol
724 | CapabilityTypeName::Service
725 | CapabilityTypeName::Directory
726 | CapabilityTypeName::EventStream
727 | CapabilityTypeName::Storage
728 | CapabilityTypeName::Config
729 );
730 self.availability = availability;
731 self
732 }
733
734 pub fn dependency(mut self, dependency: cm_rust::DependencyType) -> Self {
735 assert_matches!(
736 self.type_,
737 CapabilityTypeName::Protocol
738 | CapabilityTypeName::Service
739 | CapabilityTypeName::Directory
740 );
741 self.dependency_type = dependency;
742 self
743 }
744
745 pub fn rights(mut self, rights: fio::Operations) -> Self {
746 assert_matches!(self.type_, CapabilityTypeName::Directory);
747 self.rights = rights;
748 self
749 }
750
751 pub fn subdir(mut self, subdir: &str) -> Self {
752 assert_matches!(self.type_, CapabilityTypeName::Directory);
753 self.subdir = subdir.parse().unwrap();
754 self
755 }
756
757 pub fn scope(mut self, scope: Vec<cm_rust::EventScope>) -> Self {
758 assert_matches!(self.type_, CapabilityTypeName::EventStream);
759 self.scope = Some(scope);
760 self
761 }
762
763 pub fn filter(mut self, filter: BTreeMap<String, cm_rust::DictionaryValue>) -> Self {
764 assert_matches!(self.type_, CapabilityTypeName::EventStream);
765 self.filter = Some(filter);
766 self
767 }
768
769 pub fn build(self) -> cm_rust::UseDecl {
770 match self.type_ {
771 CapabilityTypeName::Protocol => cm_rust::UseDecl::Protocol(cm_rust::UseProtocolDecl {
772 source: self.source,
773 source_name: self.source_name.expect("name not set"),
774 source_dictionary: self.source_dictionary,
775 target_path: self.target_path.expect("path not set"),
776 dependency_type: self.dependency_type,
777 availability: self.availability,
778 }),
779 CapabilityTypeName::Service => cm_rust::UseDecl::Service(cm_rust::UseServiceDecl {
780 source: self.source,
781 source_name: self.source_name.expect("name not set"),
782 source_dictionary: self.source_dictionary,
783 target_path: self.target_path.expect("path not set"),
784 dependency_type: self.dependency_type,
785 availability: self.availability,
786 }),
787 CapabilityTypeName::Directory => {
788 cm_rust::UseDecl::Directory(cm_rust::UseDirectoryDecl {
789 source: self.source,
790 source_name: self.source_name.expect("name not set"),
791 source_dictionary: self.source_dictionary,
792 target_path: self.target_path.expect("path not set"),
793 rights: self.rights,
794 subdir: self.subdir,
795 dependency_type: self.dependency_type,
796 availability: self.availability,
797 })
798 }
799 CapabilityTypeName::Storage => cm_rust::UseDecl::Storage(cm_rust::UseStorageDecl {
800 source_name: self.source_name.expect("name not set"),
801 target_path: self.target_path.expect("path not set"),
802 availability: self.availability,
803 }),
804 CapabilityTypeName::EventStream => {
805 cm_rust::UseDecl::EventStream(cm_rust::UseEventStreamDecl {
806 source: self.source,
807 source_name: self.source_name.expect("name not set"),
808 target_path: self.target_path.expect("path not set"),
809 availability: self.availability,
810 scope: self.scope,
811 filter: self.filter,
812 })
813 }
814 CapabilityTypeName::Runner => cm_rust::UseDecl::Runner(cm_rust::UseRunnerDecl {
815 source: self.source,
816 source_name: self.source_name.expect("name not set"),
817 source_dictionary: self.source_dictionary,
818 }),
819 CapabilityTypeName::Config => cm_rust::UseDecl::Config(cm_rust::UseConfigurationDecl {
820 source: self.source,
821 source_name: self.source_name.expect("name not set"),
822 target_name: self.target_name.expect("target name not set"),
823 availability: self.availability,
824 type_: self.config_type.expect("config_type not set"),
825 default: None,
826 source_dictionary: self.source_dictionary,
827 }),
828 CapabilityTypeName::Resolver | CapabilityTypeName::Dictionary => unreachable!(),
829 }
830 }
831}
832
833impl From<UseBuilder> for cm_rust::UseDecl {
834 fn from(builder: UseBuilder) -> Self {
835 builder.build()
836 }
837}
838
839#[derive(Debug)]
845pub struct ExposeBuilder {
846 source_name: Option<Name>,
847 type_: CapabilityTypeName,
848 source_dictionary: RelativePath,
849 source: Option<cm_rust::ExposeSource>,
850 target: cm_rust::ExposeTarget,
851 target_name: Option<Name>,
852 availability: cm_rust::Availability,
853 rights: Option<fio::Operations>,
854 subdir: RelativePath,
855}
856
857impl ExposeBuilder {
858 pub fn protocol() -> Self {
859 Self::new(CapabilityTypeName::Protocol)
860 }
861
862 pub fn service() -> Self {
863 Self::new(CapabilityTypeName::Service)
864 }
865
866 pub fn directory() -> Self {
867 Self::new(CapabilityTypeName::Directory)
868 }
869
870 pub fn runner() -> Self {
871 Self::new(CapabilityTypeName::Runner)
872 }
873
874 pub fn resolver() -> Self {
875 Self::new(CapabilityTypeName::Resolver)
876 }
877
878 pub fn dictionary() -> Self {
879 Self::new(CapabilityTypeName::Dictionary)
880 }
881
882 pub fn config() -> Self {
883 Self::new(CapabilityTypeName::Config)
884 }
885
886 fn new(type_: CapabilityTypeName) -> Self {
887 Self {
888 type_,
889 source: None,
890 target: cm_rust::ExposeTarget::Parent,
891 source_name: None,
892 target_name: None,
893 source_dictionary: Default::default(),
894 rights: None,
895 subdir: Default::default(),
896 availability: cm_rust::Availability::Required,
897 }
898 }
899
900 pub fn name(mut self, name: &str) -> Self {
901 self.source_name = Some(name.parse().unwrap());
902 if self.target_name.is_some() {
903 return self;
904 }
905 self.target_name = self.source_name.clone();
906 self
907 }
908
909 pub fn target_name(mut self, name: &str) -> Self {
910 self.target_name = Some(name.parse().unwrap());
911 self
912 }
913
914 pub fn from_dictionary(mut self, dictionary: &str) -> Self {
915 assert_matches!(
916 self.type_,
917 CapabilityTypeName::Service
918 | CapabilityTypeName::Protocol
919 | CapabilityTypeName::Directory
920 | CapabilityTypeName::Dictionary
921 | CapabilityTypeName::Runner
922 | CapabilityTypeName::Resolver
923 );
924 self.source_dictionary = dictionary.parse().unwrap();
925 self
926 }
927
928 pub fn source(mut self, source: cm_rust::ExposeSource) -> Self {
929 self.source = Some(source);
930 self
931 }
932
933 pub fn source_static_child(self, source: &str) -> Self {
934 self.source(cm_rust::ExposeSource::Child(source.parse().unwrap()))
935 }
936
937 pub fn target(mut self, target: cm_rust::ExposeTarget) -> Self {
938 self.target = target;
939 self
940 }
941
942 pub fn availability(mut self, availability: cm_rust::Availability) -> Self {
943 assert_matches!(
944 self.type_,
945 CapabilityTypeName::Protocol
946 | CapabilityTypeName::Service
947 | CapabilityTypeName::Directory
948 | CapabilityTypeName::Config
949 | CapabilityTypeName::Dictionary
950 );
951 self.availability = availability;
952 self
953 }
954
955 pub fn rights(mut self, rights: fio::Operations) -> Self {
956 assert_matches!(self.type_, CapabilityTypeName::Directory);
957 self.rights = Some(rights);
958 self
959 }
960
961 pub fn subdir(mut self, subdir: &str) -> Self {
962 assert_matches!(self.type_, CapabilityTypeName::Directory);
963 self.subdir = subdir.parse().unwrap();
964 self
965 }
966
967 pub fn build(self) -> cm_rust::ExposeDecl {
968 match self.type_ {
969 CapabilityTypeName::Protocol => {
970 cm_rust::ExposeDecl::Protocol(cm_rust::ExposeProtocolDecl {
971 source: self.source.expect("source not set"),
972 source_name: self.source_name.expect("name not set"),
973 source_dictionary: self.source_dictionary,
974 target: self.target,
975 target_name: self.target_name.expect("name not set"),
976 availability: self.availability,
977 })
978 }
979 CapabilityTypeName::Service => {
980 cm_rust::ExposeDecl::Service(cm_rust::ExposeServiceDecl {
981 source: self.source.expect("source not set"),
982 source_name: self.source_name.expect("name not set"),
983 source_dictionary: self.source_dictionary,
984 target: self.target,
985 target_name: self.target_name.expect("name not set"),
986 availability: self.availability,
987 })
988 }
989 CapabilityTypeName::Directory => {
990 cm_rust::ExposeDecl::Directory(cm_rust::ExposeDirectoryDecl {
991 source: self.source.expect("source not set"),
992 source_name: self.source_name.expect("name not set"),
993 source_dictionary: self.source_dictionary,
994 target: self.target,
995 target_name: self.target_name.expect("name not set"),
996 rights: self.rights,
997 subdir: self.subdir,
998 availability: self.availability,
999 })
1000 }
1001 CapabilityTypeName::Runner => cm_rust::ExposeDecl::Runner(cm_rust::ExposeRunnerDecl {
1002 source: self.source.expect("source not set"),
1003 source_name: self.source_name.expect("name not set"),
1004 source_dictionary: self.source_dictionary,
1005 target: self.target,
1006 target_name: self.target_name.expect("name not set"),
1007 }),
1008 CapabilityTypeName::Resolver => {
1009 cm_rust::ExposeDecl::Resolver(cm_rust::ExposeResolverDecl {
1010 source: self.source.expect("source not set"),
1011 source_name: self.source_name.expect("name not set"),
1012 source_dictionary: self.source_dictionary,
1013 target: self.target,
1014 target_name: self.target_name.expect("name not set"),
1015 })
1016 }
1017 CapabilityTypeName::Config => {
1018 cm_rust::ExposeDecl::Config(cm_rust::ExposeConfigurationDecl {
1019 source: self.source.expect("source not set"),
1020 source_name: self.source_name.expect("name not set"),
1021 source_dictionary: self.source_dictionary,
1022 target: self.target,
1023 target_name: self.target_name.expect("name not set"),
1024 availability: self.availability,
1025 })
1026 }
1027 CapabilityTypeName::Dictionary => {
1028 cm_rust::ExposeDecl::Dictionary(cm_rust::ExposeDictionaryDecl {
1029 source: self.source.expect("source not set"),
1030 source_name: self.source_name.expect("name not set"),
1031 source_dictionary: self.source_dictionary,
1032 target: self.target,
1033 target_name: self.target_name.expect("name not set"),
1034 availability: self.availability,
1035 })
1036 }
1037 CapabilityTypeName::EventStream | CapabilityTypeName::Storage => unreachable!(),
1038 }
1039 }
1040}
1041
1042impl From<ExposeBuilder> for cm_rust::ExposeDecl {
1043 fn from(builder: ExposeBuilder) -> Self {
1044 builder.build()
1045 }
1046}
1047
1048pub fn offer_source_static_child(name: &str) -> cm_rust::OfferSource {
1049 cm_rust::OfferSource::Child(cm_rust::ChildRef { name: name.parse().unwrap(), collection: None })
1050}
1051
1052pub fn offer_target_static_child(name: &str) -> cm_rust::OfferTarget {
1053 cm_rust::OfferTarget::Child(cm_rust::ChildRef { name: name.parse().unwrap(), collection: None })
1054}
1055
1056#[derive(Debug)]
1062pub struct OfferBuilder {
1063 source_name: Option<Name>,
1064 type_: CapabilityTypeName,
1065 source_dictionary: RelativePath,
1066 source: Option<cm_rust::OfferSource>,
1067 target: Option<cm_rust::OfferTarget>,
1068 target_name: Option<Name>,
1069 source_instance_filter: Option<Vec<Name>>,
1070 renamed_instances: Option<Vec<cm_rust::NameMapping>>,
1071 rights: Option<fio::Operations>,
1072 subdir: RelativePath,
1073 scope: Option<Vec<cm_rust::EventScope>>,
1074 dependency_type: cm_rust::DependencyType,
1075 availability: cm_rust::Availability,
1076}
1077
1078impl OfferBuilder {
1079 pub fn protocol() -> Self {
1080 Self::new(CapabilityTypeName::Protocol)
1081 }
1082
1083 pub fn service() -> Self {
1084 Self::new(CapabilityTypeName::Service)
1085 }
1086
1087 pub fn directory() -> Self {
1088 Self::new(CapabilityTypeName::Directory)
1089 }
1090
1091 pub fn storage() -> Self {
1092 Self::new(CapabilityTypeName::Storage)
1093 }
1094
1095 pub fn runner() -> Self {
1096 Self::new(CapabilityTypeName::Runner)
1097 }
1098
1099 pub fn resolver() -> Self {
1100 Self::new(CapabilityTypeName::Resolver)
1101 }
1102
1103 pub fn dictionary() -> Self {
1104 Self::new(CapabilityTypeName::Dictionary)
1105 }
1106
1107 pub fn event_stream() -> Self {
1108 Self::new(CapabilityTypeName::EventStream)
1109 }
1110
1111 pub fn config() -> Self {
1112 Self::new(CapabilityTypeName::Config)
1113 }
1114
1115 fn new(type_: CapabilityTypeName) -> Self {
1116 Self {
1117 type_,
1118 source: None,
1119 target: None,
1120 source_name: None,
1121 target_name: None,
1122 source_dictionary: Default::default(),
1123 source_instance_filter: None,
1124 renamed_instances: None,
1125 rights: None,
1126 subdir: Default::default(),
1127 scope: None,
1128 dependency_type: cm_rust::DependencyType::Strong,
1129 availability: cm_rust::Availability::Required,
1130 }
1131 }
1132
1133 pub fn name(mut self, name: &str) -> Self {
1134 self.source_name = Some(name.parse().unwrap());
1135 if self.target_name.is_some() {
1136 return self;
1137 }
1138 self.target_name = self.source_name.clone();
1139 self
1140 }
1141
1142 pub fn target_name(mut self, name: &str) -> Self {
1143 self.target_name = Some(name.parse().unwrap());
1144 self
1145 }
1146
1147 pub fn from_dictionary(mut self, dictionary: &str) -> Self {
1148 assert_matches!(
1149 self.type_,
1150 CapabilityTypeName::Service
1151 | CapabilityTypeName::Protocol
1152 | CapabilityTypeName::Directory
1153 | CapabilityTypeName::Dictionary
1154 | CapabilityTypeName::Runner
1155 | CapabilityTypeName::Resolver
1156 );
1157 self.source_dictionary = dictionary.parse().unwrap();
1158 self
1159 }
1160
1161 pub fn source(mut self, source: cm_rust::OfferSource) -> Self {
1162 self.source = Some(source);
1163 self
1164 }
1165
1166 pub fn source_static_child(self, source: &str) -> Self {
1167 self.source(offer_source_static_child(source))
1168 }
1169
1170 pub fn target(mut self, target: cm_rust::OfferTarget) -> Self {
1171 self.target = Some(target);
1172 self
1173 }
1174
1175 pub fn target_static_child(self, target: &str) -> Self {
1176 self.target(offer_target_static_child(target))
1177 }
1178
1179 pub fn availability(mut self, availability: cm_rust::Availability) -> Self {
1180 assert_matches!(
1181 self.type_,
1182 CapabilityTypeName::Protocol
1183 | CapabilityTypeName::Service
1184 | CapabilityTypeName::Directory
1185 | CapabilityTypeName::Storage
1186 | CapabilityTypeName::EventStream
1187 | CapabilityTypeName::Config
1188 | CapabilityTypeName::Dictionary
1189 );
1190 self.availability = availability;
1191 self
1192 }
1193
1194 pub fn dependency(mut self, dependency: cm_rust::DependencyType) -> Self {
1195 assert_matches!(
1196 self.type_,
1197 CapabilityTypeName::Protocol
1198 | CapabilityTypeName::Directory
1199 | CapabilityTypeName::Dictionary
1200 );
1201 self.dependency_type = dependency;
1202 self
1203 }
1204
1205 pub fn source_instance_filter<'a>(mut self, filter: impl IntoIterator<Item = &'a str>) -> Self {
1206 assert_matches!(self.type_, CapabilityTypeName::Service);
1207 self.source_instance_filter =
1208 Some(filter.into_iter().map(|s| s.parse().unwrap()).collect());
1209 self
1210 }
1211
1212 pub fn renamed_instances<'a, 'b>(
1213 mut self,
1214 mapping: impl IntoIterator<Item = (&'a str, &'b str)>,
1215 ) -> Self {
1216 assert_matches!(self.type_, CapabilityTypeName::Service);
1217 self.renamed_instances = Some(
1218 mapping
1219 .into_iter()
1220 .map(|(s, t)| cm_rust::NameMapping {
1221 source_name: s.parse().unwrap(),
1222 target_name: t.parse().unwrap(),
1223 })
1224 .collect(),
1225 );
1226 self
1227 }
1228
1229 pub fn rights(mut self, rights: fio::Operations) -> Self {
1230 assert_matches!(self.type_, CapabilityTypeName::Directory);
1231 self.rights = Some(rights);
1232 self
1233 }
1234
1235 pub fn subdir(mut self, subdir: &str) -> Self {
1236 assert_matches!(self.type_, CapabilityTypeName::Directory);
1237 self.subdir = subdir.parse().unwrap();
1238 self
1239 }
1240
1241 pub fn scope(mut self, scope: Vec<cm_rust::EventScope>) -> Self {
1242 assert_matches!(self.type_, CapabilityTypeName::EventStream);
1243 self.scope = Some(scope);
1244 self
1245 }
1246
1247 pub fn build(self) -> cm_rust::OfferDecl {
1248 match self.type_ {
1249 CapabilityTypeName::Protocol => {
1250 cm_rust::OfferDecl::Protocol(cm_rust::OfferProtocolDecl {
1251 source: self.source.expect("source not set"),
1252 source_name: self.source_name.expect("name not set"),
1253 source_dictionary: self.source_dictionary,
1254 target: self.target.expect("target not set"),
1255 target_name: self.target_name.expect("name not set"),
1256 dependency_type: self.dependency_type,
1257 availability: self.availability,
1258 })
1259 }
1260 CapabilityTypeName::Service => cm_rust::OfferDecl::Service(cm_rust::OfferServiceDecl {
1261 source: self.source.expect("source not set"),
1262 source_name: self.source_name.expect("name not set"),
1263 source_dictionary: self.source_dictionary,
1264 target: self.target.expect("target is not set"),
1265 target_name: self.target_name.expect("name not set"),
1266 source_instance_filter: self.source_instance_filter,
1267 renamed_instances: self.renamed_instances,
1268 availability: self.availability,
1269 dependency_type: Default::default(),
1270 }),
1271 CapabilityTypeName::Directory => {
1272 cm_rust::OfferDecl::Directory(cm_rust::OfferDirectoryDecl {
1273 source: self.source.expect("source not set"),
1274 source_name: self.source_name.expect("name not set"),
1275 source_dictionary: self.source_dictionary,
1276 target: self.target.expect("target is not set"),
1277 target_name: self.target_name.expect("name not set"),
1278 rights: self.rights,
1279 subdir: self.subdir,
1280 dependency_type: self.dependency_type,
1281 availability: self.availability,
1282 })
1283 }
1284 CapabilityTypeName::Storage => cm_rust::OfferDecl::Storage(cm_rust::OfferStorageDecl {
1285 source: self.source.expect("source not set"),
1286 source_name: self.source_name.expect("name not set"),
1287 target: self.target.expect("target is not set"),
1288 target_name: self.target_name.expect("name not set"),
1289 availability: self.availability,
1290 }),
1291 CapabilityTypeName::EventStream => {
1292 cm_rust::OfferDecl::EventStream(cm_rust::OfferEventStreamDecl {
1293 source: self.source.expect("source not set"),
1294 source_name: self.source_name.expect("name not set"),
1295 target: self.target.expect("target is not set"),
1296 target_name: self.target_name.expect("name not set"),
1297 availability: self.availability,
1298 scope: self.scope,
1299 })
1300 }
1301 CapabilityTypeName::Runner => cm_rust::OfferDecl::Runner(cm_rust::OfferRunnerDecl {
1302 source: self.source.expect("source not set"),
1303 source_name: self.source_name.expect("name not set"),
1304 source_dictionary: self.source_dictionary,
1305 target: self.target.expect("target is not set"),
1306 target_name: self.target_name.expect("name not set"),
1307 }),
1308 CapabilityTypeName::Resolver => {
1309 cm_rust::OfferDecl::Resolver(cm_rust::OfferResolverDecl {
1310 source: self.source.expect("source not set"),
1311 source_name: self.source_name.expect("name not set"),
1312 source_dictionary: self.source_dictionary,
1313 target: self.target.expect("target is not set"),
1314 target_name: self.target_name.expect("name not set"),
1315 })
1316 }
1317 CapabilityTypeName::Config => {
1318 cm_rust::OfferDecl::Config(cm_rust::OfferConfigurationDecl {
1319 source: self.source.expect("source not set"),
1320 source_name: self.source_name.expect("name not set"),
1321 target: self.target.expect("target not set"),
1322 target_name: self.target_name.expect("name not set"),
1323 availability: self.availability,
1324 source_dictionary: self.source_dictionary,
1325 })
1326 }
1327 CapabilityTypeName::Dictionary => {
1328 cm_rust::OfferDecl::Dictionary(cm_rust::OfferDictionaryDecl {
1329 source: self.source.expect("source not set"),
1330 source_name: self.source_name.expect("name not set"),
1331 source_dictionary: self.source_dictionary,
1332 target: self.target.expect("target not set"),
1333 target_name: self.target_name.expect("name not set"),
1334 dependency_type: self.dependency_type,
1335 availability: self.availability,
1336 })
1337 }
1338 }
1339 }
1340}
1341
1342impl From<OfferBuilder> for cm_rust::OfferDecl {
1343 fn from(builder: OfferBuilder) -> Self {
1344 builder.build()
1345 }
1346}