// This function makes it much easier to compare expected code by adding the wrapping around // the code we want to check. #[track_caller] fn compare(jinja: &str, expected: &str, fields: &[(&str, &str)], size_hint: usize) {
compare_ex(jinja, expected, fields, size_hint, "")
}
let expected: proc_macro2::TokenStream = expected.parse().unwrap(); let expected: syn::File = syn::parse_quote! { impl askama::Template for Foo { fn render_into_with_values<AskamaW>(
&self,
__askama_writer: &mut AskamaW,
__askama_values: &dyn askama::Values,
) -> askama::Result<()> where
AskamaW: askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized,
{ #[allow(unused_imports)] use askama::{
filters::{AutoEscape as _, WriteWritable as _},
helpers::{ResultConverter as _, core::fmt::Write as _},
}; #expected
askama::Result::Ok(())
} const SIZE_HINT: askama::helpers::core::primitive::usize = #size_hint;
}
/// Implement the [`format!()`][askama::helpers::std::format] trait for [`Foo`] /// /// Please be aware of the rendering performance notice in the [`Template`][askama::Template] trait. impl askama::helpers::core::fmt::Display for Foo { #[inline] fn fmt(&self, f: &mut askama::helpers::core::fmt::Formatter<'_>) -> askama::helpers::core::fmt::Result {
askama::Template::render_into(self, f).map_err(|_| askama::helpers::core::fmt::Error)
}
}
#[test] fn check_if_let() { // In this test, we ensure that `query` never is `self.query`.
compare( "{% if let Some(query) = s && !query.is_empty() %}{{query}}{% endif %}",
r"if let Some(query,) = &self.s && !askama::helpers::as_bool(&(query.is_empty())) { match (
&((&&askama::filters::AutoEscaper::new(&(query), askama::filters::Text)).askama_auto_escape()?),
) {
(expr0,) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
}
}
}",
&[], 3,
);
// In this test, we ensure that `s` is `self.s` only in the first `if let Some(s) = self.s` // condition.
compare( "{% if let Some(s) = s %}{{ s }}{% endif %}",
r"if let Some(s,) = &self.s { match (
&((&&askama::filters::AutoEscaper::new(&(s), askama::filters::Text)).askama_auto_escape()?),
) {
(expr0,) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
}
}
}",
&[], 3,
);
// In this test, we ensure that `s` is `self.s` only in the first `if let Some(s) = self.s` // condition.
compare( "{% if let Some(s) = s && !s.is_empty() %}{{s}}{% endif %}",
r"if let Some(s,) = &self.s && !askama::helpers::as_bool(&(s.is_empty())) { match (
&((&&askama::filters::AutoEscaper::new(&(s), askama::filters::Text)).askama_auto_escape()?),
) {
(expr0,) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
}
}
}",
&[], 3,
);
}
// Since this feature is not stable yet, we can't add a "normal" test for it so instead we check // the generated code. #[test] fn check_if_let_chain() { // Both `bla` and `blob` variables must exist in this `if`.
compare( "{% if let Some(bla) = y && x && let Some(blob) = y %}{{bla}} {{blob}}{% endif %}",
r#"iflet Some(bla) = &self.y && askama::helpers::as_bool(&(le='color:red'>self.x)) && let Some(blob) = &self.y { match (
&((&&askama::filters::AutoEscaper::new(&(bla), askama::filters::Text))
.askama_auto_escape()?),
&((&&askama::filters::AutoEscaper::new(&(blob), askama::filters::Text))
.askama_auto_escape()?),
) {
(expr0, expr2) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
__askama_writer.write_str(" ")?;
(&&&askama::filters::Writable(expr2)).askama_write(__askama_writer, __askama_values)?;
}
}
}"#,
&[], 7,
);
compare(
r#"{% iflet Some(bla) = y
&& bla == "x"
&& let Some(blob) = z
&& blob == "z" %}{{bla}} {{blob}}{% endif %}"#,
r#"iflet Some(bla) = &self.y && askama::helpers::as_bool(&(bla == "x"))
&& let Some(blob) = &self.z && askama::helpers::as_bool(&(blob == "z"))
{ match (
&((&&askama::filters::AutoEscaper::new(&(bla), askama::filters::Text))
.askama_auto_escape()?),
&((&&askama::filters::AutoEscaper::new(&(blob), askama::filters::Text))
.askama_auto_escape()?),
) {
(expr0, expr2) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
__askama_writer.write_str(" ")?;
(&&&askama::filters::Writable(expr2)).askama_write(__askama_writer, __askama_values)?;
}
}
}"#,
&[], 7,
);
// Bindings variables with the same name as the bound variable should be declared in the right // order.
compare(
r#"{% iflet Some(y) = y
&& y == "x"
&& w
&& let Some(z) = z
&& z == "z" %}{{y}} {{z}}{% endif %}"#,
r#"iflet Some(y) = &self.y && self.y == "x"
&& askama::helpers::as_bool(&(self.w)) && let Some(z) = &self.z
&& askama::helpers::as_bool(&(z == "z"))
{ match (
&((&&askama::filters::AutoEscaper::new(&(y), askama::filters::Text))
.askama_auto_escape()?),
&((&&askama::filters::AutoEscaper::new(&(z), askama::filters::Text))
.askama_auto_escape()?),
) {
(expr0, expr2) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
__askama_writer.write_str(" ")?;
(&&&askama::filters::Writable(expr2)).askama_write(__askama_writer, __askama_values)?;
}
}
}"#,
&[], 7,
);
compare(
r#"{% if w
&& let Some(y) = y
&& y == "x"
&& let Some(z) = z
&& z == "z" %}{{y}} {{z}}{% endif %}"#,
r#"if askama::helpers::as_bool(&(self.w)) && let Some(y) = &le='color:red'>self.y
&& self.y == "x" && let Some(z) = &self.z
&& askama::helpers::as_bool(&(z == "z"))
{ match (
&((&&askama::filters::AutoEscaper::new(&(y), askama::filters::Text))
.askama_auto_escape()?),
&((&&askama::filters::AutoEscaper::new(&(z), askama::filters::Text))
.askama_auto_escape()?),
) {
(expr0, expr2) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
__askama_writer.write_str(" ")?;
(&&&askama::filters::Writable(expr2)).askama_write(__askama_writer, __askama_values)?;
}
}
}"#,
&[], 7,
);
}
#[test] fn check_includes_only_once() { // In this test we make sure that every used template gets referenced exactly once. let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("templates"); let path1 = path.join("include1.html").canonicalize().unwrap(); let path2 = path.join("include2.html").canonicalize().unwrap(); let path3 = path.join("include3.html").canonicalize().unwrap();
compare(
r#"{% include "include1.html" %}"#,
&format!(
r#"const _: &[askama::helpers::core::primitive::u8] = askama::helpers::core::include_bytes!({path1:#?}); const _: &[askama::helpers::core::primitive::u8] = askama::helpers::core::include_bytes!({path2:#?}); const _: &[askama::helpers::core::primitive::u8] = askama::helpers::core::include_bytes!({path3:#?});
__askama_writer.write_str("3333")?;"#
),
&[], 4,
);
}
#[test] fn check_is_defined() { // Checks that it removes conditions if we know at compile-time that they always return false. // // We're forced to add `bla` otherwise `compare` assert fails in weird ways...
compare( "{% if y is defined %}{{query}}{% endif %}bla",
r#"__askama_writer.write_str("bla")?;"#,
&[], 3,
);
compare( "{% if x is not defined %}{{query}}{% endif %}bla",
r#"__askama_writer.write_str("bla")?;"#,
&[("x", "u32")], 3,
);
compare( "{% if y is defined && x is not defined %}{{query}}{% endif %}bla",
r#"__askama_writer.write_str("bla")?;"#,
&[("x", "u32")], 3,
);
// Same with declared variables.
compare( "{% set y = 12 %}
{%- if y is not defined %}{{query}}{% endif %}bla",
r#"let y = 12;
__askama_writer.write_str("bla")?;"#,
&[], 3,
);
compare( "{% set y = 12 %}
{%- if y is not defined && x is defined %}{{query}}{% endif %}bla",
r#"let y = 12;
__askama_writer.write_str("bla")?;"#,
&[], 3,
);
// Checks that if the condition is always `true` at compile-time, then we keep the code but // remove the condition.
compare( "{% if y is defined %}bla{% endif %}",
r#"__askama_writer.write_str("bla")?;"#,
&[("y", "u32")], 3,
);
compare( "{% if x is not defined %}bla{% endif %}",
r#"__askama_writer.write_str("bla")?;"#,
&[], 3,
); // Same with declared variables.
compare( "{% set y = 12 %}
{%- if y is defined %}bla{% endif %}",
r#"let y = 12;
__askama_writer.write_str("bla")?;"#,
&[], 3,
);
// If the always `true` condition is followed by more `else if`/`else`, check that they are // removed as well.
compare( "{% if x is defined %}bli
{%- elseif x == 12 %}12{% endif %}bla",
r#"__askama_writer.write_str("blibla")?;"#,
&[("x", "u32")], 6,
);
compare( "{% if x is defined %}bli
{%- elseif x == 12 %}12
{%- else %}nope{% endif %}bla",
r#"__askama_writer.write_str("blibla")?;"#,
&[("x", "u32")], 6,
); // If it's not the first one.
compare( "{% if x == 12 %}bli
{%- elseif x is defined %}12
{%- else %}nope{% endif %}",
r#"if askama::helpers::as_bool(&(self.x == 12)) {
__askama_writer.write_str("bli")?;
} else {
__askama_writer.write_str("12")?;
}"#,
&[("x", "u32")], 5,
);
// Checking that it doesn't remove the condition if other non-"if (not) defined" checks // are present.
compare( "{% if y is defined || x == 12 %}{{x}}{% endif %}",
r"if askama::helpers::as_bool(&(self.x == 12)) { match (
&((&&askama::filters::AutoEscaper::new(&(self.x), askama::filters::Text)).askama_auto_escape()?),
) {
(expr0,) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
}
}
} ",
&[("x", "u32")], 3,
);
compare( "{% if y is defined || x == 12 %}{{x}}{% endif %}",
r"match (
&((&&askama::filters::AutoEscaper::new(&(self.x), askama::filters::Text)).askama_auto_escape()?),
) {
(expr0,) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
}
} ",
&[("y", "u32"), ("x", "u32")], 3,
);
compare( "{% if y is defined && y == 12 %}{{x}}{% endif %}",
r"",
&[], 0,
);
compare( "{% if y is defined && y == 12 %}{{y}}{% else %}bli{% endif %}",
r#"__askama_writer.write_str("bli")?;"#,
&[], 3,
);
compare( "{% if y is defined && y == 12 %}{{y}}{% else %}bli{% endif %}",
r#" if askama::helpers::as_bool(&(self.y == 12)) { match (
&((&&askama::filters::AutoEscaper::new(
&(self.y),
askama::filters::Text,
))
.askama_auto_escape()?),
) {
(expr0,) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
}
}
} else {
__askama_writer.write_str("bli")?;
} "#,
&[("y", "u32")], 6,
); // Since the first `if` is always `true`, the `else` should not be generated.
compare( "{% if y is defined %}{{y}}{% else %}bli{% endif %}",
r" match (
&((&&askama::filters::AutoEscaper::new(
&(self.y),
askama::filters::Text,
))
.askama_auto_escape()?),
) {
(expr0,) => {
(&&&askama::filters::Writable(expr0)).askama_write(__askama_writer, __askama_values)?;
}
} ",
&[("y", "u32")], 3,
);
// Checking some funny cases.
// This one is a bit useless because you can use `is not defined` but I suppose it's possible // to encounter cases like that in the wild so better have a check.
compare( "{% if !(y is defined) %}bla{% endif %}",
r#"__askama_writer.write_str("bla")?;"#,
&[], 3,
);
compare( "{% if !(y is not defined) %}bli{% endif %}bla",
r#"__askama_writer.write_str("bla")?;"#,
&[], 3,
);
compare( "{% if !(y is defined) %}bli{% endif %}bla",
r#"__askama_writer.write_str("bla")?;"#,
&[("y", "u32")], 3,
);
compare( "{% if !(y is not defined) %}bla{% endif %}",
r#"__askama_writer.write_str("bla")?;"#,
&[("y", "u32")], 3,
);
// Ensure that the `!` is kept .
compare( "{% if y is defined && !y %}bla{% endif %}",
r#"if !askama::helpers::as_bool(&(self.y)) {
__askama_writer.write_str("bla")?;
}"#,
&[("y", "bool")], 3,
);
compare( "{% if y is defined && !(y) %}bla{% endif %}",
r#"if !(askama::helpers::as_bool(&(self.y))) {
__askama_writer.write_str("bla")?;
}"#,
&[("y", "bool")], 3,
);
compare( "{% if y is not defined || !y %}bla{% endif %}",
r#"if !askama::helpers::as_bool(&(self.y)) {
__askama_writer.write_str("bla")?;
}"#,
&[("y", "bool")], 3,
);
compare( "{% if y is not defined || !(y) %}bla{% endif %}",
r#"if !(askama::helpers::as_bool(&(self.y))) {
__askama_writer.write_str("bla")?;
}"#,
&[("y", "bool")], 3,
);
}
#[test] fn check_bool_conditions() { // Checks that it removes conditions if we know at compile-time that they always return false. // // We're forced to add `bla` otherwise `compare` assert fails in weird ways...
compare( "{% if false %}{{query}}{% endif %}bla",
r#"__askama_writer.write_str("bla")?;"#,
&[], 3,
);
compare( "{% if false && false %}{{query}}{% endif %}bla",
r#"__askama_writer.write_str("bla")?;"#,
&[], 3,
);
compare( "{% if false && true %}{{query}}{% endif %}bla",
r#"__askama_writer.write_str("bla")?;"#,
&[], 3,
);
compare( "{% if true && false %}{{query}}{% endif %}bla",
r#"__askama_writer.write_str("bla")?;"#,
&[], 3,
);
compare( "{% if false || true %}bli{% endif %}bla",
r#"__askama_writer.write_str("blibla")?;"#,
&[], 6,
);
compare( "{% if true || false %}bli{% endif %}bla",
r#"__askama_writer.write_str("blibla")?;"#,
&[], 6,
);
let expected = jinja_to_rust(r#"front {% extends "a.html" %} back"#, &[], "").unwrap(); let expected = unparse(&expected); for front in CONTROL { for back in CONTROL { let src = format!(r#"front {{%{front} extends "a.html" {back}%}} back"#); let actual = jinja_to_rust(&src, &[], "").unwrap(); let actual = unparse(&actual);
assert_eq!(expected, actual, "source: {:?}", src);
}
}
}
#[test] fn test_with_config() { // In this test we make sure that the config path is tracked.
compare_ex(
r#""#,
&format!( "const _: &[askama::helpers::core::primitive::u8] = \
askama::helpers::core::include_bytes!({:#?});",
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("empty_test_config.toml")
.canonicalize()
.unwrap(),
),
&[], 0,
r#"#[template(config = "empty_test_config.toml")]"#,
);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.