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}
56
57impl ShapeTypeKind {
58 #[inline]
59 pub const fn is_array(&self) -> bool {
60 matches!(self, ShapeTypeKind::Array | ShapeTypeKind::NonEmptyArray | ShapeTypeKind::AssociativeArray)
61 }
62
63 #[inline]
64 pub const fn is_list(&self) -> bool {
65 matches!(self, ShapeTypeKind::List | ShapeTypeKind::NonEmptyList)
66 }
67
68 #[inline]
69 pub const fn is_non_empty(&self) -> bool {
70 matches!(self, ShapeTypeKind::NonEmptyArray | ShapeTypeKind::NonEmptyList)
71 }
72}
73
74impl ShapeField<'_> {
75 #[inline]
76 pub fn is_optional(&self) -> bool {
77 if let Some(key) = self.key.as_ref() { key.question_mark.is_some() } else { false }
78 }
79}
80
81impl ShapeType<'_> {
82 #[inline]
83 pub fn has_fields(&self) -> bool {
84 !self.fields.is_empty()
85 }
86
87 #[inline]
88 pub fn has_non_optional_fields(&self) -> bool {
89 self.fields.iter().any(|field| !field.is_optional())
90 }
91}
92
93impl HasSpan for ShapeType<'_> {
94 fn span(&self) -> Span {
95 self.keyword.span().join(self.right_brace)
96 }
97}
98
99impl HasSpan for ShapeKey<'_> {
100 fn span(&self) -> Span {
101 match self {
102 ShapeKey::String { span, .. } => *span,
103 ShapeKey::Integer { span, .. } => *span,
104 }
105 }
106}
107
108impl HasSpan for ShapeFieldKey<'_> {
109 fn span(&self) -> Span {
110 self.key.span().join(self.colon)
111 }
112}
113
114impl HasSpan for ShapeField<'_> {
115 fn span(&self) -> Span {
116 if let Some(key) = &self.key {
117 if let Some(comma) = self.comma { key.span().join(comma) } else { key.span().join(self.value.span()) }
118 } else if let Some(comma) = self.comma {
119 self.value.span().join(comma)
120 } else {
121 self.value.span()
122 }
123 }
124}
125
126impl HasSpan for ShapeAdditionalFields<'_> {
127 fn span(&self) -> Span {
128 match &self.parameters {
129 Some(generics) => self.ellipsis.join(generics.span()),
130 None => self.ellipsis,
131 }
132 }
133}
134
135impl std::fmt::Display for ShapeKey<'_> {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 ShapeKey::String { value, .. } => write!(f, "{}", value),
139 ShapeKey::Integer { value, .. } => write!(f, "{}", value),
140 }
141 }
142}
143
144impl std::fmt::Display for ShapeFieldKey<'_> {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 write!(f, "{}{}:", self.key, self.question_mark.as_ref().map_or("", |_| "?"))
147 }
148}
149
150impl std::fmt::Display for ShapeField<'_> {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 if let Some(key) = self.key.as_ref() {
153 write!(f, "{} {}", key, self.value)
154 } else {
155 write!(f, "{}", self.value)
156 }
157 }
158}
159
160impl std::fmt::Display for ShapeAdditionalFields<'_> {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 write!(f, "...")?;
163
164 if let Some(generics) = &self.parameters { write!(f, "{generics}") } else { Ok(()) }
165 }
166}
167
168impl std::fmt::Display for ShapeType<'_> {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 write!(f, "{}{{", self.keyword)?;
171
172 for (i, field) in self.fields.iter().enumerate() {
173 if i > 0 {
174 write!(f, ", ")?;
175 }
176
177 write!(f, "{field}")?;
178 }
179
180 if let Some(additional_fields) = &self.additional_fields {
181 if !self.fields.is_empty() {
182 write!(f, ", ")?;
183 }
184
185 write!(f, "{additional_fields}")?;
186 }
187
188 write!(f, "}}")
189 }
190}