use crate::Node;
use crate::compiler::Compiler;
use crate::node::{ComponentParameter, ComponentParameterValue};
use crate::position::Position;
use anyhow::{Result, anyhow};
use proc_macro2::TokenStream;
use quote::quote;
use std::ops::AddAssign;
use std::str::FromStr;
pub struct ComponentCompiler;
impl ComponentCompiler {
pub fn compile(
compiler: &mut Compiler,
name: &str,
parameters: &Vec<ComponentParameter>,
body: &[Node],
position: &Position,
) -> Result<TokenStream> {
let component_node = compiler
.components
.get(name)
.ok_or(anyhow!("Component {} not found", name))?;
let component_node = component_node.clone();
let component_node = match component_node {
Node::Template(file, node, _) => Node::Template(file, node, position.clone()),
_ => {
return Err(anyhow!(
"The component must return a template as the top node."
));
}
};
let mut token_stream = TokenStream::new();
for parameter in parameters {
let name_ts = TokenStream::from_str(¶meter.name)
.map_err(|err| anyhow!("Lex Error: {}", err))?;
let parameter_ts = match ¶meter.value {
ComponentParameterValue::Bool(value) => quote! {let #name_ts = #value;},
ComponentParameterValue::Number(value) => {
compiler.text_size.add_assign(value.len());
quote! {let #name_ts = #value;}
}
ComponentParameterValue::String(value) => {
compiler.text_size.add_assign(value.len());
quote! {let #name_ts = #value;}
}
ComponentParameterValue::RustExprParen(value) => {
let expr_ts = TokenStream::from_str(value)
.map_err(|err| anyhow!("Lex Error: {}", err))?;
quote! {let #name_ts = #expr_ts;}
}
ComponentParameterValue::RustExprSimple(value) => {
let expr_ts = TokenStream::from_str(value)
.map_err(|err| anyhow!("Lex Error: {}", err))?;
quote! {let #name_ts = #expr_ts;}
}
ComponentParameterValue::Block(value) => {
let block_ts = compiler.compile(&Node::Template(
String::new(),
value.clone(),
position.clone(),
))?;
quote! {
let mut #name_ts = String::new();
(|__f__: &mut dyn ::std::fmt::Write| -> ::std::fmt::Result {#block_ts Ok(())})(&mut #name_ts)?;
}
}
};
token_stream.extend(parameter_ts);
}
let body_ts = compiler.compile(&Node::Template(
String::new(),
body.to_owned(),
position.clone(),
))?;
let body_ts = quote! {let child_content = |__f__: &mut dyn ::std::fmt::Write| -> ::std::fmt::Result {#body_ts Ok(())};};
token_stream.extend(body_ts);
let component_ts = compiler.compile(&component_node);
token_stream.extend(component_ts);
let info_ts = compiler.with_info(TokenStream::new(), position);
let info_start = format!("Component {} Start", name);
let info_end = format!("Component {} End", name);
Ok(quote! {{
#info_start;
#info_ts
#token_stream
#info_end;
}})
}
}