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