use super::Expression;
use crate::{
Span, Spanned,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, Sym, 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 TaggedTemplate {
tag: Box<Expression>,
raws: Box<[Sym]>,
cookeds: Box<[Option<Sym>]>,
exprs: Box<[Expression]>,
identifier: u64,
span: Span,
}
impl TaggedTemplate {
#[inline]
#[must_use]
pub fn new(
tag: Expression,
raws: Box<[Sym]>,
cookeds: Box<[Option<Sym>]>,
exprs: Box<[Expression]>,
identifier: u64,
span: Span,
) -> Self {
Self {
tag: tag.into(),
raws,
cookeds,
exprs,
identifier,
span,
}
}
#[inline]
#[must_use]
pub const fn tag(&self) -> &Expression {
&self.tag
}
#[inline]
#[must_use]
pub const fn raws(&self) -> &[Sym] {
&self.raws
}
#[inline]
#[must_use]
pub const fn cookeds(&self) -> &[Option<Sym>] {
&self.cookeds
}
#[inline]
#[must_use]
pub const fn exprs(&self) -> &[Expression] {
&self.exprs
}
#[inline]
#[must_use]
pub const fn identifier(&self) -> u64 {
self.identifier
}
}
impl Spanned for TaggedTemplate {
#[inline]
fn span(&self) -> Span {
self.span
}
}
impl ToInternedString for TaggedTemplate {
#[inline]
fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = format!("{}`", self.tag.to_interned_string(interner));
let mut exprs = self.exprs.iter();
for raw in &self.raws {
let _ = write!(buf, "{}", interner.resolve_expect(*raw));
if let Some(expr) = exprs.next() {
let _ = write!(buf, "${{{}}}", expr.to_interned_string(interner));
}
}
buf.push('`');
buf
}
}
impl From<TaggedTemplate> for Expression {
#[inline]
fn from(template: TaggedTemplate) -> Self {
Self::TaggedTemplate(template)
}
}
impl VisitWith for TaggedTemplate {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
visitor.visit_expression(&self.tag)?;
for raw in &*self.raws {
visitor.visit_sym(raw)?;
}
for cooked in self.cookeds.iter().flatten() {
visitor.visit_sym(cooked)?;
}
for expr in &*self.exprs {
visitor.visit_expression(expr)?;
}
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.tag)?;
for raw in &mut *self.raws {
visitor.visit_sym_mut(raw)?;
}
for cooked in self.cookeds.iter_mut().flatten() {
visitor.visit_sym_mut(cooked)?;
}
for expr in &mut *self.exprs {
visitor.visit_expression_mut(expr)?;
}
ControlFlow::Continue(())
}
}