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 struct ShapeFieldKey<'input> {
33    pub name: Box<Type<'input>>,
34    pub question_mark: Option<Span>,
35    pub colon: Span,
36}
37
38#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
39pub struct ShapeField<'input> {
40    pub key: Option<ShapeFieldKey<'input>>,
41    pub value: Box<Type<'input>>,
42    pub comma: Option<Span>,
43}
44
45#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
46pub struct ShapeAdditionalFields<'input> {
47    pub ellipsis: Span,
48    pub parameters: Option<GenericParameters<'input>>,
49}
50
51impl ShapeTypeKind {
52    #[inline]
53    pub const fn is_array(&self) -> bool {
54        matches!(self, ShapeTypeKind::Array | ShapeTypeKind::NonEmptyArray | ShapeTypeKind::AssociativeArray)
55    }
56
57    #[inline]
58    pub const fn is_list(&self) -> bool {
59        matches!(self, ShapeTypeKind::List | ShapeTypeKind::NonEmptyList)
60    }
61
62    #[inline]
63    pub const fn is_non_empty(&self) -> bool {
64        matches!(self, ShapeTypeKind::NonEmptyArray | ShapeTypeKind::NonEmptyList)
65    }
66}
67
68impl ShapeField<'_> {
69    #[inline]
70    pub fn is_optional(&self) -> bool {
71        if let Some(key) = self.key.as_ref() { key.question_mark.is_some() } else { false }
72    }
73}
74
75impl ShapeType<'_> {
76    #[inline]
77    pub fn has_fields(&self) -> bool {
78        !self.fields.is_empty()
79    }
80
81    #[inline]
82    pub fn has_non_optional_fields(&self) -> bool {
83        self.fields.iter().any(|field| !field.is_optional())
84    }
85}
86
87impl HasSpan for ShapeType<'_> {
88    fn span(&self) -> Span {
89        self.keyword.span().join(self.right_brace)
90    }
91}
92
93impl HasSpan for ShapeFieldKey<'_> {
94    fn span(&self) -> Span {
95        self.name.span().join(self.colon)
96    }
97}
98
99impl HasSpan for ShapeField<'_> {
100    fn span(&self) -> Span {
101        if let Some(key) = &self.key {
102            if let Some(comma) = self.comma { key.span().join(comma) } else { key.span().join(self.value.span()) }
103        } else if let Some(comma) = self.comma {
104            self.value.span().join(comma)
105        } else {
106            self.value.span()
107        }
108    }
109}
110
111impl HasSpan for ShapeAdditionalFields<'_> {
112    fn span(&self) -> Span {
113        match &self.parameters {
114            Some(generics) => self.ellipsis.join(generics.span()),
115            None => self.ellipsis,
116        }
117    }
118}
119
120impl std::fmt::Display for ShapeFieldKey<'_> {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(f, "{}{}:", self.name, self.question_mark.as_ref().map_or("", |_| "?"))
123    }
124}
125
126impl std::fmt::Display for ShapeField<'_> {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        if let Some(key) = self.key.as_ref() {
129            write!(f, "{} {}", key, self.value)
130        } else {
131            write!(f, "{}", self.value)
132        }
133    }
134}
135
136impl std::fmt::Display for ShapeAdditionalFields<'_> {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        write!(f, "...")?;
139
140        if let Some(generics) = &self.parameters { write!(f, "{generics}") } else { Ok(()) }
141    }
142}
143
144impl std::fmt::Display for ShapeType<'_> {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        write!(f, "{}{{", self.keyword)?;
147
148        for (i, field) in self.fields.iter().enumerate() {
149            if i > 0 {
150                write!(f, ", ")?;
151            }
152
153            write!(f, "{field}")?;
154        }
155
156        if let Some(additional_fields) = &self.additional_fields {
157            if !self.fields.is_empty() {
158                write!(f, ", ")?;
159            }
160
161            write!(f, "{additional_fields}")?;
162        }
163
164        write!(f, "}}")
165    }
166}