use std::{borrow::Cow, fmt::Debug, marker::PhantomData};
use crate::{
parse_bracketed, to_string_bracketed, ASTNode, Block, ExpressionOrStatementPosition,
ExpressionPosition, GenericTypeConstraint, Keyword, ParseOptions, ParseResult, TSXToken,
TypeAnnotation, VisitSettings, Visitable,
};
use crate::{tsx_keywords, TSXKeyword};
use derive_partial_eq_extras::PartialEqExtras;
use source_map::{Span, ToString};
use tokenizer_lib::{Token, TokenReader};
pub use crate::parameters::*;
pub mod bases {
pub use crate::{
declarations::{
classes::{ClassConstructorBase, ClassFunctionBase},
StatementFunctionBase,
},
expressions::{
arrow_function::ArrowFunctionBase, object_literal::ObjectLiteralMethodBase,
ExpressionFunctionBase,
},
};
}
pub trait FunctionBased: Debug + Clone + PartialEq + Eq + Send + Sync {
type Header: Debug + Clone + PartialEq + Eq + Send + Sync;
type Name: Debug + Clone + PartialEq + Eq + Send + Sync;
type Body: ASTNode;
fn header_left(header: &Self::Header) -> Option<Cow<Span>>;
fn header_and_name_from_reader(
reader: &mut impl TokenReader<TSXToken, Span>,
state: &mut crate::ParsingState,
settings: &ParseOptions,
) -> ParseResult<(Self::Header, Self::Name)>;
fn header_and_name_to_string_from_buffer<T: ToString>(
buf: &mut T,
header: &Self::Header,
name: &Self::Name,
settings: &crate::ToStringOptions,
depth: u8,
);
fn get_parameter_body_boundary_token() -> Option<TSXToken> {
None
}
fn parameters_from_reader<T: ToString>(
reader: &mut impl TokenReader<TSXToken, Span>,
state: &mut crate::ParsingState,
settings: &ParseOptions,
) -> ParseResult<FunctionParameters> {
FunctionParameters::from_reader(reader, state, settings)
}
fn parameters_to_string_from_buffer<T: ToString>(
buf: &mut T,
parameters: &FunctionParameters,
settings: &crate::ToStringOptions,
depth: u8,
) {
parameters.to_string_from_buffer(buf, settings, depth);
}
fn parameter_body_boundary_token_to_string_from_buffer<T: ToString>(
buf: &mut T,
settings: &crate::ToStringOptions,
) {
settings.add_gap(buf);
}
}
#[derive(Debug, Clone, PartialEqExtras)]
pub struct FunctionBase<T: FunctionBased> {
pub header: T::Header,
pub name: T::Name,
pub type_parameters: Option<Vec<GenericTypeConstraint>>,
pub parameters: FunctionParameters,
pub return_type: Option<TypeAnnotation>,
pub body: T::Body,
pub position: Span,
}
#[cfg(feature = "self-rust-tokenize")]
impl<T: FunctionBased> self_rust_tokenize::SelfRustTokenize for FunctionBase<T> {
fn append_to_token_stream(
&self,
_token_stream: &mut self_rust_tokenize::proc_macro2::TokenStream,
) {
todo!("fb to tokens")
}
}
impl<T: FunctionBased> Eq for FunctionBase<T> {}
impl<T: FunctionBased + 'static> ASTNode for FunctionBase<T> {
fn from_reader(
reader: &mut impl TokenReader<TSXToken, Span>,
state: &mut crate::ParsingState,
settings: &ParseOptions,
) -> ParseResult<Self> {
let (header, name) = T::header_and_name_from_reader(reader, state, settings)?;
Self::from_reader_with_header_and_name(reader, state, settings, header, name)
}
fn to_string_from_buffer<TS: source_map::ToString>(
&self,
buf: &mut TS,
settings: &crate::ToStringOptions,
depth: u8,
) {
T::header_and_name_to_string_from_buffer(buf, &self.header, &self.name, settings, depth);
if let (true, Some(type_parameters)) = (settings.include_types, &self.type_parameters) {
to_string_bracketed(type_parameters, ('<', '>'), buf, settings, depth);
}
T::parameters_to_string_from_buffer(buf, &self.parameters, settings, depth);
if let (true, Some(return_type)) = (settings.include_types, &self.return_type) {
buf.push_str(": ");
return_type.to_string_from_buffer(buf, settings, depth);
}
T::parameter_body_boundary_token_to_string_from_buffer(buf, settings);
self.body.to_string_from_buffer(buf, settings, depth + 1);
}
fn get_position(&self) -> Cow<Span> {
Cow::Borrowed(&self.position)
}
}
impl<T: FunctionBased> FunctionBase<T> {
pub(crate) fn from_reader_with_header_and_name(
reader: &mut impl TokenReader<TSXToken, Span>,
state: &mut crate::ParsingState,
settings: &ParseOptions,
header: T::Header,
name: T::Name,
) -> ParseResult<Self> {
let type_parameters = reader
.conditional_next(|token| *token == TSXToken::OpenChevron)
.is_some()
.then(|| {
parse_bracketed(reader, state, settings, None, TSXToken::CloseChevron)
.map(|(params, _)| params)
})
.transpose()?;
let parameters = FunctionParameters::from_reader(reader, state, settings)?;
let return_type = reader
.conditional_next(|tok| matches!(tok, TSXToken::Colon))
.is_some()
.then(|| TypeAnnotation::from_reader(reader, state, settings))
.transpose()?;
if let Some(token) = T::get_parameter_body_boundary_token() {
reader.expect_next(token)?;
}
let body = T::Body::from_reader(reader, state, settings)?;
let body_pos = body.get_position();
let position = if let Some(header_pos) = T::header_left(&header) {
header_pos.union(&body_pos)
} else {
parameters.position.clone().union(&body_pos)
};
Ok(Self { position, header, name, parameters, type_parameters, body, return_type })
}
}
impl<T: FunctionBased> Visitable for FunctionBase<T>
where
T::Body: Visitable,
{
fn visit<TData>(
&self,
visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
data: &mut TData,
settings: &VisitSettings,
chain: &mut temporary_annex::Annex<crate::Chain>,
) {
self.parameters.visit(visitors, data, settings, chain);
if settings.visit_function_bodies {
self.body.visit(visitors, data, settings, chain);
}
}
fn visit_mut<TData>(
&mut self,
visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
data: &mut TData,
settings: &VisitSettings,
chain: &mut temporary_annex::Annex<crate::Chain>,
) {
self.parameters.visit_mut(visitors, data, settings, chain);
if settings.visit_function_bodies {
self.body.visit_mut(visitors, data, settings, chain);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GeneralFunctionBase<T: ExpressionOrStatementPosition>(PhantomData<T>);
pub type ExpressionFunction = FunctionBase<GeneralFunctionBase<ExpressionPosition>>;
impl<T: ExpressionOrStatementPosition> FunctionBased for GeneralFunctionBase<T> {
type Body = Block;
type Header = FunctionHeader;
type Name = T::Name;
fn header_and_name_from_reader(
reader: &mut impl TokenReader<TSXToken, Span>,
state: &mut crate::ParsingState,
settings: &crate::ParseOptions,
) -> ParseResult<(Self::Header, Self::Name)> {
let header = FunctionHeader::from_reader(reader, state, settings)?;
let name = T::from_reader(reader, state, settings)?;
Ok((header, name))
}
fn header_and_name_to_string_from_buffer<U: source_map::ToString>(
buf: &mut U,
header: &Self::Header,
name: &Self::Name,
settings: &crate::ToStringOptions,
depth: u8,
) {
header.to_string_from_buffer(buf, settings, depth);
if let Some(name) = T::as_option_str(name) {
buf.push_str(name);
}
}
fn header_left(header: &Self::Header) -> Option<Cow<Span>> {
Some(header.get_position())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum FunctionHeader {
VirginFunctionHeader {
async_keyword: Option<Keyword<tsx_keywords::Async>>,
function_keyword: Keyword<tsx_keywords::Function>,
generator_star_token_pos: Option<Span>,
},
#[cfg(feature = "extras")]
ChadFunctionHeader {
async_keyword: Option<Keyword<tsx_keywords::Async>>,
generator_keyword: Option<Keyword<tsx_keywords::Generator>>,
function_keyword: Keyword<tsx_keywords::Function>,
},
}
impl ASTNode for FunctionHeader {
fn get_position(&self) -> Cow<Span> {
match self {
FunctionHeader::VirginFunctionHeader {
async_keyword,
function_keyword,
generator_star_token_pos,
} => {
let mut position = Cow::Borrowed(
async_keyword.as_ref().map(|kw| &kw.1).unwrap_or(&function_keyword.1),
);
if let Some(generator_star_token_pos) = generator_star_token_pos {
position = Cow::Owned(position.union(generator_star_token_pos));
}
position
}
#[cfg(feature = "extras")]
FunctionHeader::ChadFunctionHeader {
async_keyword,
generator_keyword,
function_keyword,
} => {
let first_pos = async_keyword
.as_ref()
.map(Keyword::get_position)
.or_else(|| generator_keyword.as_ref().map(Keyword::get_position));
if let Some(first_pos) = first_pos {
Cow::Owned(first_pos.union(&function_keyword.1))
} else {
Cow::Borrowed(&function_keyword.1)
}
}
}
}
fn from_reader(
reader: &mut impl TokenReader<TSXToken, Span>,
_state: &mut crate::ParsingState,
_settings: &ParseOptions,
) -> ParseResult<Self> {
let async_keyword = reader
.conditional_next(|tok| matches!(tok, TSXToken::Keyword(TSXKeyword::Async)))
.map(|Token(_, span)| Keyword::new(span));
let next_generator =
reader.conditional_next(|tok| matches!(tok, TSXToken::Keyword(TSXKeyword::Generator)));
if let Some(Token(_, span)) = next_generator {
let generator_keyword = Some(Keyword::new(span));
let span = reader.expect_next(TSXToken::Keyword(TSXKeyword::Function))?;
let function_keyword = Keyword::new(span);
Ok(Self::ChadFunctionHeader { async_keyword, generator_keyword, function_keyword })
} else {
let span = reader.expect_next(TSXToken::Keyword(TSXKeyword::Function))?;
let function_keyword = Keyword::new(span);
let generator_star_token_pos = reader
.conditional_next(|tok| matches!(tok, TSXToken::Multiply))
.map(|Token(_, pos)| pos);
Ok(Self::VirginFunctionHeader {
async_keyword,
function_keyword,
generator_star_token_pos,
})
}
}
fn to_string_from_buffer<T: source_map::ToString>(
&self,
buf: &mut T,
_settings: &crate::ToStringOptions,
_depth: u8,
) {
if self.is_async() {
buf.push_str("async ");
}
buf.push_str("function");
if self.is_generator() {
buf.push_str("* ");
} else {
buf.push(' ');
}
}
}
impl FunctionHeader {
pub fn is_generator(&self) -> bool {
match self {
FunctionHeader::VirginFunctionHeader { generator_star_token_pos, .. } => {
generator_star_token_pos.is_some()
}
FunctionHeader::ChadFunctionHeader { generator_keyword, .. } => {
generator_keyword.is_some()
}
}
}
pub fn is_async(&self) -> bool {
match self {
FunctionHeader::VirginFunctionHeader { async_keyword, .. }
| FunctionHeader::ChadFunctionHeader { async_keyword, .. } => async_keyword.is_some(),
}
}
}