mago_codex/ttype/template/
variance.rs1use mago_phpdoc_syntax::cst::TemplateTagValueVariance;
2use mago_phpdoc_syntax::cst::r#type::GenericParameterVariance;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub enum Variance {
7 Invariant,
8 Covariant,
9 Contravariant,
10 Bivariant,
11}
12
13impl From<GenericParameterVariance<'_>> for Variance {
14 fn from(variance: GenericParameterVariance<'_>) -> Self {
15 match variance {
16 GenericParameterVariance::Covariant(_) => Variance::Covariant,
17 GenericParameterVariance::Contravariant(_) => Variance::Contravariant,
18 }
19 }
20}
21
22impl From<TemplateTagValueVariance> for Variance {
23 fn from(variance: TemplateTagValueVariance) -> Self {
24 match variance {
25 TemplateTagValueVariance::Invariant => Variance::Invariant,
26 TemplateTagValueVariance::Covariant => Variance::Covariant,
27 TemplateTagValueVariance::Contravariant => Variance::Contravariant,
28 }
29 }
30}
31
32impl Variance {
33 #[inline]
34 #[must_use]
35 pub const fn is_invariant(&self) -> bool {
36 matches!(self, Variance::Invariant)
37 }
38
39 #[inline]
40 #[must_use]
41 pub const fn is_covariant(&self) -> bool {
42 matches!(self, Variance::Covariant)
43 }
44
45 #[inline]
46 #[must_use]
47 pub const fn is_contravariant(&self) -> bool {
48 matches!(self, Variance::Contravariant)
49 }
50
51 #[inline]
52 #[must_use]
53 pub const fn is_bivariant(&self) -> bool {
54 matches!(self, Variance::Bivariant)
55 }
56
57 #[inline]
58 #[must_use]
59 pub const fn flip(self) -> Self {
60 match self {
61 Variance::Covariant => Variance::Contravariant,
62 Variance::Contravariant => Variance::Covariant,
63 other => other,
64 }
65 }
66
67 #[inline]
68 #[must_use]
69 pub const fn is_readonly(&self) -> bool {
70 matches!(self, Variance::Covariant | Variance::Invariant)
71 }
72
73 #[inline]
87 #[must_use]
88 pub const fn combine(outer_variance: Self, inner_variance: Self) -> Self {
89 match (outer_variance, inner_variance) {
90 (Variance::Bivariant, _) | (_, Variance::Bivariant) => Variance::Bivariant,
91 (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant,
93 (Variance::Covariant, Variance::Covariant) => Variance::Covariant,
95 (Variance::Contravariant, Variance::Contravariant) => Variance::Covariant,
97 (Variance::Covariant, Variance::Contravariant) => Variance::Contravariant,
99 (Variance::Contravariant, Variance::Covariant) => Variance::Contravariant,
101 }
102 }
103
104 #[inline]
105 #[must_use]
106 pub const fn project(self, polarity: Variance) -> Option<bool> {
107 let reads = matches!(self, Variance::Covariant | Variance::Invariant);
108 let writes = matches!(self, Variance::Contravariant | Variance::Invariant);
109
110 match polarity {
111 Variance::Contravariant => {
112 if writes {
113 Some(true)
114 } else {
115 None
116 }
117 }
118 _ => {
119 if reads {
120 Some(true)
121 } else {
122 Some(false)
123 }
124 }
125 }
126 }
127}
128
129impl std::fmt::Display for Variance {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 match self {
132 Variance::Invariant => write!(f, "invariant"),
133 Variance::Covariant => write!(f, "covariant"),
134 Variance::Contravariant => write!(f, "contravariant"),
135 Variance::Bivariant => write!(f, "*"),
136 }
137 }
138}