1use crate::language_standard::{
28 CStandard, CxxStandard, InterfaceRequirement, LanguageStandardSettings,
29 ResolvedLanguageStandards, effective_c, effective_cxx,
30};
31use crate::{SourceLanguage, Target, classify_source};
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
42pub enum Requirement<S> {
43 Unconstrained,
45 Min(S),
48 Forbidden,
50}
51
52impl<S: Copy + Ord> Requirement<S> {
53 #[must_use]
57 pub fn join(self, other: Self) -> Self {
58 self.max(other)
59 }
60
61 #[must_use]
64 pub fn join_all(requirements: impl IntoIterator<Item = Self>) -> Self {
65 requirements
66 .into_iter()
67 .fold(Self::Unconstrained, Self::join)
68 }
69
70 #[must_use]
73 pub fn satisfied_by(self, level: S) -> bool {
74 match self {
75 Self::Unconstrained => true,
76 Self::Min(min) => level >= min,
77 Self::Forbidden => false,
78 }
79 }
80
81 #[must_use]
86 pub fn sat(self, levels: &[S]) -> Vec<S> {
87 levels
88 .iter()
89 .copied()
90 .filter(|&level| self.satisfied_by(level))
91 .collect()
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum DependencyKind {
99 Compiled,
101 HeaderOnly,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub struct DependencyAttributes {
113 pub kind: DependencyKind,
115 pub impl_c: Option<CStandard>,
118 pub impl_cxx: Option<CxxStandard>,
120 pub decl_c: Option<InterfaceRequirement<CStandard>>,
125 pub decl_cxx: Option<InterfaceRequirement<CxxStandard>>,
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub enum ReqOfSource {
137 DeclaredNone,
139 Declared,
141 HeaderOnlyInference,
144 CompiledNoDeclaration,
147 CrossLanguageDefault,
150}
151
152#[must_use]
156pub fn req_of_c(dependency: &DependencyAttributes) -> Requirement<CStandard> {
157 req_of_c_with_source(dependency).0
158}
159
160#[must_use]
162pub fn req_of_c_with_source(
163 dependency: &DependencyAttributes,
164) -> (Requirement<CStandard>, ReqOfSource) {
165 req_of(
166 dependency.kind,
167 dependency.decl_c,
168 dependency.impl_c,
169 Requirement::Forbidden,
170 )
171}
172
173#[must_use]
178pub fn req_of_cxx(dependency: &DependencyAttributes) -> Requirement<CxxStandard> {
179 req_of_cxx_with_source(dependency).0
180}
181
182#[must_use]
184pub fn req_of_cxx_with_source(
185 dependency: &DependencyAttributes,
186) -> (Requirement<CxxStandard>, ReqOfSource) {
187 req_of(
188 dependency.kind,
189 dependency.decl_cxx,
190 dependency.impl_cxx,
191 Requirement::Unconstrained,
192 )
193}
194
195fn req_of<S: Copy + Ord>(
198 kind: DependencyKind,
199 decl: Option<InterfaceRequirement<S>>,
200 implementation: Option<S>,
201 absent_default: Requirement<S>,
202) -> (Requirement<S>, ReqOfSource) {
203 match (decl, implementation) {
204 (Some(InterfaceRequirement::None), _) => {
206 (Requirement::Forbidden, ReqOfSource::DeclaredNone)
207 }
208 (Some(InterfaceRequirement::Requirement(requirement)), _) => {
212 (Requirement::Min(requirement.min), ReqOfSource::Declared)
213 }
214 (None, Some(min)) => match kind {
218 DependencyKind::HeaderOnly => (Requirement::Min(min), ReqOfSource::HeaderOnlyInference),
219 DependencyKind::Compiled => (
220 Requirement::Unconstrained,
221 ReqOfSource::CompiledNoDeclaration,
222 ),
223 },
224 (None, None) => (absent_default, ReqOfSource::CrossLanguageDefault),
226 }
227}
228
229#[must_use]
245pub fn dependency_attributes(
246 target: &Target,
247 package_standards: &ResolvedLanguageStandards,
248 package_settings: &LanguageStandardSettings,
249) -> DependencyAttributes {
250 let header_only = target.kind.is_header_only();
251 let kind = if header_only {
252 DependencyKind::HeaderOnly
253 } else {
254 DependencyKind::Compiled
255 };
256
257 let has_sources_of = |language: SourceLanguage| {
258 target
259 .sources
260 .iter()
261 .any(|source| classify_source(source) == Some(language))
262 };
263
264 let impl_c = if header_only {
265 target.language.c_standard_value()
266 } else if has_sources_of(SourceLanguage::C) {
267 effective_c(package_standards, target).map(|resolved| resolved.standard)
268 } else {
269 None
270 };
271 let impl_cxx = if header_only {
272 target.language.cxx_standard_value()
273 } else if has_sources_of(SourceLanguage::Cxx) {
274 effective_cxx(package_standards, target).map(|resolved| resolved.standard)
275 } else {
276 None
277 };
278
279 let library_like = target.kind.is_library_like();
285 let decl_c = target.language.interface_c_standard_value().or_else(|| {
286 library_like
287 .then(|| package_settings.interface_c_standard_value())
288 .flatten()
289 });
290 let decl_cxx = target.language.interface_cxx_standard_value().or_else(|| {
291 library_like
292 .then(|| package_settings.interface_cxx_standard_value())
293 .flatten()
294 });
295
296 DependencyAttributes {
297 kind,
298 impl_c,
299 impl_cxx,
300 decl_c,
301 decl_cxx,
302 }
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Eq)]
311pub struct ConsumerStandards {
312 pub c: Option<CStandard>,
314 pub cxx: Option<CxxStandard>,
316}
317
318#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
326pub enum IncompatibleStandards {
327 Allow,
332 #[default]
337 Fallback,
338}
339
340impl IncompatibleStandards {
341 pub const ALL: [Self; 2] = [Self::Allow, Self::Fallback];
343
344 #[must_use]
346 pub fn as_str(self) -> &'static str {
347 match self {
348 Self::Allow => "allow",
349 Self::Fallback => "fallback",
350 }
351 }
352
353 pub fn parse(value: &str) -> Result<Self, UnknownIncompatibleStandards> {
359 match value {
360 "allow" => Ok(Self::Allow),
361 "fallback" => Ok(Self::Fallback),
362 other => Err(UnknownIncompatibleStandards {
363 value: other.to_owned(),
364 }),
365 }
366 }
367}
368
369impl std::fmt::Display for IncompatibleStandards {
370 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
371 f.write_str(self.as_str())
372 }
373}
374
375#[derive(Debug, Clone, PartialEq, Eq)]
378pub struct UnknownIncompatibleStandards {
379 pub value: String,
381}
382
383impl std::fmt::Display for UnknownIncompatibleStandards {
384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
385 write!(
386 f,
387 "invalid incompatible-standards value {:?}; expected one of: allow, fallback",
388 self.value
389 )
390 }
391}
392
393impl std::error::Error for UnknownIncompatibleStandards {}
394
395#[derive(Debug, Clone, Copy, PartialEq, Eq)]
399pub struct EffectiveRequirements {
400 pub c: Requirement<CStandard>,
402 pub cxx: Requirement<CxxStandard>,
404}
405
406#[must_use]
413pub fn edge_compatible(consumer: ConsumerStandards, dependency: EffectiveRequirements) -> bool {
414 consumer
415 .c
416 .is_none_or(|level| dependency.c.satisfied_by(level))
417 && consumer
418 .cxx
419 .is_none_or(|level| dependency.cxx.satisfied_by(level))
420}
421
422#[must_use]
427pub fn version_viable(
428 edges: impl IntoIterator<Item = (ConsumerStandards, EffectiveRequirements)>,
429) -> bool {
430 edges
431 .into_iter()
432 .all(|(consumer, dependency)| edge_compatible(consumer, dependency))
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438 use crate::language_standard::StandardRequirement;
439
440 const KINDS: [DependencyKind; 2] = [DependencyKind::Compiled, DependencyKind::HeaderOnly];
441
442 fn all_requirements<S: Copy>(levels: &[S]) -> Vec<Requirement<S>> {
444 let mut requirements = vec![Requirement::Unconstrained];
445 requirements.extend(levels.iter().copied().map(Requirement::Min));
446 requirements.push(Requirement::Forbidden);
447 requirements
448 }
449
450 fn spec_le<S: Copy + Ord>(r: Requirement<S>, s: Requirement<S>) -> bool {
454 match (r, s) {
455 (Requirement::Unconstrained, _) | (_, Requirement::Forbidden) => true,
456 (Requirement::Min(a), Requirement::Min(b)) => a <= b,
457 _ => r == s,
459 }
460 }
461
462 fn interface_min<S>(min: S) -> InterfaceRequirement<S> {
463 InterfaceRequirement::Requirement(StandardRequirement { min, max: None })
464 }
465
466 fn optional_levels<S: Copy>(levels: &[S]) -> Vec<Option<S>> {
468 let mut options = vec![None];
469 options.extend(levels.iter().copied().map(Some));
470 options
471 }
472
473 fn check_l1_finite_chain<S: Copy + Ord + std::fmt::Debug>(levels: &[S]) {
474 let requirements = all_requirements(levels);
475 for &r in &requirements {
476 assert!(spec_le(r, r));
479 assert!(spec_le(Requirement::Unconstrained, r));
480 assert!(spec_le(r, Requirement::Forbidden));
481 for &s in &requirements {
482 assert_eq!(r <= s, spec_le(r, s), "derived Ord vs D3 at {r:?} ⊑ {s:?}");
484 assert!(spec_le(r, s) || spec_le(s, r), "totality at {r:?}, {s:?}");
486 if spec_le(r, s) && spec_le(s, r) {
487 assert_eq!(r, s, "antisymmetry at {r:?}, {s:?}");
488 }
489 for &t in &requirements {
491 if spec_le(r, s) && spec_le(s, t) {
492 assert!(spec_le(r, t), "transitivity at {r:?}, {s:?}, {t:?}");
493 }
494 }
495 }
496 }
497 }
498
499 #[test]
503 fn l1_requirement_domain_is_a_finite_chain() {
504 check_l1_finite_chain(&CStandard::ALL);
505 check_l1_finite_chain(&CxxStandard::ALL);
506 }
507
508 fn check_l2_bounded_semilattice<S: Copy + Ord + std::fmt::Debug>(levels: &[S]) {
509 let requirements = all_requirements(levels);
510 for &r in &requirements {
511 assert_eq!(r.join(r), r);
513 assert_eq!(Requirement::Unconstrained.join(r), r);
514 assert_eq!(Requirement::Forbidden.join(r), Requirement::Forbidden);
515 for &s in &requirements {
516 let join = r.join(s);
517 assert_eq!(join, s.join(r));
519 assert!(
521 spec_le(r, join) && spec_le(s, join),
522 "upper bound at {r:?}, {s:?}"
523 );
524 for &upper in &requirements {
525 if spec_le(r, upper) && spec_le(s, upper) {
526 assert!(spec_le(join, upper), "leastness at {r:?}, {s:?}, {upper:?}");
527 }
528 }
529 assert_eq!(Requirement::join_all([r, s]), join);
531 assert_eq!(Requirement::join_all([s, r]), join);
532 assert_eq!(Requirement::join_all([r, r, s]), join);
533 for &t in &requirements {
535 assert_eq!(r.join(s).join(t), r.join(s.join(t)));
536 assert_eq!(Requirement::join_all([r, s, t]), r.join(s).join(t));
537 }
538 }
539 }
540 assert_eq!(
543 Requirement::join_all(std::iter::empty::<Requirement<S>>()),
544 Requirement::Unconstrained
545 );
546 for union_of in 0u32..(1 << requirements.len()) {
547 for other in 0u32..(1 << requirements.len()) {
548 assert_eq!(
549 mask_join(&requirements, union_of | other),
550 mask_join(&requirements, union_of).join(mask_join(&requirements, other))
551 );
552 }
553 }
554 }
555
556 fn mask_join<S: Copy + Ord>(requirements: &[Requirement<S>], mask: u32) -> Requirement<S> {
559 Requirement::join_all(
560 requirements
561 .iter()
562 .enumerate()
563 .filter(|&(index, _)| mask & (1 << index) != 0)
564 .map(|(_, &requirement)| requirement),
565 )
566 }
567
568 #[test]
574 fn l2_join_is_a_bounded_semilattice() {
575 check_l2_bounded_semilattice(&CStandard::ALL);
576 check_l2_bounded_semilattice(&CxxStandard::ALL);
577 }
578
579 fn check_l3_sat_characterization<S: Copy + Ord + std::fmt::Debug>(levels: &[S]) {
580 let bottom = levels[0];
581 let degenerate = (Requirement::Min(bottom), Requirement::Unconstrained);
582 for &r1 in &all_requirements(levels) {
583 for &r2 in &all_requirements(levels) {
584 let sat1 = r1.sat(levels);
585 let sat2 = r2.sat(levels);
586 let included = sat2.iter().all(|level| sat1.contains(level));
587 if spec_le(r1, r2) {
589 assert!(included, "L3(1) at {r1:?}, {r2:?}");
590 }
591 if included && (r1, r2) != degenerate {
593 assert!(spec_le(r1, r2), "L3(2) at {r1:?}, {r2:?}");
594 }
595 let equivalent = r1 == r2 || (r1, r2) == degenerate || (r2, r1) == degenerate;
598 assert_eq!(sat1 == sat2, equivalent, "L3(3) at {r1:?}, {r2:?}");
599 }
600 }
601 assert_eq!(
604 Requirement::Min(bottom).sat(levels),
605 Requirement::<S>::Unconstrained.sat(levels)
606 );
607 assert!(!spec_le(
608 Requirement::Min(bottom),
609 Requirement::Unconstrained
610 ));
611 }
612
613 #[test]
617 fn l3_sat_inclusion_characterizes_strictness() {
618 check_l3_sat_characterization(&CStandard::ALL);
619 check_l3_sat_characterization(&CxxStandard::ALL);
620 }
621
622 fn check_l4_join_is_intersection<S: Copy + Ord + std::fmt::Debug>(levels: &[S]) {
623 let requirements = all_requirements(levels);
624 let intersect = |a: &[S], b: &[S]| -> Vec<S> {
625 a.iter()
626 .copied()
627 .filter(|level| b.contains(level))
628 .collect()
629 };
630 for &r1 in &requirements {
631 for &r2 in &requirements {
632 let expected = intersect(&r1.sat(levels), &r2.sat(levels));
633 assert_eq!(r1.join(r2).sat(levels), expected, "L4 at {r1:?}, {r2:?}");
634 for &r3 in &requirements {
636 assert_eq!(
637 Requirement::join_all([r1, r2, r3]).sat(levels),
638 intersect(&expected, &r3.sat(levels))
639 );
640 }
641 }
642 }
643 assert_eq!(
645 Requirement::join_all(std::iter::empty::<Requirement<S>>()).sat(levels),
646 levels
647 );
648 }
649
650 #[test]
653 fn l4_sat_of_join_is_intersection() {
654 check_l4_join_is_intersection(&CStandard::ALL);
655 check_l4_join_is_intersection(&CxxStandard::ALL);
656 }
657
658 fn check_l5_antitonicity<S: Copy + Ord + std::fmt::Debug>(levels: &[S]) {
659 for &r1 in &all_requirements(levels) {
660 for &r2 in &all_requirements(levels) {
661 if !spec_le(r1, r2) {
662 continue;
663 }
664 for &level in levels {
665 if r2.satisfied_by(level) {
666 assert!(r1.satisfied_by(level), "L5 at {r1:?} ⊑ {r2:?}, {level:?}");
667 }
668 }
669 }
670 }
671 }
672
673 #[test]
676 fn l5_satisfies_is_antitone() {
677 check_l5_antitonicity(&CStandard::ALL);
678 check_l5_antitonicity(&CxxStandard::ALL);
679 }
680
681 fn check_l6_upward_closure<S: Copy + Ord + std::fmt::Debug>(levels: &[S]) {
682 for &requirement in &all_requirements(levels) {
683 for &level in levels {
684 if !requirement.satisfied_by(level) {
685 continue;
686 }
687 for &higher in levels {
688 if higher >= level {
689 assert!(
690 requirement.satisfied_by(higher),
691 "L6 at {requirement:?}, {level:?} ≤ {higher:?}"
692 );
693 }
694 }
695 }
696 }
697 }
698
699 #[test]
702 fn l6_satisfaction_sets_are_upward_closed() {
703 check_l6_upward_closure(&CStandard::ALL);
704 check_l6_upward_closure(&CxxStandard::ALL);
705 }
706
707 fn check_l7_monotone_joins<S: Copy + Ord + std::fmt::Debug>(levels: &[S]) {
708 let requirements = all_requirements(levels);
709 for superset in 0u32..(1 << requirements.len()) {
711 let mut subset = superset;
712 loop {
713 assert!(spec_le(
714 mask_join(&requirements, subset),
715 mask_join(&requirements, superset)
716 ));
717 if subset == 0 {
718 break;
719 }
720 subset = (subset - 1) & superset;
721 }
722 }
723 for &r1 in &requirements {
725 for &s1 in &requirements {
726 if !spec_le(r1, s1) {
727 continue;
728 }
729 for &r2 in &requirements {
730 for &s2 in &requirements {
731 if spec_le(r2, s2) {
732 assert!(spec_le(r1.join(r2), s1.join(s2)));
733 }
734 }
735 }
736 }
737 }
738 }
739
740 #[test]
743 fn l7_set_joins_are_monotone() {
744 check_l7_monotone_joins(&CStandard::ALL);
745 check_l7_monotone_joins(&CxxStandard::ALL);
746 }
747
748 fn c_attrs(
749 kind: DependencyKind,
750 decl: Option<InterfaceRequirement<CStandard>>,
751 implementation: Option<CStandard>,
752 ) -> DependencyAttributes {
753 DependencyAttributes {
754 kind,
755 impl_c: implementation,
756 impl_cxx: None,
757 decl_c: decl,
758 decl_cxx: None,
759 }
760 }
761
762 fn cxx_attrs(
763 kind: DependencyKind,
764 decl: Option<InterfaceRequirement<CxxStandard>>,
765 implementation: Option<CxxStandard>,
766 ) -> DependencyAttributes {
767 DependencyAttributes {
768 kind,
769 impl_c: None,
770 impl_cxx: implementation,
771 decl_c: None,
772 decl_cxx: decl,
773 }
774 }
775
776 #[test]
779 fn d9_row_1_declared_none_is_forbidden() {
780 for kind in KINDS {
781 for implementation in optional_levels(&CStandard::ALL) {
782 assert_eq!(
783 req_of_c_with_source(&c_attrs(
784 kind,
785 Some(InterfaceRequirement::None),
786 implementation
787 )),
788 (Requirement::Forbidden, ReqOfSource::DeclaredNone)
789 );
790 }
791 for implementation in optional_levels(&CxxStandard::ALL) {
792 assert_eq!(
793 req_of_cxx_with_source(&cxx_attrs(
794 kind,
795 Some(InterfaceRequirement::None),
796 implementation
797 )),
798 (Requirement::Forbidden, ReqOfSource::DeclaredNone)
799 );
800 }
801 }
802 }
803
804 #[test]
807 fn d9_row_2_explicit_declaration_wins() {
808 for kind in KINDS {
809 for min in CStandard::ALL {
810 for implementation in optional_levels(&CStandard::ALL) {
811 assert_eq!(
812 req_of_c_with_source(&c_attrs(
813 kind,
814 Some(interface_min(min)),
815 implementation
816 )),
817 (Requirement::Min(min), ReqOfSource::Declared)
818 );
819 }
820 }
821 for min in CxxStandard::ALL {
822 for implementation in optional_levels(&CxxStandard::ALL) {
823 assert_eq!(
824 req_of_cxx_with_source(&cxx_attrs(
825 kind,
826 Some(interface_min(min)),
827 implementation
828 )),
829 (Requirement::Min(min), ReqOfSource::Declared)
830 );
831 }
832 }
833 }
834 }
835
836 #[test]
839 fn d9_row_3_header_only_inference() {
840 for min in CStandard::ALL {
841 assert_eq!(
842 req_of_c_with_source(&c_attrs(DependencyKind::HeaderOnly, None, Some(min))),
843 (Requirement::Min(min), ReqOfSource::HeaderOnlyInference)
844 );
845 }
846 for min in CxxStandard::ALL {
847 assert_eq!(
848 req_of_cxx_with_source(&cxx_attrs(DependencyKind::HeaderOnly, None, Some(min))),
849 (Requirement::Min(min), ReqOfSource::HeaderOnlyInference)
850 );
851 }
852 }
853
854 #[test]
857 fn d9_row_4_compiled_absence_is_unconstrained() {
858 for implementation in CStandard::ALL {
859 assert_eq!(
860 req_of_c_with_source(&c_attrs(
861 DependencyKind::Compiled,
862 None,
863 Some(implementation)
864 )),
865 (
866 Requirement::Unconstrained,
867 ReqOfSource::CompiledNoDeclaration
868 )
869 );
870 }
871 for implementation in CxxStandard::ALL {
872 assert_eq!(
873 req_of_cxx_with_source(&cxx_attrs(
874 DependencyKind::Compiled,
875 None,
876 Some(implementation)
877 )),
878 (
879 Requirement::Unconstrained,
880 ReqOfSource::CompiledNoDeclaration
881 )
882 );
883 }
884 }
885
886 #[test]
890 fn d9_row_5_permissive_c_to_cxx_default() {
891 for kind in KINDS {
892 assert_eq!(
893 req_of_cxx_with_source(&cxx_attrs(kind, None, None)),
894 (
895 Requirement::Unconstrained,
896 ReqOfSource::CrossLanguageDefault
897 )
898 );
899 }
900 }
901
902 #[test]
905 fn d9_row_6_strict_cxx_to_c_default() {
906 for kind in KINDS {
907 assert_eq!(
908 req_of_c_with_source(&c_attrs(kind, None, None)),
909 (Requirement::Forbidden, ReqOfSource::CrossLanguageDefault)
910 );
911 }
912 }
913
914 #[test]
918 fn d13_header_only_consumer_is_vacuously_compatible() {
919 let header_only = ConsumerStandards { c: None, cxx: None };
920 for &c in &all_requirements(&CStandard::ALL) {
921 for &cxx in &all_requirements(&CxxStandard::ALL) {
922 assert!(edge_compatible(
923 header_only,
924 EffectiveRequirements { c, cxx }
925 ));
926 }
927 }
928 }
929
930 #[test]
934 fn d14_viability_is_a_conjunction_over_edges() {
935 assert!(version_viable(std::iter::empty()));
936 let compatible = (
937 ConsumerStandards {
938 c: None,
939 cxx: Some(CxxStandard::Cxx20),
940 },
941 EffectiveRequirements {
942 c: Requirement::Forbidden,
943 cxx: Requirement::Min(CxxStandard::Cxx17),
944 },
945 );
946 let incompatible = (
947 ConsumerStandards {
948 c: None,
949 cxx: Some(CxxStandard::Cxx11),
950 },
951 compatible.1,
952 );
953 assert!(version_viable([compatible]));
954 assert!(!version_viable([compatible, incompatible]));
955 }
956
957 #[test]
960 fn incompatible_standards_parses_cargo_vocabulary() {
961 assert_eq!(
962 IncompatibleStandards::default(),
963 IncompatibleStandards::Fallback
964 );
965 for value in IncompatibleStandards::ALL {
966 assert_eq!(IncompatibleStandards::parse(value.as_str()), Ok(value));
967 }
968 let err = IncompatibleStandards::parse("warn").unwrap_err();
969 assert_eq!(err.value, "warn");
970 assert!(err.to_string().contains("allow, fallback"));
971 }
972
973 #[test]
976 fn appendix_reference_table_satisfies_over_cxx_levels() {
977 let table: [(Requirement<CxxStandard>, [bool; 7]); 4] = [
978 (Requirement::Unconstrained, [true; 7]),
979 (
980 Requirement::Min(CxxStandard::Cxx17),
981 [false, false, false, true, true, true, true],
982 ),
983 (
984 Requirement::Min(CxxStandard::Cxx20),
985 [false, false, false, false, true, true, true],
986 ),
987 (Requirement::Forbidden, [false; 7]),
988 ];
989 for (requirement, cells) in table {
990 for (level, expected) in CxxStandard::ALL.into_iter().zip(cells) {
991 assert_eq!(
992 requirement.satisfied_by(level),
993 expected,
994 "{requirement:?} at {level}"
995 );
996 }
997 }
998 }
999
1000 #[test]
1003 fn appendix_example_1_declared_interface_on_compiled_target() {
1004 let z = DependencyAttributes {
1005 kind: DependencyKind::Compiled,
1006 impl_c: None,
1007 impl_cxx: Some(CxxStandard::Cxx23),
1008 decl_c: None,
1009 decl_cxx: Some(interface_min(CxxStandard::Cxx17)),
1010 };
1011 assert_eq!(req_of_cxx(&z), Requirement::Min(CxxStandard::Cxx17));
1014 let z_undeclared = DependencyAttributes {
1017 decl_cxx: None,
1018 ..z
1019 };
1020 assert_eq!(req_of_cxx(&z_undeclared), Requirement::Unconstrained);
1021 let r_z = req_of_cxx(&z).join(Requirement::join_all([]));
1024 assert_eq!(r_z, Requirement::Min(CxxStandard::Cxx17));
1025 let x = ConsumerStandards {
1028 c: None,
1029 cxx: Some(CxxStandard::Cxx17),
1030 };
1031 let z_requirements = EffectiveRequirements {
1032 c: req_of_c(&z),
1033 cxx: r_z,
1034 };
1035 assert_eq!(z_requirements.c, Requirement::Forbidden);
1036 assert!(edge_compatible(x, z_requirements));
1037 assert!(version_viable([(x, z_requirements)]));
1039 }
1040
1041 #[test]
1045 fn appendix_example_2_diamond_shared_version() {
1046 let z = cxx_attrs(
1047 DependencyKind::Compiled,
1048 Some(interface_min(CxxStandard::Cxx20)),
1049 None,
1050 );
1051 let z_requirements = EffectiveRequirements {
1052 c: req_of_c(&z),
1053 cxx: req_of_cxx(&z).join(Requirement::join_all([])),
1054 };
1055 assert_eq!(z_requirements.cxx, Requirement::Min(CxxStandard::Cxx20));
1056 let y = ConsumerStandards {
1057 c: None,
1058 cxx: Some(CxxStandard::Cxx23),
1059 };
1060 let x = ConsumerStandards {
1061 c: None,
1062 cxx: Some(CxxStandard::Cxx17),
1063 };
1064 assert!(edge_compatible(y, z_requirements));
1065 assert!(!edge_compatible(x, z_requirements));
1066 assert!(!version_viable([(y, z_requirements), (x, z_requirements)]));
1068 }
1069
1070 #[test]
1074 fn appendix_example_3_none_poisons_the_public_chain() {
1075 let b = cxx_attrs(
1076 DependencyKind::Compiled,
1077 Some(InterfaceRequirement::None),
1078 Some(CxxStandard::Cxx17),
1079 );
1080 assert_eq!(req_of_cxx(&b), Requirement::Forbidden);
1081 let a = cxx_attrs(DependencyKind::Compiled, None, Some(CxxStandard::Cxx17));
1082 assert_eq!(req_of_cxx(&a), Requirement::Unconstrained);
1083 let r_a = req_of_cxx(&a).join(req_of_cxx(&b));
1086 assert_eq!(r_a, Requirement::Forbidden);
1087 let root = ConsumerStandards {
1089 c: None,
1090 cxx: Some(CxxStandard::Cxx26),
1091 };
1092 let a_requirements = EffectiveRequirements {
1093 c: req_of_c(&a).join(req_of_c(&b)),
1094 cxx: r_a,
1095 };
1096 assert!(!edge_compatible(root, a_requirements));
1097 assert!(!version_viable([(root, a_requirements)]));
1098 let a_private = EffectiveRequirements {
1101 c: req_of_c(&a),
1102 cxx: req_of_cxx(&a),
1103 };
1104 assert!(edge_compatible(root, a_private));
1105 }
1106
1107 #[test]
1110 fn appendix_example_4_mixed_language_consumer() {
1111 let w = DependencyAttributes {
1112 kind: DependencyKind::Compiled,
1113 impl_c: Some(CStandard::C17),
1114 impl_cxx: None,
1115 decl_c: Some(interface_min(CStandard::C17)),
1116 decl_cxx: None,
1117 };
1118 let w_requirements = EffectiveRequirements {
1119 c: req_of_c(&w),
1120 cxx: req_of_cxx(&w),
1121 };
1122 assert_eq!(w_requirements.c, Requirement::Min(CStandard::C17));
1125 assert_eq!(w_requirements.cxx, Requirement::Unconstrained);
1126 let m = ConsumerStandards {
1129 c: Some(CStandard::C11),
1130 cxx: Some(CxxStandard::Cxx20),
1131 };
1132 assert!(!edge_compatible(m, w_requirements));
1133 for c in [CStandard::C17, CStandard::C23] {
1135 assert!(edge_compatible(
1136 ConsumerStandards { c: Some(c), ..m },
1137 w_requirements
1138 ));
1139 }
1140 let cxx_only = ConsumerStandards {
1142 c: None,
1143 cxx: Some(CxxStandard::Cxx20),
1144 };
1145 assert!(edge_compatible(cxx_only, w_requirements));
1146 let v = cxx_attrs(DependencyKind::Compiled, None, Some(CxxStandard::Cxx20));
1149 let v_requirements = EffectiveRequirements {
1150 c: req_of_c(&v),
1151 cxx: req_of_cxx(&v),
1152 };
1153 assert_eq!(v_requirements.c, Requirement::Forbidden);
1154 for c in CStandard::ALL {
1155 let consumer = ConsumerStandards {
1156 c: Some(c),
1157 cxx: Some(CxxStandard::Cxx20),
1158 };
1159 assert!(!edge_compatible(consumer, v_requirements));
1160 }
1161 }
1162
1163 #[test]
1166 fn appendix_example_5_header_only_inference_then_relaxation() {
1167 let h = cxx_attrs(DependencyKind::HeaderOnly, None, Some(CxxStandard::Cxx20));
1168 assert_eq!(req_of_cxx(&h), Requirement::Min(CxxStandard::Cxx20));
1170 let x = ConsumerStandards {
1171 c: None,
1172 cxx: Some(CxxStandard::Cxx17),
1173 };
1174 let h_requirements = EffectiveRequirements {
1175 c: req_of_c(&h),
1176 cxx: req_of_cxx(&h),
1177 };
1178 assert!(!edge_compatible(x, h_requirements));
1179 let h_declared = DependencyAttributes {
1182 decl_cxx: Some(interface_min(CxxStandard::Cxx17)),
1183 ..h
1184 };
1185 assert_eq!(
1186 req_of_cxx(&h_declared),
1187 Requirement::Min(CxxStandard::Cxx17)
1188 );
1189 let declared_requirements = EffectiveRequirements {
1190 c: req_of_c(&h_declared),
1191 cxx: req_of_cxx(&h_declared),
1192 };
1193 assert!(edge_compatible(x, declared_requirements));
1194 assert!(req_of_cxx(&h_declared) <= req_of_cxx(&h));
1197 }
1198}