mago_type_syntax/ast/
callable.rs

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