use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use yarte_hir::{Struct, HIR};
use crate::EachCodeGen;
use crate::{CodeGen, IfElseCodeGen};
pub struct BytesCodeGen<'a, T: CodeGen> {
codegen: T,
s: &'a Struct<'a>,
parent: Ident,
buf: Ident,
}
impl<'a, T: CodeGen> BytesCodeGen<'a, T> {
pub fn new<'n>(
codegen: T,
s: &'n Struct,
buf: Ident,
parent: &'static str,
) -> BytesCodeGen<'n, T> {
BytesCodeGen {
codegen,
s,
parent: format_ident!("{}", parent),
buf,
}
}
#[inline]
fn template(&mut self, nodes: Vec<HIR>, tokens: &mut TokenStream) {
let nodes = self.codegen.gen(nodes);
let parent = &self.parent;
let buf = &self.buf;
tokens.extend(self.s.implement_head(
quote!(#parent::TemplateBytesTrait),
"e!(
fn call<B: #parent::Buffer>(&self, capacity: usize) -> B::Freeze {
use #parent::*;
let mut #buf = B::with_capacity(capacity);
macro_rules! buf_ref {
($b:expr) => { &mut $b };
}
#nodes
#buf.freeze()
}
fn ccall<B: #parent::Buffer>(self, capacity: usize) -> B::Freeze {
use #parent::*;
let mut #buf = B::with_capacity(capacity);
macro_rules! buf_ref {
($b:expr) => { &mut $b };
}
#nodes
#buf.freeze()
}
fn write_call<B: #parent::Buffer>(&self, #buf: &mut B) {
use #parent::*;
macro_rules! buf_ref {
($b:expr) => { $b };
}
#nodes
}
fn write_ccall<B: #parent::Buffer>(self, #buf: &mut B) {
use #parent::*;
macro_rules! buf_ref {
($b:expr) => { $b };
}
#nodes
}
),
));
}
}
impl<'a, T: CodeGen> CodeGen for BytesCodeGen<'a, T> {
fn gen(&mut self, v: Vec<HIR>) -> TokenStream {
let mut tokens = TokenStream::new();
self.template(v, &mut tokens);
tokens
}
}
pub struct TextBytesCodeGen<'a> {
buf: &'a syn::Expr,
}
impl<'a> TextBytesCodeGen<'a> {
pub fn new(buf: &syn::Expr) -> TextBytesCodeGen {
TextBytesCodeGen { buf }
}
}
impl<'a> EachCodeGen for TextBytesCodeGen<'a> {}
impl<'a> IfElseCodeGen for TextBytesCodeGen<'a> {}
impl<'a> CodeGen for TextBytesCodeGen<'a> {
fn gen(&mut self, v: Vec<HIR>) -> TokenStream {
let mut tokens = TokenStream::new();
for i in v {
use HIR::*;
tokens.extend(match i {
Local(a) => quote!(#a),
Lit(a) => {
let buf = &self.buf;
let buf = "e!(#buf);
literal(a, buf)
}
Safe(a) | Expr(a) => {
let buf = &self.buf;
quote!((&(#a)).__render_itb_safe(buf_ref!(#buf));)
}
Each(a) => self.gen_each(*a),
IfElse(a) => self.gen_if_else(*a),
});
}
tokens
}
}
fn gen<C>(codegen: &mut C, v: Vec<HIR>, buf: TokenStream) -> TokenStream
where
C: CodeGen + EachCodeGen + IfElseCodeGen,
{
let mut tokens = TokenStream::new();
for i in v {
use HIR::*;
tokens.extend(match i {
Local(a) => quote!(#a),
Lit(a) => literal(a, &buf),
Safe(a) => quote!((&(#a)).__render_itb_safe(buf_ref!(#buf));),
Expr(a) => quote!((&(#a)).__render_itb(buf_ref!(#buf));),
Each(a) => codegen.gen_each(*a),
IfElse(a) => codegen.gen_if_else(*a),
})
}
quote! {{ #tokens }}
}
fn literal(a: String, buf: &TokenStream) -> TokenStream {
let len = a.len();
let b = a.as_bytes();
match len {
0 => unreachable!(),
1..=3 => {
let range: TokenStream = write_bb(b, buf);
quote! {{
#[doc = #a]
yarte::Buffer::reserve(buf_ref!(#buf), #len);
unsafe {
#range
yarte::Buffer::advance(buf_ref!(#buf), #len);
}
}}
}
_ => {
quote! {
yarte::Buffer::extend(buf_ref!(#buf), #a);
}
}
}
}
fn write_bb(b: &[u8], buf: &TokenStream) -> TokenStream {
b.iter()
.enumerate()
.flat_map(|(i, b)| {
quote! {
*yarte::Buffer::buf_ptr(buf_ref!(#buf)).add(#i) = #b;
}
})
.collect()
}
pub struct HTMLBytesCodeGen<'a> {
buf: &'a syn::Expr,
}
impl<'a> HTMLBytesCodeGen<'a> {
pub fn new(buf: &syn::Expr) -> HTMLBytesCodeGen {
HTMLBytesCodeGen { buf }
}
}
impl<'a> EachCodeGen for HTMLBytesCodeGen<'a> {}
impl<'a> IfElseCodeGen for HTMLBytesCodeGen<'a> {}
impl<'a> CodeGen for HTMLBytesCodeGen<'a> {
fn gen(&mut self, v: Vec<HIR>) -> TokenStream {
let buf = self.buf;
gen(self, v, quote!(#buf))
}
}
#[cfg(feature = "html-min")]
pub mod html_min {
use super::*;
use yarte_dom::DOMFmt;
pub struct HTMLMinBytesCodeGen<'a> {
buf: &'a syn::Expr,
}
impl<'a> HTMLMinBytesCodeGen<'a> {
pub fn new(buf: &syn::Expr) -> HTMLMinBytesCodeGen {
HTMLMinBytesCodeGen { buf }
}
}
impl<'a> EachCodeGen for HTMLMinBytesCodeGen<'a> {}
impl<'a> IfElseCodeGen for HTMLMinBytesCodeGen<'a> {}
impl<'a> CodeGen for HTMLMinBytesCodeGen<'a> {
fn gen(&mut self, v: Vec<HIR>) -> TokenStream {
let dom: DOMFmt = v.into();
let buf = self.buf;
gen(self, dom.0, quote!(#buf))
}
}
}