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
//! Binding patterns used in destructuring, parameter lists, and
//! variable-declarator targets.

use crate::expression::{Expression, PropertyKey};
use crate::identifier::Identifier;
use crate::span::Spanned;

/// A binding pattern paired with its source span.
pub type Pattern = Spanned<PatternKind>;

/// The shape of an ECMAScript binding pattern.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PatternKind {
    /// A plain identifier binding (`let x = ...`).
    Identifier(Identifier),
    /// An array-destructuring pattern (`let [a, , b, ...rest] = ...`).
    /// `None` entries are holes; the final entry may be a `Rest` variant.
    Array {
        /// Element patterns; `None` for sparse holes.
        elements: Vec<Option<Pattern>>,
    },
    /// An object-destructuring pattern (`let { a, b: c, ...rest } = ...`).
    Object {
        /// Properties and an optional final rest element.
        properties: Vec<ObjectPatternMember>,
    },
    /// A rest element used inside [`PatternKind::Array`] or as the final
    /// formal parameter (`...args`).
    Rest {
        /// The pattern collecting the remaining elements.
        argument: Box<Pattern>,
    },
    /// A pattern with a default value, used in destructuring or as a default
    /// formal parameter (`{ a = 1 }`, `(x = 0) =>`).
    Assignment {
        /// The pattern receiving the value when one is supplied.
        left: Box<Pattern>,
        /// The default expression to use when no value is supplied.
        right: Box<Expression>,
    },
}

/// One member of an object-destructuring pattern.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ObjectPatternMember {
    /// A `key: value` destructuring property.  When `shorthand` is true the
    /// key and value are derived from the same identifier (`{ a }` rather
    /// than `{ a: a }`); when `computed` is true the key was written in
    /// brackets (`{ [k]: value }`).
    Property {
        /// The key (identifier, string, number, computed expression, or
        /// private identifier).
        key: PropertyKey,
        /// The pattern receiving the corresponding value.
        value: Pattern,
        /// Whether the key was a computed expression (`[expr]`).
        computed: bool,
        /// Whether the property used shorthand (`{ a }`).
        shorthand: bool,
    },
    /// A rest property (`{ ...rest }`).
    Rest {
        /// The pattern collecting the remaining properties.
        argument: Pattern,
    },
}

impl std::fmt::Display for PatternKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Identifier(name) => write!(f, "{name}"),
            Self::Array { elements } => write_array_pattern(f, elements),
            Self::Object { properties } => write_object_pattern(f, properties),
            Self::Rest { argument } => write!(f, "...{argument}"),
            Self::Assignment { left, right } => write!(f, "{left} = {right}"),
        }
    }
}

fn write_array_pattern(
    f: &mut std::fmt::Formatter<'_>,
    elements: &[Option<Pattern>],
) -> std::fmt::Result {
    let body = elements
        .iter()
        .map(|element| {
            element
                .as_ref()
                .map_or_else(String::new, |pat| format!("{pat}"))
        })
        .collect::<Vec<_>>()
        .join(", ");
    write!(f, "[{body}]")
}

fn write_object_pattern(
    f: &mut std::fmt::Formatter<'_>,
    properties: &[ObjectPatternMember],
) -> std::fmt::Result {
    let body = properties
        .iter()
        .map(|member| format!("{member}"))
        .collect::<Vec<_>>()
        .join(", ");
    write!(f, "{{{body}}}")
}

impl std::fmt::Display for ObjectPatternMember {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Property {
                key,
                value,
                computed,
                shorthand,
            } => write_object_pattern_property(f, key, value, *computed, *shorthand),
            Self::Rest { argument } => write!(f, "...{argument}"),
        }
    }
}

fn write_object_pattern_property(
    f: &mut std::fmt::Formatter<'_>,
    key: &PropertyKey,
    value: &Pattern,
    computed: bool,
    shorthand: bool,
) -> std::fmt::Result {
    if shorthand {
        write!(f, "{value}")
    } else if computed {
        write!(f, "[{key}]: {value}")
    } else {
        write!(f, "{key}: {value}")
    }
}