use oxc::{
allocator::TakeIn as _,
ast::ast::{
ArrayExpressionElement, AssignmentTarget, AssignmentTargetMaybeDefault, BindingPattern,
Expression, ObjectPropertyKind, PropertyKind,
},
span::SPAN,
};
use crate::AstFactory;
use super::binding_property_ext::BindingPropertyExt as _;
pub trait BindingPatternExt<'ast> {
fn into_assignment_target(self, ast_factory: &AstFactory<'ast>) -> AssignmentTarget<'ast>;
fn into_expression(self, ast_factory: &AstFactory<'ast>) -> Expression<'ast>;
}
impl<'ast> BindingPatternExt<'ast> for BindingPattern<'ast> {
fn into_assignment_target(self, ast_factory: &AstFactory<'ast>) -> AssignmentTarget<'ast> {
match self {
BindingPattern::BindingIdentifier(id) => AssignmentTarget::AssignmentTargetIdentifier(
ast_factory.alloc_identifier_reference(id.span, id.name),
),
BindingPattern::ObjectPattern(mut obj_pat) => {
let rest = obj_pat.rest.take().map(|rest| {
ast_factory.alloc_assignment_target_rest(
rest.span,
rest.unbox().argument.into_assignment_target(ast_factory),
)
});
let mut properties = ast_factory.vec_with_capacity(obj_pat.properties.len());
obj_pat.properties.take_in(ast_factory.allocator).into_iter().for_each(|binding_prop| {
properties.push(binding_prop.into_assignment_target_property(ast_factory));
});
AssignmentTarget::ObjectAssignmentTarget(
ast_factory.alloc_object_assignment_target(SPAN, properties, rest),
)
}
BindingPattern::ArrayPattern(mut arr_pat) => {
let rest = arr_pat.rest.take().map(|rest| {
ast_factory.alloc_assignment_target_rest(
rest.span,
rest.unbox().argument.into_assignment_target(ast_factory),
)
});
let mut elements = ast_factory.vec_with_capacity(arr_pat.elements.len());
arr_pat.elements.take_in(ast_factory.allocator).into_iter().for_each(|binding_pat| {
elements.push(binding_pat.map(|binding_pat| match binding_pat {
BindingPattern::AssignmentPattern(assign_pat) => {
let assign_pat = assign_pat.unbox();
AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(
ast_factory.alloc_assignment_target_with_default(
assign_pat.span,
assign_pat.left.into_assignment_target(ast_factory),
assign_pat.right,
),
)
}
_ => {
AssignmentTargetMaybeDefault::from(binding_pat.into_assignment_target(ast_factory))
}
}));
});
AssignmentTarget::ArrayAssignmentTarget(ast_factory.alloc_array_assignment_target(
arr_pat.span,
elements,
rest,
))
}
BindingPattern::AssignmentPattern(_) => {
unreachable!("`BindingPattern::AssignmentPattern` should be pre-handled in above")
}
}
}
fn into_expression(self, ast_factory: &AstFactory<'ast>) -> Expression<'ast> {
match self {
BindingPattern::BindingIdentifier(id) => ast_factory.expression_identifier(SPAN, id.name),
BindingPattern::ObjectPattern(mut obj_pat) => {
let capacity = obj_pat.properties.len() + usize::from(obj_pat.rest.is_some());
let mut properties = ast_factory.vec_with_capacity(capacity);
obj_pat.properties.take_in(ast_factory.allocator).into_iter().for_each(|binding_prop| {
properties.push(ObjectPropertyKind::ObjectProperty(ast_factory.alloc_object_property(
SPAN,
PropertyKind::Init,
binding_prop.key,
binding_prop.value.into_expression(ast_factory),
false,
binding_prop.shorthand,
binding_prop.computed,
)));
});
if let Some(rest) = obj_pat.rest.take() {
let BindingPattern::BindingIdentifier(ref id) = rest.argument else {
unreachable!("The rest element should be `BindingIdentifier`")
};
properties.push(ObjectPropertyKind::ObjectProperty(ast_factory.alloc_object_property(
SPAN,
PropertyKind::Init,
ast_factory.property_key_static_identifier(SPAN, id.name),
ast_factory.expression_identifier(SPAN, id.name),
false,
true,
false,
)));
}
Expression::ObjectExpression(ast_factory.alloc_object_expression(SPAN, properties))
}
BindingPattern::ArrayPattern(mut arg_pat) => {
let capacity = arg_pat.elements.len() + usize::from(arg_pat.rest.is_some());
let mut elements = ast_factory.vec_with_capacity(capacity);
arg_pat.elements.take_in(ast_factory.allocator).into_iter().for_each(|binding_pat| {
elements.push(binding_pat.map_or(
ArrayExpressionElement::Elision(ast_factory.alloc_elision(SPAN)),
|binding_pat| ArrayExpressionElement::from(binding_pat.into_expression(ast_factory)),
));
});
if let Some(rest) = arg_pat.rest.take() {
let BindingPattern::BindingIdentifier(ref id) = rest.argument else {
unreachable!("The rest element should be `BindingIdentifier`")
};
elements.push(ArrayExpressionElement::Identifier(
ast_factory.alloc_identifier_reference(SPAN, id.name),
));
}
Expression::ArrayExpression(ast_factory.alloc_array_expression(SPAN, elements))
}
BindingPattern::AssignmentPattern(mut assign_pat) => {
assign_pat.left.take_in(ast_factory.allocator).into_expression(ast_factory)
}
}
}
}