Skip to main content

mago_type_syntax/cst/
shape.rs

1use strum::Display;
2
3use mago_span::HasSpan;
4use mago_span::Span;
5use mago_syntax_core::cst::Sequence;
6
7use crate::cst::Type;
8use crate::cst::generics::GenericParameters;
9use crate::cst::identifier::Identifier;
10use crate::cst::keyword::Keyword;
11
12#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Display)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize))]
14#[cfg_attr(feature = "serde", serde(tag = "type", content = "value"))]
15pub enum ShapeTypeKind {
16    Array,
17    NonEmptyArray,
18    AssociativeArray,
19    List,
20    NonEmptyList,
21}
22
23#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize))]
25pub struct ShapeType<'arena> {
26    pub kind: ShapeTypeKind,
27    pub keyword: Keyword<'arena>,
28    pub left_brace: Span,
29    pub fields: Sequence<'arena, ShapeField<'arena>>,
30    pub additional_fields: Option<ShapeAdditionalFields<'arena>>,
31    pub right_brace: Span,
32}
33
34#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize))]
36pub enum ShapeKey<'arena> {
37    String {
38        value: &'arena [u8],
39        span: Span,
40    },
41    Integer {
42        value: i64,
43        span: Span,
44    },
45    ClassLikeConstant {
46        class_name: Identifier<'arena>,
47        double_colon: Span,
48        constant_name: Identifier<'arena>,
49        span: Span,
50    },
51}
52
53#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55pub struct ShapeFieldKey<'arena> {
56    pub key: ShapeKey<'arena>,
57    pub question_mark: Option<Span>,
58    pub colon: Span,
59}
60
61#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize))]
63pub struct ShapeField<'arena> {
64    pub key: Option<ShapeFieldKey<'arena>>,
65    pub value: &'arena Type<'arena>,
66    pub comma: Option<Span>,
67}
68
69#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize))]
71pub struct ShapeAdditionalFields<'arena> {
72    pub ellipsis: Span,
73    pub parameters: Option<GenericParameters<'arena>>,
74    pub comma: Option<Span>,
75}
76
77impl ShapeTypeKind {
78    #[inline]
79    #[must_use]
80    pub const fn is_array(&self) -> bool {
81        matches!(self, ShapeTypeKind::Array | ShapeTypeKind::NonEmptyArray | ShapeTypeKind::AssociativeArray)
82    }
83
84    #[inline]
85    #[must_use]
86    pub const fn is_list(&self) -> bool {
87        matches!(self, ShapeTypeKind::List | ShapeTypeKind::NonEmptyList)
88    }
89
90    #[inline]
91    #[must_use]
92    pub const fn is_non_empty(&self) -> bool {
93        matches!(self, ShapeTypeKind::NonEmptyArray | ShapeTypeKind::NonEmptyList)
94    }
95}
96
97impl ShapeField<'_> {
98    #[inline]
99    #[must_use]
100    pub fn is_optional(&self) -> bool {
101        if let Some(key) = self.key.as_ref() { key.question_mark.is_some() } else { false }
102    }
103}
104
105impl ShapeType<'_> {
106    #[inline]
107    #[must_use]
108    pub fn has_fields(&self) -> bool {
109        !self.fields.is_empty()
110    }
111
112    #[inline]
113    #[must_use]
114    pub fn has_non_optional_fields(&self) -> bool {
115        self.fields.iter().any(|field| !field.is_optional())
116    }
117}
118
119impl HasSpan for ShapeType<'_> {
120    fn span(&self) -> Span {
121        self.keyword.span().join(self.right_brace)
122    }
123}
124
125impl HasSpan for ShapeKey<'_> {
126    fn span(&self) -> Span {
127        match self {
128            ShapeKey::String { span, .. } => *span,
129            ShapeKey::Integer { span, .. } => *span,
130            ShapeKey::ClassLikeConstant { span, .. } => *span,
131        }
132    }
133}
134
135impl HasSpan for ShapeFieldKey<'_> {
136    fn span(&self) -> Span {
137        self.key.span().join(self.colon)
138    }
139}
140
141impl HasSpan for ShapeField<'_> {
142    fn span(&self) -> Span {
143        if let Some(key) = &self.key {
144            if let Some(comma) = self.comma { key.span().join(comma) } else { key.span().join(self.value.span()) }
145        } else if let Some(comma) = self.comma {
146            self.value.span().join(comma)
147        } else {
148            self.value.span()
149        }
150    }
151}
152
153impl HasSpan for ShapeAdditionalFields<'_> {
154    fn span(&self) -> Span {
155        let span = match &self.parameters {
156            Some(generics) => self.ellipsis.join(generics.span()),
157            None => self.ellipsis,
158        };
159
160        if let Some(comma) = self.comma { span.join(comma) } else { span }
161    }
162}
163
164impl std::fmt::Display for ShapeKey<'_> {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        match self {
167            ShapeKey::String { value, .. } => f.write_str(&String::from_utf8_lossy(value)),
168            ShapeKey::Integer { value, .. } => write!(f, "{value}"),
169            ShapeKey::ClassLikeConstant { class_name, constant_name, .. } => {
170                write!(f, "{}::{}", class_name, constant_name)
171            }
172        }
173    }
174}
175
176impl std::fmt::Display for ShapeFieldKey<'_> {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178        write!(f, "{}{}:", self.key, self.question_mark.as_ref().map_or("", |_| "?"))
179    }
180}
181
182impl std::fmt::Display for ShapeField<'_> {
183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184        if let Some(key) = self.key.as_ref() {
185            write!(f, "{} {}", key, self.value)
186        } else {
187            write!(f, "{}", self.value)
188        }
189    }
190}
191
192impl std::fmt::Display for ShapeAdditionalFields<'_> {
193    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194        write!(f, "...")?;
195
196        if let Some(generics) = &self.parameters { write!(f, "{generics}") } else { Ok(()) }
197    }
198}
199
200impl std::fmt::Display for ShapeType<'_> {
201    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        write!(f, "{}{{", self.keyword)?;
203
204        for (i, field) in self.fields.iter().enumerate() {
205            if i > 0 {
206                write!(f, ", ")?;
207            }
208
209            write!(f, "{field}")?;
210        }
211
212        if let Some(additional_fields) = &self.additional_fields {
213            if !self.fields.is_empty() {
214                write!(f, ", ")?;
215            }
216
217            write!(f, "{additional_fields}")?;
218        }
219
220        write!(f, "}}")
221    }
222}