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