use std::{collections::BTreeMap, path::PathBuf, sync::OnceLock};
use femstache_eval::{EvalResult, TemplateEnv, TemplateInput, Value};
use femstache_loader::{TemplateLoader, TemplateSource};
use femstache_parser::Template;
use crate::{
output::{EscapeMode, Output},
renderer::TemplateRenderer,
};
const TEMPLATES: &[(&str, &str)] = &[("meow.fem.html", "{{meow}} :3")];
static TEMPLATE_LOADER: OnceLock<TemplateLoader> = OnceLock::new();
fn get_loader() -> TemplateLoader {
TEMPLATE_LOADER
.get_or_init(|| {
let mut templates = BTreeMap::new();
for &(name, source) in TEMPLATES {
templates.insert(PathBuf::from(name), source.to_owned());
}
TemplateLoader::new(TemplateSource::map(templates))
})
.clone()
}
fn render(input: &impl TemplateInput, template: &Template) -> EvalResult<String> {
let loader = get_loader();
let env = TemplateEnv::new(input);
let blocks = BTreeMap::new();
let mut renderer = TemplateRenderer::new(env, blocks, Some(loader));
let mut output = Output::string(EscapeMode::None);
renderer.render_document(template.document(), &mut output)?;
Ok(output.into_inner())
}
macro_rules! template_test {
($name:ident, $input:expr, $tpl:literal, $expected:literal) => {
#[test]
fn $name() {
let tpl = Template::parse($tpl).unwrap();
let output = render(&$input, &tpl).unwrap();
assert_eq!(output, $expected);
}
};
}
struct Input;
impl TemplateInput for Input {
fn get(&self, key: &str) -> Option<Value> {
if key == "meow" {
Some(Value::string("gay"))
} else if key == "meows" {
Some(Value::List(vec![
Value::string("meow"),
Value::string("nyan"),
Value::string("miauw"),
Value::string("nyaa"),
]))
} else if key == "maybe_meow" {
Some(Value::Optional(Some(Box::new(Value::string("meow :3")))))
} else if key == "cuties" {
const CUTIES: [(&str, &str, &str); 3] = [
("bitey kitty", "nhac nhac chomp", "you"),
("sylveon", "meow~", "estrogen"),
("shadow the hedgehog", "nice ****!", "testosterone"),
];
Some(Value::List(
CUTIES
.iter()
.map(|&(name, sound, favorite_food)| {
let mut fields = BTreeMap::new();
fields.insert("name".to_owned(), Value::string(name));
fields.insert("sound".to_owned(), Value::string(sound));
fields.insert("favorite_food".to_owned(), Value::string(favorite_food));
Value::Struct(fields)
})
.collect(),
))
} else {
None
}
}
}
fn make_input() -> Input {
Input
}
template_test!(empty, make_input(), "", "");
template_test!(literal_meow, make_input(), "meow", "meow");
template_test!(stache_meow, make_input(), "{{meow}}", "gay");
template_test!(
if_statement_var,
make_input(),
"{% if meow %}woa{% end %}{% if meows %} x3{% end %}",
"woa x3"
);
template_test!(
if_statement_true,
make_input(),
"{% if true %}woa{% end %}",
"woa"
);
template_test!(
if_statement_false,
make_input(),
"{% if false %}woa{% end %}",
""
);
template_test!(
if_statement_none,
make_input(),
"{% if None %}woa{% end %}",
""
);
template_test!(
if_statement_some_1,
make_input(),
"{% if Some(false) %}woa{% end %}",
"woa"
);
template_test!(
if_statement_some_2,
make_input(),
"{% if Some(None) %}woa{% end %}",
"woa"
);
template_test!(
if_statement_string_empty,
make_input(),
r#"{% if "" %}woa{% end %}"#,
""
);
template_test!(
if_statement_string_flirty,
make_input(),
r#"{% if "hi ur cute ;3" %}woa{% end %}"#,
"woa"
);
template_test!(
if_statement_list_empty,
make_input(),
r#"{% if [] %}woa{% end %}"#,
""
);
template_test!(
if_statement_list_aggressive,
make_input(),
r#"{% if ["woa gowai >:("] %}woa{% end %}"#,
"woa"
);
template_test!(
if_statement_eq,
make_input(),
r#"{% if true == true %}woa{% end %}"#,
"woa"
);
template_test!(
if_statement_neq,
make_input(),
r#"{% if "a" != "b" %}woa{% end %}"#,
"woa"
);
template_test!(
if_let_statement,
make_input(),
r#"{% if let Some(meow) = maybe_meow %}{{ meow }}{% end %}"#,
"meow :3"
);
template_test!(
if_statement_else_1,
make_input(),
r#"{% if true %}woa{% else %}wao{% end %}"#,
"woa"
);
template_test!(
if_statement_else_2,
make_input(),
r#"{% if false %}woa{% else %}wao{% end %}"#,
"wao"
);
template_test!(
if_statement_else_if_1,
make_input(),
r#"{% if true %}meow{% else if true %}bark{% else %}honk{% end %}"#,
"meow"
);
template_test!(
if_statement_else_if_2,
make_input(),
r#"{% if false %}meow{% else if true %}bark{% else %}honk{% end %}"#,
"bark"
);
template_test!(
if_statement_else_if_3,
make_input(),
r#"{% if false %}meow{% else if false %}bark{% else %}honk{% end %}"#,
"honk"
);
template_test!(
if_statement_else_if_let_1,
make_input(),
r#"{% if false %}woof{% else if let Some(meow) = maybe_meow %}{{ meow }}{% end %}"#,
"meow :3"
);
template_test!(
for_statement_var_1,
make_input(),
r#"{% for item in meows %}woa{% end %}"#,
"woawoawoawoa"
);
template_test!(
for_statement_var_2,
make_input(),
r#"{% for item in meows %}woa{{item}}{% end %}"#,
"woameowwoanyanwoamiauwwoanyaa"
);
template_test!(
for_statement_literal,
make_input(),
r#"hi{% for item in ["uwu", "owo", "awa"] %} {{item}}{% end %}"#,
"hi uwu owo awa"
);
template_test!(
let_1,
make_input(),
r#"{% let x3 = "x3" %}*nuzzles u* {{ x3 }}"#,
"*nuzzles u* x3"
);
template_test!(
let_2,
make_input(),
r#"{% let nyan = meow %}*nuzzles u* {{ nyan }}"#,
"*nuzzles u* gay"
);
template_test!(
stuff,
make_input(),
r#"hewo! {% let nyan = meow %}{% let nya = None %}{% if nyan %}you are {{meow}}{% end %}{% if nya %}oh noes!{% end %} {% for meow in meows %}{{meow}} {% end %}{{meow}}{% if false %}nope{% end %}{% if None %}nope{% end %}{% if true %} yuh{% end %}"#,
"hewo! you are gay meow nyan miauw nyaa gay yuh"
);
template_test!(string_1, make_input(), r#"{{ "meow!" }}"#, "meow!");
template_test!(
string_2,
make_input(),
r#"{{ "meow!" ++ " :3" }}"#,
"meow! :3"
);
template_test!(
string_3,
make_input(),
r#"{{ "meow!" ++ " " ++ meow }}"#,
"meow! gay"
);
template_test!(
string_4,
make_input(),
r#"{{ ("me" ++ "ow!") ++ (":3" ++ "c") }}"#,
"meow!:3c"
);
template_test!(
struct_1,
make_input(),
r#"<ul>{% for cutie in cuties %}<li><strong>{{ cutie.name }}</strong>: {{ cutie.sound }} i like to eat {{ cutie.favorite_food }}</li>{% end %}</ul>"#,
"<ul><li><strong>bitey kitty</strong>: nhac nhac chomp i like to eat you</li><li><strong>sylveon</strong>: meow~ i like to eat estrogen</li><li><strong>shadow the hedgehog</strong>: nice ****! i like to eat testosterone</li></ul>"
);
template_test!(
include,
make_input(),
r#"{% include "meow.fem.html" %}"#,
"gay :3"
);