1use strum::Display;
2
3use mago_span::HasSpan;
4use mago_span::Span;
5use mago_syntax_core::cst::Sequence;
6
7use crate::cst::Type;
8use crate::cst::VariableType;
9use crate::cst::keyword::Keyword;
10
11#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Display)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize))]
13#[cfg_attr(feature = "serde", serde(tag = "type", content = "value"))]
14pub enum CallableTypeKind {
15 Callable,
16 PureCallable,
17 Closure,
18 PureClosure,
19}
20
21#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23pub struct CallableType<'arena> {
24 pub kind: CallableTypeKind,
25 pub keyword: Keyword<'arena>,
26 pub specification: Option<CallableTypeSpecification<'arena>>,
27}
28
29#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31pub struct CallableTypeSpecification<'arena> {
32 pub parameters: CallableTypeParameters<'arena>,
33 pub return_type: Option<CallableTypeReturnType<'arena>>,
34}
35
36#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize))]
38pub struct CallableTypeParameters<'arena> {
39 pub left_parenthesis: Span,
40 pub entries: Sequence<'arena, CallableTypeParameter<'arena>>,
41 pub right_parenthesis: Span,
42}
43
44#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize))]
46pub struct CallableTypeParameter<'arena> {
47 pub parameter_type: Option<Type<'arena>>,
48 pub ampersand: Option<Span>,
49 pub equals: Option<Span>,
50 pub ellipsis: Option<Span>,
51 pub variable: Option<VariableType<'arena>>,
52 pub comma: Option<Span>,
53}
54
55#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
56#[cfg_attr(feature = "serde", derive(serde::Serialize))]
57pub struct CallableTypeReturnType<'arena> {
58 pub colon: Span,
59 pub return_type: &'arena Type<'arena>,
60}
61
62impl CallableTypeKind {
63 #[inline]
64 #[must_use]
65 pub fn is_pure(&self) -> bool {
66 matches!(self, CallableTypeKind::PureCallable | CallableTypeKind::PureClosure)
67 }
68
69 #[inline]
70 #[must_use]
71 pub fn is_closure(&self) -> bool {
72 matches!(self, CallableTypeKind::Closure | CallableTypeKind::PureClosure)
73 }
74}
75
76impl CallableTypeParameter<'_> {
77 #[inline]
78 #[must_use]
79 pub const fn is_variadic(&self) -> bool {
80 self.ellipsis.is_some()
81 }
82
83 #[inline]
84 #[must_use]
85 pub const fn is_optional(&self) -> bool {
86 self.equals.is_some()
87 }
88
89 #[inline]
90 #[must_use]
91 pub const fn is_by_reference(&self) -> bool {
92 self.ampersand.is_some()
93 }
94}
95
96impl HasSpan for CallableType<'_> {
97 fn span(&self) -> Span {
98 match &self.specification {
99 Some(specification) => self.keyword.span.join(specification.span()),
100 None => self.keyword.span,
101 }
102 }
103}
104
105impl HasSpan for CallableTypeSpecification<'_> {
106 fn span(&self) -> Span {
107 match &self.return_type {
108 Some(return_type) => self.parameters.span().join(return_type.span()),
109 None => self.parameters.span(),
110 }
111 }
112}
113
114impl HasSpan for CallableTypeParameters<'_> {
115 fn span(&self) -> Span {
116 self.left_parenthesis.join(self.right_parenthesis)
117 }
118}
119
120impl HasSpan for CallableTypeParameter<'_> {
121 fn span(&self) -> Span {
122 let start = match &self.parameter_type {
123 Some(parameter_type) => parameter_type.span(),
124 None => self
125 .ampersand
126 .or(self.equals)
127 .or(self.ellipsis)
128 .or(self.variable.as_ref().map(mago_span::HasSpan::span))
129 .or(self.comma)
130 .unwrap_or_else(Span::zero),
131 };
132
133 let end = self
134 .comma
135 .or(self.variable.as_ref().map(mago_span::HasSpan::span))
136 .or(self.ellipsis)
137 .or(self.equals)
138 .or(self.ampersand)
139 .unwrap_or(start);
140
141 start.join(end)
142 }
143}
144
145impl HasSpan for CallableTypeReturnType<'_> {
146 fn span(&self) -> Span {
147 self.colon.join(self.return_type.span())
148 }
149}
150
151impl std::fmt::Display for CallableTypeReturnType<'_> {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 write!(f, ": {}", self.return_type)
154 }
155}
156
157impl std::fmt::Display for CallableTypeParameter<'_> {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 if let Some(parameter_type) = &self.parameter_type {
160 write!(f, "{parameter_type}")?;
161 }
162
163 if self.ampersand.is_some() {
164 write!(f, " &")?;
165 }
166
167 if self.equals.is_some() {
168 write!(f, "=")?;
169 } else if self.ellipsis.is_some() {
170 write!(f, "...")?;
171 } else {
172 }
174
175 if let Some(variable) = &self.variable {
176 write!(f, " {variable}")?;
177 }
178
179 Ok(())
180 }
181}
182
183impl std::fmt::Display for CallableTypeParameters<'_> {
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 write!(f, "(")?;
186 for (i, entry) in self.entries.iter().enumerate() {
187 if i > 0 {
188 write!(f, ", ")?;
189 }
190 write!(f, "{entry}")?;
191 }
192 write!(f, ")")?;
193 Ok(())
194 }
195}
196
197impl std::fmt::Display for CallableTypeSpecification<'_> {
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 write!(f, "{}", self.parameters)?;
200 if let Some(return_type) = &self.return_type {
201 write!(f, "{return_type}")?;
202 }
203 Ok(())
204 }
205}
206
207impl std::fmt::Display for CallableType<'_> {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 write!(f, "{}", self.keyword)?;
210 if let Some(specification) = &self.specification {
211 write!(f, "{specification}")?;
212 }
213 Ok(())
214 }
215}