use crate::operations::{ContainsSymbol, contains};
use crate::scope::Scope;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
declaration::Binding,
statement::{Block, Statement},
};
use boa_interner::{Interner, ToIndentedString, 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 struct Try {
block: Block,
handler: ErrorHandler,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ErrorHandler {
Catch(Catch),
Finally(Finally),
Full(Catch, Finally),
}
impl Try {
#[inline]
#[must_use]
pub const fn new(block: Block, handler: ErrorHandler) -> Self {
Self { block, handler }
}
#[inline]
#[must_use]
pub const fn block(&self) -> &Block {
&self.block
}
#[inline]
#[must_use]
pub const fn catch(&self) -> Option<&Catch> {
match &self.handler {
ErrorHandler::Catch(c) | ErrorHandler::Full(c, _) => Some(c),
ErrorHandler::Finally(_) => None,
}
}
#[inline]
#[must_use]
pub const fn finally(&self) -> Option<&Finally> {
match &self.handler {
ErrorHandler::Finally(f) | ErrorHandler::Full(_, f) => Some(f),
ErrorHandler::Catch(_) => None,
}
}
}
impl ToIndentedString for Try {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = format!(
"{}try {}",
" ".repeat(indentation),
self.block.to_indented_string(interner, indentation)
);
if let Some(catch) = self.catch() {
buf.push_str(&catch.to_indented_string(interner, indentation));
}
if let Some(finally) = self.finally() {
buf.push_str(&finally.to_indented_string(interner, indentation));
}
buf
}
}
impl From<Try> for Statement {
#[inline]
fn from(try_catch: Try) -> Self {
Self::Try(try_catch)
}
}
impl VisitWith for Try {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
visitor.visit_block(&self.block)?;
if let Some(catch) = &self.catch() {
visitor.visit_catch(catch)?;
}
if let Some(finally) = &self.finally() {
visitor.visit_finally(finally)?;
}
ControlFlow::Continue(())
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
visitor.visit_block_mut(&mut self.block)?;
match &mut self.handler {
ErrorHandler::Catch(c) => visitor.visit_catch_mut(c)?,
ErrorHandler::Finally(f) => visitor.visit_finally_mut(f)?,
ErrorHandler::Full(c, f) => {
visitor.visit_catch_mut(c)?;
visitor.visit_finally_mut(f)?;
}
}
ControlFlow::Continue(())
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Catch {
pub(crate) parameter: Option<Binding>,
pub(crate) block: Block,
pub(crate) contains_direct_eval: bool,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) scope: Scope,
}
impl Catch {
#[inline]
#[must_use]
pub fn new(parameter: Option<Binding>, block: Block) -> Self {
let mut contains_direct_eval = contains(&block, ContainsSymbol::DirectEval);
if let Some(param) = ¶meter {
contains_direct_eval |= contains(param, ContainsSymbol::DirectEval);
}
Self {
parameter,
block,
contains_direct_eval,
scope: Scope::default(),
}
}
#[inline]
#[must_use]
pub const fn parameter(&self) -> Option<&Binding> {
self.parameter.as_ref()
}
#[inline]
#[must_use]
pub const fn block(&self) -> &Block {
&self.block
}
#[inline]
#[must_use]
pub const fn scope(&self) -> &Scope {
&self.scope
}
}
impl ToIndentedString for Catch {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = " catch".to_owned();
if let Some(param) = &self.parameter {
let _ = write!(buf, "({})", param.to_interned_string(interner));
}
let _ = write!(
buf,
" {}",
self.block.to_indented_string(interner, indentation)
);
buf
}
}
impl VisitWith for Catch {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
if let Some(binding) = &self.parameter {
visitor.visit_binding(binding)?;
}
visitor.visit_block(&self.block)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
if let Some(binding) = &mut self.parameter {
visitor.visit_binding_mut(binding)?;
}
visitor.visit_block_mut(&mut self.block)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Finally {
block: Block,
}
impl Finally {
#[inline]
#[must_use]
pub const fn block(&self) -> &Block {
&self.block
}
}
impl ToIndentedString for Finally {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
format!(
" finally {}",
self.block.to_indented_string(interner, indentation)
)
}
}
impl From<Block> for Finally {
#[inline]
fn from(block: Block) -> Self {
Self { block }
}
}
impl VisitWith for Finally {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
visitor.visit_block(&self.block)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
visitor.visit_block_mut(&mut self.block)
}
}