ecma-syntax-cat 0.1.0

ECMAScript abstract syntax tree as comp-cat-rs-idiomatic Rust types. ESTree-shaped, ES2024-complete, no panics, no Rc, no interior mutability. Foundation crate for boa-cat and related downstream tooling.
Documentation
//! Class definitions and member kinds.

use crate::expression::{Expression, PropertyKey};
use crate::function::Function;
use crate::identifier::Identifier;
use crate::statement::Statement;

/// An ECMAScript class definition.  Shared between [`ClassDeclaration`] and
/// [`ClassExpression`].
///
/// [`ClassDeclaration`]: crate::statement::StatementKind::ClassDeclaration
/// [`ClassExpression`]: crate::expression::ExpressionKind::ClassExpression
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(clippy::struct_field_names)] // super_class mirrors the ESTree field name
pub struct Class {
    id: Option<Identifier>,
    super_class: Option<Box<Expression>>,
    body: Vec<ClassMember>,
}

impl Class {
    /// Build a class definition.
    #[must_use]
    pub fn new(
        id: Option<Identifier>,
        super_class: Option<Expression>,
        body: Vec<ClassMember>,
    ) -> Self {
        Self {
            id,
            super_class: super_class.map(Box::new),
            body,
        }
    }

    /// The class's name, if any.  Declarations always name it; expressions
    /// may be anonymous.
    #[must_use]
    pub fn id(&self) -> Option<&Identifier> {
        self.id.as_ref()
    }

    /// The optional `extends` clause expression.
    #[must_use]
    pub fn super_class(&self) -> Option<&Expression> {
        self.super_class.as_deref()
    }

    /// The class body members in source order.
    #[must_use]
    pub fn body(&self) -> &[ClassMember] {
        &self.body
    }
}

impl std::fmt::Display for Class {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("class")?;
        if let Some(name) = &self.id {
            write!(f, " {name}")?;
        }
        if let Some(super_expr) = &self.super_class {
            write!(f, " extends {super_expr}")?;
        }
        f.write_str(" { ... }")
    }
}

/// One member of a class body.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ClassMember {
    /// A method, getter, setter, or constructor.
    Method {
        /// The method key (identifier, string, number, computed, or private).
        key: PropertyKey,
        /// The function definition.
        value: Function,
        /// What kind of method it is.
        kind: MethodKind,
        /// Whether the member is `static`.
        is_static: bool,
        /// Whether the key was a computed expression (`[k]`).
        computed: bool,
    },
    /// A field declaration (`x = 1;` or `static y = 2;`).
    Property {
        /// The property key.
        key: PropertyKey,
        /// Optional initialiser expression.
        value: Option<Expression>,
        /// Whether the field is `static`.
        is_static: bool,
        /// Whether the key was a computed expression.
        computed: bool,
    },
    /// A static initialiser block (`static { ... }`).
    StaticBlock {
        /// Statements in the block.
        body: Vec<Statement>,
    },
}

/// Distinguishes the four method-like class members.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MethodKind {
    /// A constructor.
    Constructor,
    /// A regular method.
    Method,
    /// A getter (`get name() { ... }`).
    Get,
    /// A setter (`set name(v) { ... }`).
    Set,
}

impl std::fmt::Display for MethodKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            Self::Constructor => "constructor",
            Self::Method => "method",
            Self::Get => "get",
            Self::Set => "set",
        })
    }
}