use crate::{
derive_ASTNode, ASTNode, Block, ParseError, ParseErrors, TSXKeyword, TSXToken, TypeAnnotation,
VariableField, WithComment,
};
use source_map::Span;
use tokenizer_lib::Token;
use visitable_derive::Visitable;
#[cfg_attr(target_family = "wasm", tsify::declare)]
pub type ExceptionVarField = WithComment<VariableField>;
#[apply(derive_ASTNode)]
#[derive(Debug, PartialEq, Clone, Visitable, get_field_by_type::GetFieldByType)]
#[get_field_by_type_target(Span)]
pub struct TryCatchStatement {
pub try_inner: Block,
pub catch_inner: Option<Block>,
pub exception_var: Option<(ExceptionVarField, Option<TypeAnnotation>)>,
pub finally_inner: Option<Block>,
pub position: Span,
}
impl ASTNode for TryCatchStatement {
fn get_position(&self) -> Span {
self.position
}
fn from_reader(
reader: &mut impl tokenizer_lib::TokenReader<TSXToken, crate::TokenStart>,
state: &mut crate::ParsingState,
options: &crate::ParseOptions,
) -> Result<Self, crate::ParseError> {
let start = state.expect_keyword(reader, TSXKeyword::Try)?;
let try_inner = Block::from_reader(reader, state, options)?;
let mut catch_inner: Option<Block> = None;
let mut exception_var: Option<(ExceptionVarField, Option<TypeAnnotation>)> = None;
if let Some(Token(TSXToken::Keyword(TSXKeyword::Catch), _)) = reader.peek() {
state.append_keyword_at_pos(reader.next().unwrap().1 .0, TSXKeyword::Catch);
if let Some(Token(TSXToken::OpenParentheses, _)) = reader.peek() {
reader.expect_next(TSXToken::OpenParentheses)?;
let variable_field =
WithComment::<VariableField>::from_reader(reader, state, options)?;
let mut exception_var_type: Option<TypeAnnotation> = None;
if reader
.conditional_next(|tok| {
options.type_annotations && matches!(tok, TSXToken::Colon)
})
.is_some()
{
exception_var_type = Some(TypeAnnotation::from_reader(reader, state, options)?);
}
exception_var = Some((variable_field, exception_var_type));
reader.expect_next(TSXToken::CloseParentheses)?;
}
catch_inner = Some(Block::from_reader(reader, state, options)?);
}
let mut finally_inner: Option<Block> = None;
if let Some(Token(TSXToken::Keyword(TSXKeyword::Finally), _)) = reader.peek() {
state.append_keyword_at_pos(reader.next().unwrap().1 .0, TSXKeyword::Finally);
finally_inner = Some(Block::from_reader(reader, state, options)?);
}
let position: Span = if let Some(finally_block) = &finally_inner {
start.union(finally_block.get_position())
} else if let Some(catch_block) = &catch_inner {
start.union(catch_block.get_position())
} else {
return Err(ParseError::new(
ParseErrors::ExpectedCatchOrFinally,
reader.next().unwrap().get_span(),
));
};
Ok(Self { try_inner, catch_inner, exception_var, finally_inner, position })
}
fn to_string_from_buffer<T: source_map::ToString>(
&self,
buf: &mut T,
options: &crate::ToStringOptions,
local: crate::LocalToStringInformation,
) {
buf.push_str("try");
options.push_gap_optionally(buf);
self.try_inner.to_string_from_buffer(buf, options, local.next_level());
if let Some(catch) = &self.catch_inner {
options.push_gap_optionally(buf);
buf.push_str("catch");
options.push_gap_optionally(buf);
if let Some((exception_var, exception_var_type)) = &self.exception_var {
buf.push('(');
exception_var.to_string_from_buffer(buf, options, local);
if let Some(exception_var_type) = exception_var_type {
buf.push_str(": ");
exception_var_type.to_string_from_buffer(buf, options, local);
}
buf.push(')');
options.push_gap_optionally(buf);
}
catch.to_string_from_buffer(buf, options, local.next_level());
}
if let Some(finally) = &self.finally_inner {
options.push_gap_optionally(buf);
buf.push_str("finally");
options.push_gap_optionally(buf);
finally.to_string_from_buffer(buf, options, local.next_level());
}
}
}