use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use yarte_hir::{Struct, HIR};
use crate::{CodeGen, EachCodeGen, IfElseCodeGen};
pub struct FixedCodeGen<'a, T: CodeGen> {
codegen: T,
s: &'a Struct<'a>,
parent: Ident,
}
impl<'a, T: CodeGen> FixedCodeGen<'a, T> {
pub fn new<'n>(codegen: T, s: &'n Struct, parent: &'static str) -> FixedCodeGen<'n, T> {
FixedCodeGen {
codegen,
s,
parent: format_ident!("{}", parent),
}
}
#[inline]
fn template(&mut self, nodes: Vec<HIR>, tokens: &mut TokenStream) {
let nodes = self.codegen.gen(nodes);
let parent = &self.parent;
tokens.extend(self.s.implement_head(
quote!(yarte::TemplateFixedTrait),
"e!(
unsafe fn call<'call>(&self, buf: &'call mut [std::mem::MaybeUninit<u8>]) -> Option<&'call [u8]> {
unsafe {
#[allow(unused_import)]
use #parent::*;
macro_rules! buf_ptr {
() => { buf as *mut _ as * mut u8 };
}
macro_rules! len {
() => { buf.len() };
}
let mut buf_cur = 0;
#[allow(unused_macros)]
macro_rules! __yarte_check_write {
($len:expr, $write:block) => {
if len!() < buf_cur + $len {
return None;
} else $write
};
}
#[allow(unused_macros)]
macro_rules! __yarte_write_bytes_long {
($b:expr) => {
__yarte_check_write!($b.len(), {
std::ptr::copy_nonoverlapping((&$b as *const _ as *const u8), buf_ptr!().add(buf_cur), $b.len());
buf_cur += $b.len();
})
};
}
#nodes
Some(std::slice::from_raw_parts(buf as *const _ as *const u8, buf_cur))
}
}
unsafe fn ccall(self, buf: &mut [std::mem::MaybeUninit<u8>]) -> Option<&[u8]> {
unsafe {
#[allow(unused_import)]
use #parent::*;
macro_rules! buf_ptr {
() => { buf as *mut _ as * mut u8 };
}
macro_rules! len {
() => { buf.len() };
}
let mut buf_cur = 0;
#[allow(unused_macros)]
macro_rules! __yarte_check_write {
($len:expr, $write:block) => {
if len!() < buf_cur + $len {
return None;
} else $write
};
}
#[allow(unused_macros)]
macro_rules! __yarte_write_bytes_long {
($b:expr) => {
__yarte_check_write!($b.len(), {
std::ptr::copy_nonoverlapping((&$b as *const _ as *const u8), buf_ptr!().add(buf_cur), $b.len());
buf_cur += $b.len();
})
};
}
#nodes
Some(std::slice::from_raw_parts(buf as *const _ as *const u8, buf_cur))
}
}
),
));
}
}
fn literal(a: String, parent: &Ident) -> TokenStream {
let len = a.len();
let b = a.as_bytes();
match len {
0 => unreachable!(),
1..=3 => {
let range: TokenStream = write_bb(b);
quote! {{
#[doc = #a]
__yarte_check_write!(#len, {
#range
buf_cur += #len;
})
}}
}
4..=15 => {
quote! {{
#[doc = #a]
const YARTE_SLICE: [u8; #len] = [#(#b),*];
__yarte_write_bytes_long!(YARTE_SLICE);
}}
}
_ => {
quote! {{
#[doc = #a]
const YARTE_SLICE: #parent::Aligned256<[u8; #len]> = #parent::Aligned256([#(#b),*]);
__yarte_write_bytes_long!(YARTE_SLICE.0);
}}
}
}
}
fn write_bb(b: &[u8]) -> TokenStream {
b.iter()
.enumerate()
.map(|(i, b)| {
quote! {
*buf_ptr!().add(buf_cur + #i) = #b;
}
})
.flatten()
.collect()
}
impl<'a, T: CodeGen> CodeGen for FixedCodeGen<'a, T> {
fn gen(&mut self, v: Vec<HIR>) -> TokenStream {
let mut tokens = TokenStream::new();
self.template(v, &mut tokens);
tokens
}
}
pub struct TextFixedCodeGen(pub &'static str);
impl EachCodeGen for TextFixedCodeGen {}
impl IfElseCodeGen for TextFixedCodeGen {}
impl CodeGen for TextFixedCodeGen {
fn gen(&mut self, v: Vec<HIR>) -> TokenStream {
let mut tokens = TokenStream::new();
let parent = format_ident!("{}", self.0);
for i in v {
use HIR::*;
tokens.extend(match i {
Local(a) => quote!(#a),
Lit(a) => literal(a, &parent),
Safe(a) | Expr(a) => {
quote!(buf_cur += (&(#a)).__render_it_safe(&mut buf[buf_cur..])?;)
}
Each(a) => self.gen_each(*a),
IfElse(a) => self.gen_if_else(*a),
});
}
tokens
}
}
fn gen<C>(codegen: &mut C, v: Vec<HIR>, parent: &str) -> TokenStream
where
C: CodeGen + EachCodeGen + IfElseCodeGen,
{
let mut tokens = TokenStream::new();
let parent = format_ident!("{}", parent);
for i in v {
use HIR::*;
tokens.extend(match i {
Local(a) => quote!(#a),
Lit(a) => literal(a, &parent),
Safe(a) => quote!(buf_cur += (&(#a)).__render_it_safe(&mut buf[buf_cur..])?;),
Expr(a) => quote!(buf_cur += (&(#a)).__render_it(&mut buf[buf_cur..])?;),
Each(a) => codegen.gen_each(*a),
IfElse(a) => codegen.gen_if_else(*a),
})
}
tokens
}
pub struct HTMLFixedCodeGen(pub &'static str);
impl EachCodeGen for HTMLFixedCodeGen {}
impl IfElseCodeGen for HTMLFixedCodeGen {}
impl CodeGen for HTMLFixedCodeGen {
fn gen(&mut self, v: Vec<HIR>) -> TokenStream {
let parent = self.0;
gen(self, v, parent)
}
}
#[cfg(feature = "html-min")]
pub mod html_min {
use super::*;
use yarte_dom::DOMFmt;
pub struct HTMLMinFixedCodeGen(pub &'static str);
impl EachCodeGen for HTMLMinFixedCodeGen {}
impl IfElseCodeGen for HTMLMinFixedCodeGen {}
impl CodeGen for HTMLMinFixedCodeGen {
fn gen(&mut self, v: Vec<HIR>) -> TokenStream {
let parent = self.0;
let dom: DOMFmt = v.into();
gen(self, dom.0, parent)
}
}
}