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
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}