use super::{Expression, access::PropertyAccessField};
use crate::{
Span, Spanned,
function::PrivateName,
join_nodes,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToInternedString};
use core::{fmt::Write as _, ops::ControlFlow};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum OptionalOperationKind {
SimplePropertyAccess {
field: PropertyAccessField,
},
PrivatePropertyAccess {
field: PrivateName,
},
Call {
args: Box<[Expression]>,
},
}
impl VisitWith for OptionalOperationKind {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::SimplePropertyAccess { field } => visitor.visit_property_access_field(field),
Self::PrivatePropertyAccess { field } => visitor.visit_private_name(field),
Self::Call { args } => {
for arg in args {
visitor.visit_expression(arg)?;
}
ControlFlow::Continue(())
}
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::SimplePropertyAccess { field } => visitor.visit_property_access_field_mut(field),
Self::PrivatePropertyAccess { field } => visitor.visit_private_name_mut(field),
Self::Call { args } => {
for arg in args.iter_mut() {
visitor.visit_expression_mut(arg)?;
}
ControlFlow::Continue(())
}
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct OptionalOperation {
kind: OptionalOperationKind,
shorted: bool,
span: Span,
}
impl OptionalOperation {
#[inline]
#[must_use]
pub const fn new(kind: OptionalOperationKind, shorted: bool, span: Span) -> Self {
Self {
kind,
shorted,
span,
}
}
#[inline]
#[must_use]
pub const fn kind(&self) -> &OptionalOperationKind {
&self.kind
}
#[inline]
#[must_use]
pub const fn shorted(&self) -> bool {
self.shorted
}
}
impl Spanned for OptionalOperation {
#[inline]
fn span(&self) -> Span {
self.span
}
}
impl ToInternedString for OptionalOperation {
fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = if self.shorted {
String::from("?.")
} else {
if let OptionalOperationKind::SimplePropertyAccess {
field: PropertyAccessField::Const(name),
} = &self.kind
{
return format!(".{}", interner.resolve_expect(name.sym()));
}
if let OptionalOperationKind::PrivatePropertyAccess { field } = &self.kind {
return format!(".#{}", interner.resolve_expect(field.description()));
}
String::new()
};
match &self.kind {
OptionalOperationKind::SimplePropertyAccess { field } => match field {
PropertyAccessField::Const(name) => {
buf.push_str(&interner.resolve_expect(name.sym()).to_string());
}
PropertyAccessField::Expr(expr) => {
let _ = write!(buf, "[{}]", expr.to_interned_string(interner));
}
},
OptionalOperationKind::PrivatePropertyAccess { field } => {
let _ = write!(buf, "#{}", interner.resolve_expect(field.description()));
}
OptionalOperationKind::Call { args } => {
let _ = write!(buf, "({})", join_nodes(interner, args));
}
}
buf
}
}
impl VisitWith for OptionalOperation {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
visitor.visit_optional_operation_kind(&self.kind)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
visitor.visit_optional_operation_kind_mut(&mut self.kind)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Optional {
target: Box<Expression>,
chain: Box<[OptionalOperation]>,
span: Span,
}
impl Optional {
#[inline]
#[must_use]
pub fn new(target: Expression, chain: Box<[OptionalOperation]>, span: Span) -> Self {
Self {
target: Box::new(target),
chain,
span,
}
}
#[inline]
#[must_use]
pub fn target(&self) -> &Expression {
self.target.as_ref()
}
#[inline]
#[must_use]
pub fn chain(&self) -> &[OptionalOperation] {
self.chain.as_ref()
}
}
impl Spanned for Optional {
#[inline]
fn span(&self) -> Span {
self.span
}
}
impl From<Optional> for Expression {
fn from(opt: Optional) -> Self {
Self::Optional(opt)
}
}
impl ToInternedString for Optional {
fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = self.target.to_interned_string(interner);
for item in &*self.chain {
buf.push_str(&item.to_interned_string(interner));
}
buf
}
}
impl VisitWith for Optional {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
visitor.visit_expression(&self.target)?;
for op in &*self.chain {
visitor.visit_optional_operation(op)?;
}
ControlFlow::Continue(())
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
visitor.visit_expression_mut(&mut self.target)?;
for op in &mut *self.chain {
visitor.visit_optional_operation_mut(op)?;
}
ControlFlow::Continue(())
}
}