1use crate::error::Location;
6use crate::types::common::*;
7use crate::{
8 AnyRef, AsClause, AsClauseContext, Canonicalize, CapabilityClause, CapabilityId, DictionaryRef,
9 Error, EventScope, FromClause, FromClauseContext, PathClause, SourceAvailability,
10};
11
12use crate::one_or_many::{
13 OneOrMany, one_or_many_from_context, one_or_many_from_impl, option_one_or_many_as_ref,
14};
15use crate::types::right::{Rights, RightsClause};
16pub use cm_types::{
17 Availability, BorrowedName, BoundedName, DependencyType, HandleType, Name, OnTerminate,
18 ParseError, Path, RelativePath, StartupMode, Url,
19};
20use cml_macro::{OneOrMany, Reference};
21use itertools::Either;
22use json_spanned_value::Spanned;
23use reference_doc::ReferenceDoc;
24use serde::{Deserialize, Serialize};
25
26use std::fmt;
27use std::fmt::Write;
28use std::path::PathBuf;
29#[allow(unused)] use std::str::FromStr;
31use std::sync::Arc;
32
33#[derive(Deserialize, Debug, PartialEq, Clone, ReferenceDoc, Serialize)]
86#[serde(deny_unknown_fields)]
87#[reference_doc(fields_as = "list", top_level_doc_after_fields)]
88pub struct Offer {
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub service: Option<OneOrMany<Name>>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub protocol: Option<OneOrMany<Name>>,
96
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub directory: Option<OneOrMany<Name>>,
100
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub runner: Option<OneOrMany<Name>>,
104
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub resolver: Option<OneOrMany<Name>>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub storage: Option<OneOrMany<Name>>,
112
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub dictionary: Option<OneOrMany<Name>>,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub config: Option<OneOrMany<Name>>,
120
121 pub from: OneOrMany<OfferFromRef>,
133
134 pub to: OneOrMany<OfferToRef>,
139
140 #[serde(skip_serializing_if = "Option::is_none")]
144 pub r#as: Option<Name>,
145
146 #[serde(skip_serializing_if = "Option::is_none")]
156 pub dependency: Option<DependencyType>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
161 #[reference_doc(json_type = "array of string")]
162 pub rights: Option<Rights>,
163
164 #[serde(skip_serializing_if = "Option::is_none")]
167 pub subdir: Option<RelativePath>,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub event_stream: Option<OneOrMany<Name>>,
172
173 #[serde(skip_serializing_if = "Option::is_none")]
176 pub scope: Option<OneOrMany<EventScope>>,
177
178 #[serde(skip_serializing_if = "Option::is_none")]
195 pub availability: Option<Availability>,
196
197 #[serde(skip_serializing_if = "Option::is_none")]
202 pub source_availability: Option<SourceAvailability>,
203
204 #[serde(skip_serializing_if = "Option::is_none")]
210 pub target_availability: Option<TargetAvailability>,
211}
212
213impl Offer {
214 pub fn empty(from: OneOrMany<OfferFromRef>, to: OneOrMany<OfferToRef>) -> Offer {
217 Self {
218 protocol: None,
219 from,
220 to,
221 r#as: None,
222 service: None,
223 directory: None,
224 config: None,
225 runner: None,
226 resolver: None,
227 storage: None,
228 dictionary: None,
229 dependency: None,
230 rights: None,
231 subdir: None,
232 event_stream: None,
233 scope: None,
234 availability: None,
235 source_availability: None,
236 target_availability: None,
237 }
238 }
239}
240
241impl Default for Offer {
242 fn default() -> Self {
243 Self {
244 from: OneOrMany::One(OfferFromRef::Self_),
245 to: OneOrMany::Many(vec![]),
246 service: None,
247 protocol: None,
248 directory: None,
249 storage: None,
250 runner: None,
251 resolver: None,
252 dictionary: None,
253 config: None,
254 r#as: None,
255 rights: None,
256 subdir: None,
257 dependency: None,
258 event_stream: None,
259 scope: None,
260 availability: None,
261 source_availability: None,
262 target_availability: None,
263 }
264 }
265}
266
267impl FromClause for Offer {
268 fn from_(&self) -> OneOrMany<AnyRef<'_>> {
269 one_or_many_from_impl(&self.from)
270 }
271}
272
273impl Canonicalize for Offer {
274 fn canonicalize(&mut self) {
275 if let Some(service) = &mut self.service {
277 service.canonicalize();
278 } else if let Some(protocol) = &mut self.protocol {
279 protocol.canonicalize();
280 } else if let Some(directory) = &mut self.directory {
281 directory.canonicalize();
282 } else if let Some(runner) = &mut self.runner {
283 runner.canonicalize();
284 } else if let Some(resolver) = &mut self.resolver {
285 resolver.canonicalize();
286 } else if let Some(storage) = &mut self.storage {
287 storage.canonicalize();
288 } else if let Some(event_stream) = &mut self.event_stream {
289 event_stream.canonicalize();
290 if let Some(scope) = &mut self.scope {
291 scope.canonicalize();
292 }
293 }
294 }
295}
296
297impl CapabilityClause for Offer {
298 fn service(&self) -> Option<OneOrMany<&BorrowedName>> {
299 option_one_or_many_as_ref(&self.service)
300 }
301 fn protocol(&self) -> Option<OneOrMany<&BorrowedName>> {
302 option_one_or_many_as_ref(&self.protocol)
303 }
304 fn directory(&self) -> Option<OneOrMany<&BorrowedName>> {
305 option_one_or_many_as_ref(&self.directory)
306 }
307 fn storage(&self) -> Option<OneOrMany<&BorrowedName>> {
308 option_one_or_many_as_ref(&self.storage)
309 }
310 fn runner(&self) -> Option<OneOrMany<&BorrowedName>> {
311 option_one_or_many_as_ref(&self.runner)
312 }
313 fn resolver(&self) -> Option<OneOrMany<&BorrowedName>> {
314 option_one_or_many_as_ref(&self.resolver)
315 }
316 fn event_stream(&self) -> Option<OneOrMany<&BorrowedName>> {
317 option_one_or_many_as_ref(&self.event_stream)
318 }
319 fn dictionary(&self) -> Option<OneOrMany<&BorrowedName>> {
320 option_one_or_many_as_ref(&self.dictionary)
321 }
322 fn config(&self) -> Option<OneOrMany<&BorrowedName>> {
323 option_one_or_many_as_ref(&self.config)
324 }
325
326 fn set_service(&mut self, o: Option<OneOrMany<Name>>) {
327 self.service = o;
328 }
329 fn set_protocol(&mut self, o: Option<OneOrMany<Name>>) {
330 self.protocol = o;
331 }
332 fn set_directory(&mut self, o: Option<OneOrMany<Name>>) {
333 self.directory = o;
334 }
335 fn set_storage(&mut self, o: Option<OneOrMany<Name>>) {
336 self.storage = o;
337 }
338 fn set_runner(&mut self, o: Option<OneOrMany<Name>>) {
339 self.runner = o;
340 }
341 fn set_resolver(&mut self, o: Option<OneOrMany<Name>>) {
342 self.resolver = o;
343 }
344 fn set_event_stream(&mut self, o: Option<OneOrMany<Name>>) {
345 self.event_stream = o;
346 }
347 fn set_dictionary(&mut self, o: Option<OneOrMany<Name>>) {
348 self.dictionary = o;
349 }
350 fn set_config(&mut self, o: Option<OneOrMany<Name>>) {
351 self.config = o
352 }
353
354 fn availability(&self) -> Option<Availability> {
355 self.availability
356 }
357 fn set_availability(&mut self, a: Option<Availability>) {
358 self.availability = a;
359 }
360
361 fn decl_type(&self) -> &'static str {
362 "offer"
363 }
364 fn supported(&self) -> &[&'static str] {
365 &[
366 "service",
367 "protocol",
368 "directory",
369 "storage",
370 "runner",
371 "resolver",
372 "event_stream",
373 "config",
374 ]
375 }
376 fn are_many_names_allowed(&self) -> bool {
377 [
378 "service",
379 "protocol",
380 "directory",
381 "storage",
382 "runner",
383 "resolver",
384 "event_stream",
385 "config",
386 ]
387 .contains(&self.capability_type().unwrap())
388 }
389}
390
391impl PathClause for Offer {
392 fn path(&self) -> Option<&Path> {
393 None
394 }
395}
396
397impl RightsClause for Offer {
398 fn rights(&self) -> Option<&Rights> {
399 self.rights.as_ref()
400 }
401}
402
403impl AsClause for Offer {
404 fn r#as(&self) -> Option<&BorrowedName> {
405 self.r#as.as_ref().map(Name::as_ref)
406 }
407}
408
409#[derive(Debug, Deserialize, PartialEq, Eq, Hash, Clone, Serialize)]
411#[serde(rename_all = "snake_case")]
412pub enum TargetAvailability {
413 Required,
414 Unknown,
415}
416
417#[derive(PartialEq, Clone)]
418pub enum OfferToAllCapability<'a> {
419 Dictionary(&'a str),
420 Protocol(&'a str),
421}
422
423impl<'a> OfferToAllCapability<'a> {
424 pub fn name(&self) -> &'a str {
425 match self {
426 OfferToAllCapability::Dictionary(name) => name,
427 OfferToAllCapability::Protocol(name) => name,
428 }
429 }
430
431 pub fn offer_type(&self) -> &'static str {
432 match self {
433 OfferToAllCapability::Dictionary(_) => "Dictionary",
434 OfferToAllCapability::Protocol(_) => "Protocol",
435 }
436 }
437
438 pub fn offer_type_plural(&self) -> &'static str {
439 match self {
440 OfferToAllCapability::Dictionary(_) => "dictionaries",
441 OfferToAllCapability::Protocol(_) => "protocols",
442 }
443 }
444}
445
446pub fn offer_to_all_from_offer(value: &Offer) -> impl Iterator<Item = OfferToAllCapability<'_>> {
447 if let Some(protocol) = &value.protocol {
448 Either::Left(
449 protocol.iter().map(|protocol| OfferToAllCapability::Protocol(protocol.as_str())),
450 )
451 } else if let Some(dictionary) = &value.dictionary {
452 Either::Right(
453 dictionary
454 .iter()
455 .map(|dictionary| OfferToAllCapability::Dictionary(dictionary.as_str())),
456 )
457 } else {
458 panic!("Expected a dictionary or a protocol");
459 }
460}
461pub fn offer_to_all_and_component_diff_sources_message<'a>(
462 capability: impl Iterator<Item = OfferToAllCapability<'a>>,
463 component: &str,
464) -> String {
465 let mut output = String::new();
466 let mut capability = capability.peekable();
467 write!(&mut output, "{} ", capability.peek().unwrap().offer_type()).unwrap();
468 for (i, capability) in capability.enumerate() {
469 if i > 0 {
470 write!(&mut output, ", ").unwrap();
471 }
472 write!(&mut output, "{}", capability.name()).unwrap();
473 }
474 write!(
475 &mut output,
476 r#" is offered to both "all" and child component "{}" with different sources"#,
477 component
478 )
479 .unwrap();
480 output
481}
482
483pub fn offer_to_all_and_component_diff_capabilities_message<'a>(
484 capability: impl Iterator<Item = OfferToAllCapability<'a>>,
485 component: &str,
486) -> String {
487 let mut output = String::new();
488 let mut capability_peek = capability.peekable();
489
490 let first_offer_to_all = capability_peek.peek().unwrap().clone();
494 write!(&mut output, "{} ", first_offer_to_all.offer_type()).unwrap();
495 for (i, capability) in capability_peek.enumerate() {
496 if i > 0 {
497 write!(&mut output, ", ").unwrap();
498 }
499 write!(&mut output, "{}", capability.name()).unwrap();
500 }
501 write!(&mut output, r#" is aliased to "{}" with the same name as an offer to "all", but from different source {}"#, component, first_offer_to_all.offer_type_plural()).unwrap();
502 output
503}
504
505pub fn offer_to_all_would_duplicate(
510 offer_to_all: &Offer,
511 specific_offer: &Offer,
512 target: &cm_types::BorrowedName,
513) -> Result<bool, Error> {
514 assert!(offer_to_all.protocol.is_some() || offer_to_all.dictionary.is_some());
516
517 if CapabilityId::from_offer_expose(specific_offer).iter().flatten().all(
520 |specific_offer_cap_id| {
521 CapabilityId::from_offer_expose(offer_to_all)
522 .iter()
523 .flatten()
524 .all(|offer_to_all_cap_id| offer_to_all_cap_id != specific_offer_cap_id)
525 },
526 ) {
527 return Ok(false);
528 }
529
530 let to_field_matches = specific_offer.to.iter().any(
531 |specific_offer_to| matches!(specific_offer_to, OfferToRef::Named(c) if **c == *target),
532 );
533
534 if !to_field_matches {
535 return Ok(false);
536 }
537
538 if offer_to_all.from != specific_offer.from {
539 return Err(Error::validate(offer_to_all_and_component_diff_sources_message(
540 offer_to_all_from_offer(offer_to_all),
541 target.as_str(),
542 )));
543 }
544
545 if offer_to_all_from_offer(offer_to_all).all(|to_all_protocol| {
547 offer_to_all_from_offer(specific_offer)
548 .all(|to_specific_protocol| to_all_protocol != to_specific_protocol)
549 }) {
550 return Err(Error::validate(offer_to_all_and_component_diff_capabilities_message(
551 offer_to_all_from_offer(offer_to_all),
552 target.as_str(),
553 )));
554 }
555
556 Ok(true)
557}
558
559#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
561#[reference(
562 expected = "\"parent\", \"framework\", \"self\", \"void\", \"#<child-name>\", or a dictionary path"
563)]
564pub enum OfferFromRef {
565 Named(Name),
567 Parent,
569 Framework,
571 Self_,
573 Void,
575 Dictionary(DictionaryRef),
577}
578
579impl OfferFromRef {
580 pub fn is_named(&self) -> bool {
581 match self {
582 OfferFromRef::Named(_) => true,
583 _ => false,
584 }
585 }
586}
587
588#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
590#[reference(expected = "\"#<child-name>\", \"#<collection-name>\", or \"self/<dictionary>\"")]
591pub enum OfferToRef {
592 Named(Name),
594
595 All,
597
598 OwnDictionary(Name),
600}
601
602#[derive(OneOrMany, Debug, Clone)]
604#[one_or_many(
605 expected = "one or an array of \"#<child-name>\", \"#<collection-name>\", or \"self/<dictionary>\", with unique elements",
606 inner_type = "OfferToRef",
607 min_length = 1,
608 unique_items = true
609)]
610pub struct OneOrManyOfferToRefs;
611
612#[derive(OneOrMany, Debug, Clone)]
614#[one_or_many(
615 expected = "one or an array of \"parent\", \"framework\", \"self\", \"#<child-name>\", \"#<collection-name>\", or a dictionary path",
616 inner_type = "OfferFromRef",
617 min_length = 1,
618 unique_items = true
619)]
620pub struct OneOrManyOfferFromRefs;
621
622#[derive(Deserialize, Debug, PartialEq, Clone)]
623#[serde(deny_unknown_fields)]
624pub struct ParsedOffer {
625 #[serde(default, skip_serializing_if = "Option::is_none")]
626 pub service: Option<Spanned<OneOrMany<Name>>>,
627 pub protocol: Option<Spanned<OneOrMany<Name>>>,
628 pub directory: Option<Spanned<OneOrMany<Name>>>,
629 pub runner: Option<Spanned<OneOrMany<Name>>>,
630 pub resolver: Option<Spanned<OneOrMany<Name>>>,
631 pub storage: Option<Spanned<OneOrMany<Name>>>,
632 pub dictionary: Option<Spanned<OneOrMany<Name>>>,
633 pub config: Option<Spanned<OneOrMany<Name>>>,
634 pub from: Spanned<OneOrMany<OfferFromRef>>,
635 pub to: Spanned<OneOrMany<OfferToRef>>,
636 pub r#as: Option<Spanned<Name>>,
637 pub dependency: Option<Spanned<DependencyType>>,
638 pub rights: Option<Spanned<Rights>>,
639 pub subdir: Option<Spanned<RelativePath>>,
640 pub event_stream: Option<Spanned<OneOrMany<Name>>>,
641 pub scope: Option<Spanned<OneOrMany<EventScope>>>,
642 pub availability: Option<Spanned<Availability>>,
643 pub source_availability: Option<Spanned<SourceAvailability>>,
644 pub target_availability: Option<Spanned<TargetAvailability>>,
645}
646
647#[derive(Debug, Clone)]
648pub struct ContextOffer {
649 pub origin: Origin,
650 pub service: Option<ContextSpanned<OneOrMany<Name>>>,
651 pub protocol: Option<ContextSpanned<OneOrMany<Name>>>,
652 pub directory: Option<ContextSpanned<OneOrMany<Name>>>,
653 pub runner: Option<ContextSpanned<OneOrMany<Name>>>,
654 pub resolver: Option<ContextSpanned<OneOrMany<Name>>>,
655 pub storage: Option<ContextSpanned<OneOrMany<Name>>>,
656 pub dictionary: Option<ContextSpanned<OneOrMany<Name>>>,
657 pub config: Option<ContextSpanned<OneOrMany<Name>>>,
658 pub from: ContextSpanned<OneOrMany<OfferFromRef>>,
659 pub to: ContextSpanned<OneOrMany<OfferToRef>>,
660 pub r#as: Option<ContextSpanned<Name>>,
661 pub dependency: Option<ContextSpanned<DependencyType>>,
662 pub rights: Option<ContextSpanned<Rights>>,
663 pub subdir: Option<ContextSpanned<RelativePath>>,
664 pub event_stream: Option<ContextSpanned<OneOrMany<Name>>>,
665 pub scope: Option<ContextSpanned<OneOrMany<EventScope>>>,
666 pub availability: Option<ContextSpanned<Availability>>,
667 pub source_availability: Option<ContextSpanned<SourceAvailability>>,
668 pub target_availability: Option<ContextSpanned<TargetAvailability>>,
669}
670
671impl ContextCapabilityClause for ContextOffer {
672 fn service(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
673 option_one_or_many_as_ref_context(&self.service)
674 }
675 fn protocol(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
676 option_one_or_many_as_ref_context(&self.protocol)
677 }
678 fn directory(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
679 option_one_or_many_as_ref_context(&self.directory)
680 }
681 fn storage(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
682 option_one_or_many_as_ref_context(&self.storage)
683 }
684 fn runner(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
685 option_one_or_many_as_ref_context(&self.runner)
686 }
687 fn resolver(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
688 option_one_or_many_as_ref_context(&self.resolver)
689 }
690 fn event_stream(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
691 option_one_or_many_as_ref_context(&self.event_stream)
692 }
693 fn dictionary(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
694 option_one_or_many_as_ref_context(&self.dictionary)
695 }
696 fn config(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
697 option_one_or_many_as_ref_context(&self.config)
698 }
699
700 fn decl_type(&self) -> &'static str {
701 "offer"
702 }
703 fn supported(&self) -> &[&'static str] {
704 &[
705 "service",
706 "protocol",
707 "directory",
708 "storage",
709 "event_stream",
710 "runner",
711 "resolver",
712 "config",
713 ]
714 }
715 fn are_many_names_allowed(&self) -> bool {
716 [
717 "service",
718 "protocol",
719 "directory",
720 "storage",
721 "runner",
722 "resolver",
723 "event_stream",
724 "config",
725 ]
726 .contains(&self.capability_type(None).unwrap())
727 }
728
729 fn set_service(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
730 self.service = o;
731 }
732
733 fn set_protocol(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
734 self.protocol = o;
735 }
736
737 fn set_directory(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
738 self.directory = o;
739 }
740
741 fn set_storage(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
742 self.storage = o;
743 }
744
745 fn set_runner(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
746 self.runner = o;
747 }
748 fn set_resolver(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
749 self.resolver = o;
750 }
751 fn set_event_stream(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
752 self.event_stream = o;
753 }
754 fn set_dictionary(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
755 self.dictionary = o;
756 }
757 fn set_config(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
758 self.config = o;
759 }
760
761 fn origin(&self) -> &Origin {
762 &self.origin
763 }
764
765 fn file_path(&self) -> PathBuf {
767 (*self.origin.file).clone()
768 }
769}
770
771impl PartialEq for ContextOffer {
772 fn eq(&self, other: &Self) -> bool {
773 macro_rules! cmp {
774 ($field:ident) => {
775 match (&self.$field, &other.$field) {
776 (Some(a), Some(b)) => a.value == b.value,
777 (None, None) => true,
778 _ => false,
779 }
780 };
781 }
782
783 cmp!(service)
784 && cmp!(protocol)
785 && cmp!(directory)
786 && cmp!(runner)
787 && cmp!(resolver)
788 && cmp!(storage)
789 && cmp!(dictionary)
790 && cmp!(config)
791 && self.from.value == other.from.value
792 && self.to.value == other.to.value
793 && cmp!(r#as)
794 && cmp!(dependency)
795 && cmp!(rights)
796 && cmp!(subdir)
797 && cmp!(event_stream)
798 && cmp!(scope)
799 && cmp!(availability)
800 && cmp!(source_availability)
801 && cmp!(target_availability)
802 }
803}
804
805impl Eq for ContextOffer {}
806
807impl Default for ContextOffer {
808 fn default() -> Self {
809 let synthetic_origin = Origin {
810 file: Arc::new(PathBuf::from("synthetic")),
811 location: Location { line: 0, column: 0 },
812 };
813
814 Self {
815 from: ContextSpanned {
816 value: OneOrMany::One(OfferFromRef::Self_),
817 origin: synthetic_origin.clone(),
818 },
819 to: ContextSpanned { value: OneOrMany::Many(vec![]), origin: synthetic_origin.clone() },
820 origin: synthetic_origin,
821 service: None,
822 protocol: None,
823 directory: None,
824 storage: None,
825 runner: None,
826 resolver: None,
827 dictionary: None,
828 config: None,
829 r#as: None,
830 rights: None,
831 subdir: None,
832 dependency: None,
833 event_stream: None,
834 scope: None,
835 availability: None,
836 source_availability: None,
837 target_availability: None,
838 }
839 }
840}
841
842impl ContextPathClause for ContextOffer {
843 fn path(&self) -> Option<&ContextSpanned<Path>> {
844 None
845 }
846}
847
848impl AsClauseContext for ContextOffer {
849 fn r#as(&self) -> Option<ContextSpanned<&BorrowedName>> {
850 self.r#as.as_ref().map(|spanned_name| ContextSpanned {
851 value: spanned_name.value.as_ref(),
852 origin: spanned_name.origin.clone(),
853 })
854 }
855}
856
857impl FromClauseContext for ContextOffer {
858 fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>> {
859 one_or_many_from_context(&self.from)
860 }
861}
862
863impl Hydrate for ParsedOffer {
864 type Output = ContextOffer;
865
866 fn hydrate(self, file: &Arc<PathBuf>, buffer: &String) -> Result<Self::Output, Error> {
867 Ok(ContextOffer {
868 origin: Origin::synthetic(file.clone().to_path_buf()),
869 service: hydrate_opt_simple(self.service, file, buffer),
870 protocol: hydrate_opt_simple(self.protocol, file, buffer),
871 directory: hydrate_opt_simple(self.directory, file, buffer),
872 runner: hydrate_opt_simple(self.runner, file, buffer),
873 resolver: hydrate_opt_simple(self.resolver, file, buffer),
874 storage: hydrate_opt_simple(self.storage, file, buffer),
875 dictionary: hydrate_opt_simple(self.dictionary, file, buffer),
876 config: hydrate_opt_simple(self.config, file, buffer),
877 from: hydrate_simple(self.from, file, buffer),
878 to: hydrate_simple(self.to, file, buffer),
879 r#as: hydrate_opt_simple(self.r#as, file, buffer),
880 dependency: hydrate_opt_simple(self.dependency, file, buffer),
881 rights: hydrate_opt_simple(self.rights, file, buffer),
882 subdir: hydrate_opt_simple(self.subdir, file, buffer),
883 event_stream: hydrate_opt_simple(self.event_stream, file, buffer),
884 scope: hydrate_opt_simple(self.scope, file, buffer),
885 availability: hydrate_opt_simple(self.availability, file, buffer),
886 source_availability: hydrate_opt_simple(self.source_availability, file, buffer),
887 target_availability: hydrate_opt_simple(self.target_availability, file, buffer),
888 })
889 }
890}
891
892pub fn offer_to_all_from_context_offer(
893 value: &ContextOffer,
894) -> impl Iterator<Item = OfferToAllCapability<'_>> {
895 if let Some(protocol) = &value.protocol {
896 Either::Left(
897 protocol.value.iter().map(|protocol| OfferToAllCapability::Protocol(protocol.as_str())),
898 )
899 } else if let Some(dictionary) = &value.dictionary {
900 Either::Right(
901 dictionary
902 .value
903 .iter()
904 .map(|dictionary| OfferToAllCapability::Dictionary(dictionary.as_str())),
905 )
906 } else {
907 panic!("Expected a dictionary or a protocol");
908 }
909}
910
911pub fn offer_to_all_would_duplicate_context(
916 offer_to_all: &ContextSpanned<ContextOffer>,
917 specific_offer: &ContextSpanned<ContextOffer>,
918 target: &cm_types::BorrowedName,
919) -> Result<bool, Error> {
920 assert!(offer_to_all.value.protocol.is_some() || offer_to_all.value.dictionary.is_some());
922
923 if CapabilityId::from_context_offer(specific_offer).iter().flatten().all(
926 |specific_offer_cap_id| {
927 CapabilityId::from_context_offer(offer_to_all)
928 .iter()
929 .flatten()
930 .all(|offer_to_all_cap_id| offer_to_all_cap_id.0 != specific_offer_cap_id.0)
931 },
932 ) {
933 return Ok(false);
934 }
935
936 let to_field_matches = specific_offer.value.to.value.iter().any(
937 |specific_offer_to| matches!(specific_offer_to, OfferToRef::Named(c) if **c == *target),
938 );
939
940 if !to_field_matches {
941 return Ok(false);
942 }
943
944 if offer_to_all.value.from != specific_offer.value.from {
945 return Err(Error::validate_contexts(
946 offer_to_all_and_component_diff_sources_message(
947 offer_to_all_from_context_offer(&offer_to_all.value),
948 target.as_str(),
949 ),
950 vec![offer_to_all.origin.clone(), specific_offer.origin.clone()],
951 ));
952 }
953
954 if offer_to_all_from_context_offer(&offer_to_all.value).all(|to_all_protocol| {
956 offer_to_all_from_context_offer(&specific_offer.value)
957 .all(|to_specific_protocol| to_all_protocol != to_specific_protocol)
958 }) {
959 return Err(Error::validate_contexts(
960 offer_to_all_and_component_diff_capabilities_message(
961 offer_to_all_from_context_offer(&offer_to_all.value),
962 target.as_str(),
963 ),
964 vec![offer_to_all.origin.clone(), specific_offer.origin.clone()],
965 ));
966 }
967
968 Ok(true)
969}
970
971#[cfg(test)]
972pub fn create_offer(
973 protocol_name: &str,
974 from: OneOrMany<OfferFromRef>,
975 to: OneOrMany<OfferToRef>,
976) -> Offer {
977 Offer {
978 protocol: Some(OneOrMany::One(Name::from_str(protocol_name).unwrap())),
979 ..Offer::empty(from, to)
980 }
981}
982
983#[cfg(test)]
984mod tests {
985 use super::*;
986
987 #[test]
988 fn test_offer_would_duplicate() {
989 let offer = create_offer(
990 "fuchsia.logger.LegacyLog",
991 OneOrMany::One(OfferFromRef::Parent {}),
992 OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
993 );
994
995 let offer_to_all = create_offer(
996 "fuchsia.logger.LogSink",
997 OneOrMany::One(OfferFromRef::Parent {}),
998 OneOrMany::One(OfferToRef::All),
999 );
1000
1001 assert!(
1003 !offer_to_all_would_duplicate(
1004 &offer_to_all,
1005 &offer,
1006 &Name::from_str("something").unwrap()
1007 )
1008 .unwrap()
1009 );
1010
1011 let offer = create_offer(
1012 "fuchsia.logger.LogSink",
1013 OneOrMany::One(OfferFromRef::Parent {}),
1014 OneOrMany::One(OfferToRef::Named(Name::from_str("not-something").unwrap())),
1015 );
1016
1017 assert!(
1019 !offer_to_all_would_duplicate(
1020 &offer_to_all,
1021 &offer,
1022 &Name::from_str("something").unwrap()
1023 )
1024 .unwrap()
1025 );
1026
1027 let mut offer = create_offer(
1028 "fuchsia.logger.LogSink",
1029 OneOrMany::One(OfferFromRef::Parent {}),
1030 OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
1031 );
1032
1033 offer.r#as = Some(Name::from_str("FakeLog").unwrap());
1034
1035 assert!(
1037 !offer_to_all_would_duplicate(
1038 &offer_to_all,
1039 &offer,
1040 &Name::from_str("something").unwrap()
1041 )
1042 .unwrap()
1043 );
1044
1045 let offer = create_offer(
1046 "fuchsia.logger.LogSink",
1047 OneOrMany::One(OfferFromRef::Parent {}),
1048 OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
1049 );
1050
1051 assert!(
1052 offer_to_all_would_duplicate(
1053 &offer_to_all,
1054 &offer,
1055 &Name::from_str("something").unwrap()
1056 )
1057 .unwrap()
1058 );
1059
1060 let offer = create_offer(
1061 "fuchsia.logger.LogSink",
1062 OneOrMany::One(OfferFromRef::Named(Name::from_str("other").unwrap())),
1063 OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
1064 );
1065
1066 assert!(
1067 offer_to_all_would_duplicate(
1068 &offer_to_all,
1069 &offer,
1070 &Name::from_str("something").unwrap()
1071 )
1072 .is_err()
1073 );
1074 }
1075}