Skip to main content

mago_codex/ttype/template/
variance.rs

1use 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_bivariant(&self) -> bool {
48        matches!(self, Variance::Bivariant)
49    }
50
51    #[inline]
52    #[must_use]
53    pub const fn flip(self) -> Self {
54        match self {
55            Variance::Covariant => Variance::Contravariant,
56            Variance::Contravariant => Variance::Covariant,
57            other => other,
58        }
59    }
60
61    #[inline]
62    #[must_use]
63    pub const fn is_readonly(&self) -> bool {
64        matches!(self, Variance::Covariant | Variance::Invariant)
65    }
66
67    #[inline]
68    #[must_use]
69    pub const fn project(self, polarity: Variance) -> Option<bool> {
70        let reads = matches!(self, Variance::Covariant | Variance::Invariant);
71        let writes = matches!(self, Variance::Contravariant | Variance::Invariant);
72
73        match polarity {
74            Variance::Contravariant => {
75                if writes {
76                    Some(true)
77                } else {
78                    None
79                }
80            }
81            _ => {
82                if reads {
83                    Some(true)
84                } else {
85                    Some(false)
86                }
87            }
88        }
89    }
90}
91
92impl std::fmt::Display for Variance {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        match self {
95            Variance::Invariant => write!(f, "invariant"),
96            Variance::Covariant => write!(f, "covariant"),
97            Variance::Contravariant => write!(f, "contravariant"),
98            Variance::Bivariant => write!(f, "*"),
99        }
100    }
101}