Skip to main content

mago_type_syntax/cst/
callable.rs

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            // No default marker: parameter is required and not variadic.
173        }
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}