Skip to main content

mago_type_syntax/ast/
shape.rs

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