mago-type-syntax 1.25.2

Provides core utilities useful for building lexers and parsers within Mago.
Documentation
use serde::Serialize;
use strum::Display;

use mago_span::HasSpan;
use mago_span::Span;
use mago_syntax_core::ast::Sequence;

use crate::ast::Type;
use crate::ast::VariableType;
use crate::ast::keyword::Keyword;

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
#[serde(tag = "type", content = "value")]
pub enum CallableTypeKind {
    Callable,
    PureCallable,
    Closure,
    PureClosure,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
pub struct CallableType<'arena> {
    pub kind: CallableTypeKind,
    pub keyword: Keyword<'arena>,
    pub specification: Option<CallableTypeSpecification<'arena>>,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
pub struct CallableTypeSpecification<'arena> {
    pub parameters: CallableTypeParameters<'arena>,
    pub return_type: Option<CallableTypeReturnType<'arena>>,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
pub struct CallableTypeParameters<'arena> {
    pub left_parenthesis: Span,
    pub entries: Sequence<'arena, CallableTypeParameter<'arena>>,
    pub right_parenthesis: Span,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
pub struct CallableTypeParameter<'arena> {
    pub parameter_type: Option<Type<'arena>>,
    pub equals: Option<Span>,
    pub ellipsis: Option<Span>,
    pub variable: Option<VariableType<'arena>>,
    pub comma: Option<Span>,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
pub struct CallableTypeReturnType<'arena> {
    pub colon: Span,
    pub return_type: &'arena Type<'arena>,
}

impl CallableTypeKind {
    #[inline]
    #[must_use]
    pub fn is_pure(&self) -> bool {
        matches!(self, CallableTypeKind::PureCallable | CallableTypeKind::PureClosure)
    }

    #[inline]
    #[must_use]
    pub fn is_closure(&self) -> bool {
        matches!(self, CallableTypeKind::Closure | CallableTypeKind::PureClosure)
    }
}

impl CallableTypeParameter<'_> {
    #[inline]
    #[must_use]
    pub const fn is_variadic(&self) -> bool {
        self.ellipsis.is_some()
    }

    #[inline]
    #[must_use]
    pub const fn is_optional(&self) -> bool {
        self.equals.is_some()
    }
}

impl HasSpan for CallableType<'_> {
    fn span(&self) -> Span {
        match &self.specification {
            Some(specification) => self.keyword.span.join(specification.span()),
            None => self.keyword.span,
        }
    }
}

impl HasSpan for CallableTypeSpecification<'_> {
    fn span(&self) -> Span {
        match &self.return_type {
            Some(return_type) => self.parameters.span().join(return_type.span()),
            None => self.parameters.span(),
        }
    }
}

impl HasSpan for CallableTypeParameters<'_> {
    fn span(&self) -> Span {
        self.left_parenthesis.join(self.right_parenthesis)
    }
}

impl HasSpan for CallableTypeParameter<'_> {
    fn span(&self) -> Span {
        let start = match &self.parameter_type {
            Some(parameter_type) => parameter_type.span(),
            None => self
                .equals
                .or(self.ellipsis)
                .or(self.variable.as_ref().map(mago_span::HasSpan::span))
                .or(self.comma)
                .unwrap(),
        };

        let end = self
            .comma
            .or(self.variable.as_ref().map(mago_span::HasSpan::span))
            .or(self.ellipsis)
            .or(self.equals)
            .unwrap_or(start);

        start.join(end)
    }
}

impl HasSpan for CallableTypeReturnType<'_> {
    fn span(&self) -> Span {
        self.colon.join(self.return_type.span())
    }
}

impl std::fmt::Display for CallableTypeReturnType<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, ": {}", self.return_type)
    }
}

impl std::fmt::Display for CallableTypeParameter<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(parameter_type) = &self.parameter_type {
            write!(f, "{parameter_type}")?;
        }

        if self.equals.is_some() {
            write!(f, "=")?;
        } else if self.ellipsis.is_some() {
            write!(f, "...")?;
        }

        if let Some(variable) = &self.variable {
            write!(f, " {variable}")?;
        }

        Ok(())
    }
}

impl std::fmt::Display for CallableTypeParameters<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "(")?;
        for (i, entry) in self.entries.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            write!(f, "{entry}")?;
        }
        write!(f, ")")?;
        Ok(())
    }
}

impl std::fmt::Display for CallableTypeSpecification<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.parameters)?;
        if let Some(return_type) = &self.return_type {
            write!(f, "{return_type}")?;
        }
        Ok(())
    }
}

impl std::fmt::Display for CallableType<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.keyword)?;
        if let Some(specification) = &self.specification {
            write!(f, "{specification}")?;
        }
        Ok(())
    }
}