├── book ├── .gitignore ├── book.toml └── src │ ├── SUMMARY.md │ ├── performance.md │ ├── debugging.md │ ├── getting_started.md │ ├── askama.md │ ├── integrations.md │ ├── configuration.md │ └── creating_templates.md ├── askama ├── LICENSE-MIT ├── LICENSE-APACHE ├── src │ ├── helpers.rs │ ├── error.rs │ └── filters │ │ └── json.rs ├── Cargo.toml └── benches │ └── to-json.rs ├── askama_actix ├── LICENSE-MIT ├── LICENSE-APACHE ├── templates │ └── hello.html ├── README.md ├── tests │ └── basic.rs ├── Cargo.toml └── src │ └── lib.rs ├── askama_axum ├── LICENSE-MIT ├── LICENSE-APACHE ├── templates │ └── hello.html ├── README.md ├── tests │ └── basic.rs ├── src │ └── lib.rs └── Cargo.toml ├── askama_derive ├── templates │ ├── a.html │ ├── b.html │ └── sub │ │ ├── b.html │ │ ├── c.html │ │ └── sub1 │ │ └── d.html ├── LICENSE-MIT ├── LICENSE-APACHE ├── README.md ├── Cargo.toml └── src │ ├── tests.rs │ └── heritage.rs ├── askama_warp ├── LICENSE-MIT ├── LICENSE-APACHE ├── templates │ └── hello.html ├── tests │ └── warp.rs ├── README.md ├── Cargo.toml └── src │ └── lib.rs ├── testing ├── templates │ ├── foo.html │ ├── foo.jinja │ ├── attr.html │ ├── foo.html.jinja │ ├── filters.html │ ├── generics.html │ ├── hello.html │ ├── included.html │ ├── composition.html │ ├── filters_join.html │ ├── format.html │ ├── if.html │ ├── invalid_syntax.html │ ├── cycle1.html │ ├── cycle2.html │ ├── nested-attr.html │ ├── rust-macros.html │ ├── let-base.html │ ├── tuple-attr.html │ ├── char-literals │ │ ├── char-literal-1.txt │ │ ├── char-literal-2.txt │ │ ├── char-literal-5.txt │ │ ├── char-literal-3.txt │ │ ├── char-literal-4.txt │ │ ├── char-literal-6.txt │ │ └── char-literal-7.txt │ ├── else.html │ ├── rust-macros-full-path.html │ ├── include_invalid_syntax.html │ ├── let.html │ ├── literals.html │ ├── raw-simple.html │ ├── if-let-shadowing.html │ ├── if-let.html │ ├── json.html │ ├── size-parent.txt │ ├── else-if.html │ ├── include.html │ ├── import.html │ ├── raw-ws.html │ ├── extend_and_import.html │ ├── match-no-ws.html │ ├── deep-import-parent.html │ ├── deep-nested-macro.html │ ├── nested-base.html │ ├── size-child-super.txt │ ├── if-let-struct.html │ ├── include-extends-included.html │ ├── option.html │ ├── size-child.txt │ ├── base.html │ ├── child.html │ ├── include-extends.html │ ├── raw-complex.html │ ├── macro-self-arg.html │ ├── let-child.html │ ├── include-macro.html │ ├── nested-child.html │ ├── included-macro.html │ ├── match-literal-num.html │ ├── big-table.html │ ├── deep-import-child.html │ ├── let-decl.html │ ├── ranges.txt │ ├── render_in_place.html │ ├── let-destruct-tuple.html │ ├── nested-for.html │ ├── simple.html │ ├── precedence.html │ ├── literals-escape.html │ ├── simple-no-escape.txt │ ├── match-literal-char.html │ ├── if-let-else.html │ ├── match-literal.html │ ├── include-extends-base.html │ ├── match-opt.html │ ├── fragment-base.html │ ├── match-opt-bool.html │ ├── precedence-for.html │ ├── fragment-include.html │ ├── fragment-mid-super.html │ ├── fragment-simple.html │ ├── blocks.txt │ ├── match-enum-or.html │ ├── deep-kid.html │ ├── nested-macro-args.html │ ├── nested-macro.html │ ├── for.html │ ├── fragment-super.html │ ├── fragment-unused-expr.html │ ├── macro-import-str-cmp.html │ ├── fragment-nested-super.html │ ├── rust-macro-args.html │ ├── macro-no-args.html │ ├── macro-short-circuit.html │ ├── fragment-nested-block.html │ ├── match-option-result-option.html │ ├── deep-base.html │ ├── if-let-with-for.html │ ├── for-break-continue.html │ ├── for-range.html │ ├── macro-import-str-cmp-macro.html │ ├── named-end.html │ ├── macro.html │ ├── match-custom-enum.html │ ├── teams.html │ ├── let-shadow.html │ ├── deep-mid.html │ ├── operators.html │ ├── num-literals.html │ ├── compare.html │ ├── if-coerce.html │ └── allow-whitespaces.html ├── test_minimize.toml ├── test_trim.toml ├── tests │ ├── ui │ │ ├── no_template_attribute.rs │ │ ├── cycle.rs │ │ ├── cycle2.rs │ │ ├── incorrect_path.rs │ │ ├── macro-super.rs │ │ ├── lit_on_assignment_lhs.rs │ │ ├── name_mismatch_endblock.rs │ │ ├── name_mismatch_endmacro.rs │ │ ├── break_outside_of_loop.rs │ │ ├── filter_block_ws.rs │ │ ├── loop_cycle_empty.rs │ │ ├── typo_in_keyword.rs │ │ ├── duplicated_template_attribute.rs │ │ ├── block_and_vars.rs │ │ ├── block_and_vars.stderr │ │ ├── loop_cycle_empty.stderr │ │ ├── no_template_attribute.stderr │ │ ├── loop_cycle_wrong_argument_count.rs │ │ ├── duplicated_template_attribute.stderr │ │ ├── loop_cycle_wrong_argument_count.stderr │ │ ├── lit_on_assignment_lhs.stderr │ │ ├── extends.rs │ │ ├── filter_block_ws.stderr │ │ ├── match_with_extra.stderr │ │ ├── typo_in_keyword.stderr │ │ ├── excessive_nesting.stderr │ │ ├── incorrect_path.stderr │ │ ├── error_file_path.rs │ │ ├── match_with_extra.rs │ │ ├── macro-super.stderr │ │ ├── break_outside_of_loop.stderr │ │ ├── name_mismatch_endblock.stderr │ │ ├── name_mismatch_endmacro.stderr │ │ ├── cycle2.stderr │ │ ├── cycle.stderr │ │ ├── macro.rs │ │ ├── extends.stderr │ │ ├── macro.stderr │ │ ├── char_literal.rs │ │ ├── error_file_path.stderr │ │ ├── unclosed-nodes.rs │ │ ├── macro_named_argument.rs │ │ ├── macro_named_argument.stderr │ │ ├── char_literal.stderr │ │ └── unclosed-nodes.stderr │ ├── coerce.rs │ ├── extend.rs │ ├── if.rs │ ├── hello.rs │ ├── render_in_place.rs │ ├── size_hint.rs │ ├── ui.rs │ ├── methods.rs │ ├── include.rs │ ├── operators.rs │ ├── gen_loop_else.py │ ├── rust_macro.rs │ ├── ext.rs │ ├── try.rs │ ├── tuple.rs │ ├── calls.rs │ ├── vars.rs │ ├── let_destructoring.rs │ ├── block_fragments.rs │ ├── if_let.rs │ ├── macro.rs │ └── filter_block.rs ├── Cargo.toml └── benches │ └── all.rs ├── askama_escape ├── LICENSE-MIT ├── LICENSE-APACHE ├── Cargo.toml ├── README.md ├── benches │ └── all.rs └── src │ └── lib.rs ├── askama_parser ├── LICENSE-MIT ├── LICENSE-APACHE ├── benches │ ├── librustdoc │ │ ├── item_info.html │ │ ├── type_layout_size.html │ │ ├── LICENSE.md │ │ ├── source.html │ │ ├── short_item_info.html │ │ ├── print_item.html │ │ ├── item_union.html │ │ ├── sidebar.html │ │ └── type_layout.html │ └── from_str.rs ├── README.md ├── Cargo.toml └── tests │ └── target-recursion.txt ├── askama_rocket ├── LICENSE-MIT ├── LICENSE-APACHE ├── templates │ └── hello.html ├── README.md ├── tests │ └── basic.rs ├── Cargo.toml └── src │ └── lib.rs ├── .gitignore ├── .gitattributes ├── .github └── FUNDING.yml ├── fuzz ├── .gitignore ├── README.md ├── fuzz_targets │ ├── fuzz_parser.rs │ └── fuzz_filters.rs └── Cargo.toml ├── deny.toml ├── Cargo.toml ├── README.md └── LICENSE-MIT /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /askama/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /askama/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /askama_actix/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /askama_axum/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /askama_derive/templates/a.html: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /askama_derive/templates/b.html: -------------------------------------------------------------------------------- 1 | bar 2 | -------------------------------------------------------------------------------- /askama_warp/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /testing/templates/foo.html: -------------------------------------------------------------------------------- 1 | foo.html -------------------------------------------------------------------------------- /testing/templates/foo.jinja: -------------------------------------------------------------------------------- 1 | foo.jinja -------------------------------------------------------------------------------- /askama_axum/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /askama_derive/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /askama_derive/templates/sub/b.html: -------------------------------------------------------------------------------- 1 | bar 2 | -------------------------------------------------------------------------------- /askama_derive/templates/sub/c.html: -------------------------------------------------------------------------------- 1 | baz 2 | -------------------------------------------------------------------------------- /askama_escape/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /askama_parser/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /askama_rocket/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /askama_warp/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /askama_actix/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /askama_derive/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /askama_derive/templates/sub/sub1/d.html: -------------------------------------------------------------------------------- 1 | echo 2 | -------------------------------------------------------------------------------- /askama_escape/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /askama_parser/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /askama_rocket/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /testing/templates/attr.html: -------------------------------------------------------------------------------- 1 | {{ inner.a }} 2 | -------------------------------------------------------------------------------- /testing/templates/foo.html.jinja: -------------------------------------------------------------------------------- 1 | foo.html.jinja -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /testing/templates/filters.html: -------------------------------------------------------------------------------- 1 | {{ strvar|e }} 2 | -------------------------------------------------------------------------------- /testing/templates/generics.html: -------------------------------------------------------------------------------- 1 | {{ t }}{{ u }} 2 | -------------------------------------------------------------------------------- /testing/templates/hello.html: -------------------------------------------------------------------------------- 1 | Hello, {{ name }}! 2 | -------------------------------------------------------------------------------- /testing/templates/included.html: -------------------------------------------------------------------------------- 1 | INCLUDED: {{ s }} 2 | -------------------------------------------------------------------------------- /askama_actix/templates/hello.html: -------------------------------------------------------------------------------- 1 | Hello, {{ name }}! 2 | -------------------------------------------------------------------------------- /askama_axum/templates/hello.html: -------------------------------------------------------------------------------- 1 | Hello, {{ name }}! 2 | -------------------------------------------------------------------------------- /askama_rocket/templates/hello.html: -------------------------------------------------------------------------------- 1 | Hello, {{ name }}! 2 | -------------------------------------------------------------------------------- /askama_warp/templates/hello.html: -------------------------------------------------------------------------------- 1 | Hello, {{ name }}! 2 | -------------------------------------------------------------------------------- /testing/templates/composition.html: -------------------------------------------------------------------------------- 1 | composed: {{ foo }} 2 | -------------------------------------------------------------------------------- /testing/templates/filters_join.html: -------------------------------------------------------------------------------- 1 | {{ s|join(", ") }} 2 | -------------------------------------------------------------------------------- /testing/templates/format.html: -------------------------------------------------------------------------------- 1 | {{ "{:?}"|format(var) }} 2 | -------------------------------------------------------------------------------- /testing/templates/if.html: -------------------------------------------------------------------------------- 1 | {% if cond %}true{% endif %} 2 | -------------------------------------------------------------------------------- /testing/templates/invalid_syntax.html: -------------------------------------------------------------------------------- 1 | {% let 12 = 0 } 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html text eol=lf 2 | *.txt text eol=lf 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [djc] 2 | patreon: dochtman 3 | -------------------------------------------------------------------------------- /testing/templates/cycle1.html: -------------------------------------------------------------------------------- 1 | {% extends "cycle1.html" %} 2 | -------------------------------------------------------------------------------- /testing/templates/cycle2.html: -------------------------------------------------------------------------------- 1 | {% extends "cycle1.html" %} 2 | -------------------------------------------------------------------------------- /testing/templates/nested-attr.html: -------------------------------------------------------------------------------- 1 | {{ inner.holder.a }} 2 | -------------------------------------------------------------------------------- /testing/templates/rust-macros.html: -------------------------------------------------------------------------------- 1 | Hello, {{ hello!() }}! 2 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | coverage 5 | -------------------------------------------------------------------------------- /testing/templates/let-base.html: -------------------------------------------------------------------------------- 1 | {% block body %}{% endblock %} 2 | -------------------------------------------------------------------------------- /testing/templates/tuple-attr.html: -------------------------------------------------------------------------------- 1 | {{ tuple.0 }}{{ tuple.1 }} 2 | -------------------------------------------------------------------------------- /testing/test_minimize.toml: -------------------------------------------------------------------------------- 1 | [general] 2 | whitespace = "minimize" 3 | -------------------------------------------------------------------------------- /testing/test_trim.toml: -------------------------------------------------------------------------------- 1 | [general] 2 | whitespace = "suppress" 3 | -------------------------------------------------------------------------------- /testing/templates/char-literals/char-literal-1.txt: -------------------------------------------------------------------------------- 1 | {% let s = '\a' %} 2 | -------------------------------------------------------------------------------- /testing/templates/char-literals/char-literal-2.txt: -------------------------------------------------------------------------------- 1 | {% let s = '\x' %} 2 | -------------------------------------------------------------------------------- /testing/templates/char-literals/char-literal-5.txt: -------------------------------------------------------------------------------- 1 | {% let s = '\u' %} 2 | -------------------------------------------------------------------------------- /testing/templates/char-literals/char-literal-3.txt: -------------------------------------------------------------------------------- 1 | {% let s = '\x1' %} 2 | -------------------------------------------------------------------------------- /testing/templates/char-literals/char-literal-4.txt: -------------------------------------------------------------------------------- 1 | {% let s = '\x80' %} 2 | -------------------------------------------------------------------------------- /testing/templates/char-literals/char-literal-6.txt: -------------------------------------------------------------------------------- 1 | {% let s = '\u{}' %} 2 | -------------------------------------------------------------------------------- /testing/templates/else.html: -------------------------------------------------------------------------------- 1 | {% if cond %}true{% else %}false{% endif %} 2 | -------------------------------------------------------------------------------- /testing/templates/rust-macros-full-path.html: -------------------------------------------------------------------------------- 1 | Hello, {{ foo::hello2!() }}! 2 | -------------------------------------------------------------------------------- /testing/templates/char-literals/char-literal-7.txt: -------------------------------------------------------------------------------- 1 | {% let s = '\u{110000}' %} 2 | -------------------------------------------------------------------------------- /testing/templates/include_invalid_syntax.html: -------------------------------------------------------------------------------- 1 | {% extends "invalid_syntax.html" %} 2 | -------------------------------------------------------------------------------- /testing/templates/let.html: -------------------------------------------------------------------------------- 1 | {% let v = s %}{{ v }} 2 | {% let (v1,v2) = t %}{{ v1 }}{{ v2 }} -------------------------------------------------------------------------------- /testing/templates/literals.html: -------------------------------------------------------------------------------- 1 | {{ 'a' }} 2 | {{ "a" }} 3 | {{ true }} 4 | {{ false }} 5 | -------------------------------------------------------------------------------- /testing/templates/raw-simple.html: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | {{ name }} 3 | {% endraw %} 4 | -------------------------------------------------------------------------------- /testing/templates/if-let-shadowing.html: -------------------------------------------------------------------------------- 1 | {% if let Some(text) = text %}{{ text }}{% endif %} 2 | -------------------------------------------------------------------------------- /testing/templates/if-let.html: -------------------------------------------------------------------------------- 1 | {% if let Some(some_text) = text %}{{ some_text }}{% endif %} 2 | -------------------------------------------------------------------------------- /testing/templates/json.html: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "{{ foo }}", 3 | "bar": {{ bar|json|safe }} 4 | } 5 | -------------------------------------------------------------------------------- /testing/templates/size-parent.txt: -------------------------------------------------------------------------------- 1 | {% block main %}{% if true %}12345{% endif %}{% endblock %} 2 | -------------------------------------------------------------------------------- /testing/templates/else-if.html: -------------------------------------------------------------------------------- 1 | {% if cond %}true{% else if check %}checked{% else %}false{% endif %} 2 | -------------------------------------------------------------------------------- /testing/templates/include.html: -------------------------------------------------------------------------------- 1 | {% for s in strs %} 2 | {% include "included.html" %} 3 | {%- endfor %} 4 | -------------------------------------------------------------------------------- /testing/templates/import.html: -------------------------------------------------------------------------------- 1 | 2 | {%- import "macro.html" as scope -%} 3 | 4 | {% call scope::thrice(s) %} 5 | -------------------------------------------------------------------------------- /testing/templates/raw-ws.html: -------------------------------------------------------------------------------- 1 | <{% raw -%} {{hello}} {%- endraw %}> 2 | < {%- raw %}{{bye}}{% endraw -%} > 3 | -------------------------------------------------------------------------------- /testing/templates/extend_and_import.html: -------------------------------------------------------------------------------- 1 | {% import "macro.html" as m1 %} 2 | 3 | {% block header %}{% endblock %} 4 | -------------------------------------------------------------------------------- /testing/templates/match-no-ws.html: -------------------------------------------------------------------------------- 1 | {% match foo %}{% when Some with (bar) %}{{ bar }}{% when None %}{% endmatch %} 2 | -------------------------------------------------------------------------------- /testing/templates/deep-import-parent.html: -------------------------------------------------------------------------------- 1 | {%- import "deep-import-child.html" as libj -%} 2 | {% call libj::parent() %} 3 | -------------------------------------------------------------------------------- /testing/templates/deep-nested-macro.html: -------------------------------------------------------------------------------- 1 | {%- import "nested-macro.html" as libi -%} 2 | {%- call libi::parent() -%} 3 | -------------------------------------------------------------------------------- /testing/templates/nested-base.html: -------------------------------------------------------------------------------- 1 | {% block content %} 2 | loopy 3 | {% block foo %}Foo{% endblock %} 4 | {% endblock %} 5 | -------------------------------------------------------------------------------- /testing/templates/size-child-super.txt: -------------------------------------------------------------------------------- 1 | {% extends "size-parent.txt" %} 2 | {% block main %}{% call super() %}{% endblock %} 3 | -------------------------------------------------------------------------------- /testing/templates/if-let-struct.html: -------------------------------------------------------------------------------- 1 | {% if let Digits { one, two, three } = digits %}{{ one }} {{ two }} {{ three }}{% endif %} 2 | -------------------------------------------------------------------------------- /testing/templates/include-extends-included.html: -------------------------------------------------------------------------------- 1 | {% extends "include-extends-base.html" %} 2 | {% block header %}foo{% endblock %} 3 | -------------------------------------------------------------------------------- /testing/templates/option.html: -------------------------------------------------------------------------------- 1 | {% if var.is_some() -%} 2 | some: {{ var.unwrap() }} 3 | {%- else -%} 4 | none 5 | {%- endif %} 6 | -------------------------------------------------------------------------------- /testing/templates/size-child.txt: -------------------------------------------------------------------------------- 1 | {% extends "size-parent.txt" %} 2 | {% block main %}{% if true %}123{% endif %}{% endblock %} 3 | -------------------------------------------------------------------------------- /testing/templates/base.html: -------------------------------------------------------------------------------- 1 | {{ title }} 2 | {% block content %}{% endblock %} 3 | {% block foo %}Foo{% endblock %} 4 | Copyright 2017 5 | -------------------------------------------------------------------------------- /testing/templates/child.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %}({{ title }}) Content goes here{% endblock content %} 3 | -------------------------------------------------------------------------------- /testing/templates/include-extends.html: -------------------------------------------------------------------------------- 1 |
2 |

Welcome

3 | {% include "include-extends-included.html" %} 4 |
5 | -------------------------------------------------------------------------------- /testing/templates/raw-complex.html: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | {% block name %} 3 | {{ name }} 4 | {% endblock %} 5 | {% endraw %} 6 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["djc", "cetra3"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Askama" 7 | -------------------------------------------------------------------------------- /testing/templates/macro-self-arg.html: -------------------------------------------------------------------------------- 1 | {%- macro my_s(slf) -%} 2 | {{ slf.s }} 3 | {%- endmacro -%} 4 | 5 | {%- call my_s(self) -%} 6 | -------------------------------------------------------------------------------- /testing/templates/let-child.html: -------------------------------------------------------------------------------- 1 | {% extends "let-base.html" %} 2 | {% block body -%} 3 | {% let x = 1 %} 4 | {{- x -}} 5 | {%- endblock %} 6 | -------------------------------------------------------------------------------- /testing/tests/ui/no_template_attribute.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | struct NoTemplate; 5 | 6 | fn main() { 7 | } 8 | -------------------------------------------------------------------------------- /testing/templates/include-macro.html: -------------------------------------------------------------------------------- 1 | {% macro m(name) -%} 2 | Hello, {{ name }}! 3 | {%- endmacro -%} 4 | {% include "included-macro.html" %} 5 | -------------------------------------------------------------------------------- /testing/templates/nested-child.html: -------------------------------------------------------------------------------- 1 | {% extends "nested-base.html" %} 2 | {% block content %} 3 | {% block foo %}durpy{% endblock %} 4 | {% endblock content %} 5 | -------------------------------------------------------------------------------- /testing/tests/ui/cycle.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(path = "cycle2.html")] 5 | struct Cycle; 6 | 7 | fn main() { 8 | } 9 | -------------------------------------------------------------------------------- /testing/templates/included-macro.html: -------------------------------------------------------------------------------- 1 | {% macro m2(name) -%} 2 | Howdy, {{ name }}! 3 | {%- endmacro -%} 4 | 5 | {% call m(name) %} 6 | {% call m2(name2) %} 7 | -------------------------------------------------------------------------------- /testing/templates/match-literal-num.html: -------------------------------------------------------------------------------- 1 | {% match item %} 2 | {% when 42 %} 3 | Found answer to everything 4 | {% else %} 5 | Else found {{item}} 6 | {% endmatch %} 7 | -------------------------------------------------------------------------------- /testing/tests/ui/cycle2.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(path = "cycle1.html")] 5 | struct Cycle; 6 | 7 | fn main() { 8 | } 9 | -------------------------------------------------------------------------------- /testing/templates/big-table.html: -------------------------------------------------------------------------------- 1 | 2 | {% for row in table %} 3 | {% for col in row %}{% endfor %} 4 | {% endfor %} 5 |
{{ col|escape }}
6 | -------------------------------------------------------------------------------- /testing/templates/deep-import-child.html: -------------------------------------------------------------------------------- 1 | {%- import "nested-macro.html" as libi -%} 2 | {%- macro parent() -%} 3 | {% call libi::parent() %} 4 | {%- endmacro -%} 5 | -------------------------------------------------------------------------------- /testing/templates/let-decl.html: -------------------------------------------------------------------------------- 1 | {% let val -%} 2 | {% if cond -%} 3 | {% let val = "foo" -%} 4 | {% else -%} 5 | {% let val = s -%} 6 | {% endif -%} 7 | {{ val }} 8 | -------------------------------------------------------------------------------- /testing/templates/ranges.txt: -------------------------------------------------------------------------------- 1 | {{ foo[..]|join("") }} 2 | {{ foo[0 + 1..]|join("") }} 3 | {% let bar = 1 %} 4 | {{ foo[..bar]|join("") }} 5 | {{ foo[..=bar]|join("") }} 6 | -------------------------------------------------------------------------------- /testing/templates/render_in_place.html: -------------------------------------------------------------------------------- 1 | Section 1: {{ s1 }} 2 | Section 2: {{ s2|safe }} 3 | Section 3 for: 4 | {% for s in s3.as_slice() -%} 5 | * {{ s }} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /testing/templates/let-destruct-tuple.html: -------------------------------------------------------------------------------- 1 | {% let (a, ((b, c), (d))) = abcd %}{{a}}{{b}}{{c}}{{d}} 2 | {% let (a, (_, d)) = abcd %}{{a}}{{d}} 3 | {% let (((a))) = abcd.0 %}{{a}} 4 | -------------------------------------------------------------------------------- /testing/templates/nested-for.html: -------------------------------------------------------------------------------- 1 | {% for seq in seqs -%} 2 | {{ loop.index }} 3 | {% for v in seq -%} 4 | {{ loop.index0 }}{{ v }} 5 | {%- endfor -%} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /testing/templates/simple.html: -------------------------------------------------------------------------------- 1 | {# our very first test! #} 2 | hello world, {{ strvar }} 3 | with number: {{ num }} 4 | Iñtërnâtiônàlizætiøn is important 5 | in vars too: {{ i18n }} 6 | -------------------------------------------------------------------------------- /testing/templates/precedence.html: -------------------------------------------------------------------------------- 1 | {{ 3 * 4 / 2 -}} 2 | {{ 26 / 2 % 7 -}} 3 | {{ 3 % 2 * 6 -}} 4 | {{ 1 * 2 + 4 -}} 5 | {{ 11 - 15 / 3 -}} 6 | {{ 4 + 5 % 3 -}} 7 | {{ 4 | 2 + 5 & 2 -}} 8 | -------------------------------------------------------------------------------- /testing/tests/ui/incorrect_path.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(path = "thisdoesnotexist.html")] 5 | struct MyTemplate; 6 | 7 | fn main() { 8 | } 9 | -------------------------------------------------------------------------------- /testing/templates/literals-escape.html: -------------------------------------------------------------------------------- 1 | {{ '\x41' }}{{ '\n' }}{{ '\r' }}{{ '\t' }}{{ '\\' }}{{ '\0' }}{{ '\u{2665}' }}{{ '\'' }}{{ '\"' }}{{ '"' }} 2 | {{ "\x41\n\r\t\\\0\u{2665}\'\"'" }} 3 | -------------------------------------------------------------------------------- /testing/templates/simple-no-escape.txt: -------------------------------------------------------------------------------- 1 | {# our very first test! #} 2 | hello world, {{ strvar }} 3 | with number: {{ num }} 4 | Iñtërnâtiônàlizætiøn is important 5 | in vars too: {{ i18n }} 6 | -------------------------------------------------------------------------------- /testing/templates/match-literal-char.html: -------------------------------------------------------------------------------- 1 | {% match item %} 2 | {% when 'a' %} 3 | Found literal a 4 | {% when 'b' %} 5 | Found literal b 6 | {% else %} 7 | Else found {{item}} 8 | {% endmatch %} 9 | -------------------------------------------------------------------------------- /testing/templates/if-let-else.html: -------------------------------------------------------------------------------- 1 | {%- if !cond -%} 2 | !cond 3 | {%- else if let Ok(ok) = value -%} 4 | {{ ok }} 5 | {%- else if let Err(err) = value -%} 6 | {{ err }} 7 | {%- endif -%} 8 | -------------------------------------------------------------------------------- /testing/templates/match-literal.html: -------------------------------------------------------------------------------- 1 | {% match item %} 2 | {% when "foo" %} 3 | Found literal foo 4 | {% when "bar" %} 5 | Found literal bar 6 | {% else %} 7 | Else found {{item}} 8 | {% endmatch %} 9 | -------------------------------------------------------------------------------- /testing/templates/include-extends-base.html: -------------------------------------------------------------------------------- 1 |
2 |

Below me is the header

3 | {% block header %}{% endblock %} 4 |

Above me is the header

5 |
6 | Hello, {{ name }}! 7 | -------------------------------------------------------------------------------- /testing/templates/match-opt.html: -------------------------------------------------------------------------------- 1 | {% match item %} 2 | {% when Some with ("foo") %} 3 | Found literal foo 4 | {% when Some with (val) %} 5 | Found {{val}} 6 | {% when None %} 7 | Not Found 8 | {% endmatch %} 9 | -------------------------------------------------------------------------------- /testing/tests/ui/macro-super.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{%- macro super() -%}{%- endmacro -%}", ext = "html")] 5 | struct MacroSuper; 6 | 7 | fn main() { 8 | } 9 | -------------------------------------------------------------------------------- /testing/templates/fragment-base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block body %} 5 |

Parent body content

6 | {% endblock %} 7 | {% block other_body %}{% endblock %} 8 | 9 | 10 | -------------------------------------------------------------------------------- /testing/templates/match-opt-bool.html: -------------------------------------------------------------------------------- 1 | {% match item %} 2 | {% when Some with (true) %} 3 | Found Some(true) 4 | {% when Some with (false) %} 5 | Found Some(false) 6 | {% when None %} 7 | Not Found 8 | {% endmatch %} 9 | -------------------------------------------------------------------------------- /testing/templates/precedence-for.html: -------------------------------------------------------------------------------- 1 | {% for s in strings %} 2 | {{- loop.index0 }}. {{ s }}{{ 2 * loop.index }}{% if !loop.first %}{% else %} (first){% endif %}{% if loop.last %} (last){% endif %} 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /testing/templates/fragment-include.html: -------------------------------------------------------------------------------- 1 | {% extends "fragment-base.html" %} 2 | 3 | {% block body %} 4 | {% include "included.html" %} 5 | {% endblock %} 6 | 7 | {% block other_body %} 8 |

Don't render me.

9 | {% endblock %} -------------------------------------------------------------------------------- /testing/tests/ui/lit_on_assignment_lhs.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | source = "{%let 7=x%}", 6 | ext = "txt" 7 | )] 8 | struct MyTemplate; 9 | 10 | fn main() { 11 | } 12 | -------------------------------------------------------------------------------- /testing/templates/fragment-mid-super.html: -------------------------------------------------------------------------------- 1 | {% extends "fragment-base.html" %} 2 | 3 | {% block body %} 4 | [{% call super() %}] 5 | {% endblock %} 6 | 7 | {% block other_body %} 8 | ({% call super() %}) 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /testing/tests/ui/name_mismatch_endblock.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{% block foo %}{% endblock not_foo %}", ext = "html")] 5 | struct NameMismatchEndBlock; 6 | 7 | fn main() { 8 | } 9 | -------------------------------------------------------------------------------- /askama_parser/benches/librustdoc/item_info.html: -------------------------------------------------------------------------------- 1 | {% if !items.is_empty() %} 2 | 3 | {% for item in items %} 4 | {{item|safe}} {# #} 5 | {% endfor %} 6 | 7 | {% endif %} 8 | -------------------------------------------------------------------------------- /testing/templates/fragment-simple.html: -------------------------------------------------------------------------------- 1 | {% extends "fragment-base.html" %} 2 | 3 | {% block body %} 4 |

Hello {{ name }}!

5 | {% endblock %} 6 | 7 | {% block other_body %} 8 |

Don't render me.

9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /testing/templates/blocks.txt: -------------------------------------------------------------------------------- 1 | {% block index %} 2 | Section: {{ s1 }} 3 | {% endblock %} 4 | 5 | {% block section -%} 6 | [ 7 | {%- for value in values -%} 8 | {{ value }} 9 | {%- endfor -%} 10 | ] 11 | {%- endblock %} 12 | -------------------------------------------------------------------------------- /testing/templates/match-enum-or.html: -------------------------------------------------------------------------------- 1 | The card is 2 | {%- match suit %} 3 | {%- when Suit::Clubs or Suit::Spades -%} 4 | {{ " black" }} 5 | {%- when Suit::Diamonds or Suit::Hearts -%} 6 | {{ " red" }} 7 | {%- endmatch %} 8 | 9 | -------------------------------------------------------------------------------- /testing/tests/ui/name_mismatch_endmacro.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{% macro foo(arg) %} {{arg}} {% endmacro not_foo %}", ext = "html")] 5 | struct NameMismatchEndMacro; 6 | 7 | fn main() { 8 | } 9 | -------------------------------------------------------------------------------- /testing/tests/ui/break_outside_of_loop.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | source = "Have a {%break%}, have a parsing error!", 6 | ext = "txt" 7 | )] 8 | struct MyTemplate; 9 | 10 | fn main() { 11 | } 12 | -------------------------------------------------------------------------------- /testing/templates/deep-kid.html: -------------------------------------------------------------------------------- 1 | {% extends "deep-mid.html" %} 2 | {% import "macro.html" as libk %} 3 | 4 | {% block head %} 5 | 6 | {% endblock %} 7 | 8 | {% block content %} 9 | {% call libk::thrice(item) %} 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /testing/templates/nested-macro-args.html: -------------------------------------------------------------------------------- 1 | {%- macro outer(first) -%} 2 | {%- call inner(first, "second") -%} 3 | {%- endmacro -%} 4 | 5 | {%- macro inner(first, second) -%} 6 | {{ first }} {{ second }} 7 | {%- endmacro -%} 8 | 9 | {%- call outer("first") -%} 10 | -------------------------------------------------------------------------------- /testing/templates/nested-macro.html: -------------------------------------------------------------------------------- 1 | {%- macro child0() -%} 2 | foo 3 | {%- endmacro -%} 4 | 5 | {%- macro child1() -%} 6 | {% call child0() %} 7 | {%- endmacro -%} 8 | 9 | {%- macro parent() -%} 10 | {% call child1() %} 11 | {%- endmacro -%} 12 | -------------------------------------------------------------------------------- /testing/templates/for.html: -------------------------------------------------------------------------------- 1 | {% for s in strings %} 2 | {{- loop.index0 }}. {{ s }}{% if loop.first %} (first){% endif %} 3 | {% endfor %} 4 | {% for (s1, s2, ) in tuple_strings %} 5 | {{- loop.index0 }}. {{ s1 }},{{ s2 }}{% if loop.first %} (first){% endif %} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /testing/tests/ui/filter_block_ws.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{% filter lower|indent(2) - %} 5 | HELLO 6 | {{v}} 7 | {%- endfilter %}", ext = "html")] 8 | struct A; 9 | 10 | fn main() { 11 | A.render().unwrap(); 12 | } 13 | -------------------------------------------------------------------------------- /testing/tests/ui/loop_cycle_empty.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | source = r#"{% for v in values %}{{ loop.cycle([]) }}{{ v }},{% endfor %}"#, 6 | ext = "txt" 7 | )] 8 | struct ForCycleEmpty; 9 | 10 | fn main() { 11 | } 12 | -------------------------------------------------------------------------------- /testing/templates/fragment-super.html: -------------------------------------------------------------------------------- 1 | {% extends "fragment-base.html" %} 2 | 3 | {% block body %} 4 |

Hello {{ name }}!

5 | {% call super() %} 6 | {% endblock %} 7 | 8 | {% block other_body %} 9 |

Don't render me.

10 | {% call super() %} 11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /testing/tests/ui/typo_in_keyword.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | source = "{%for i in 1..=10%}{{i}}{%endfo%}\n1234567890123456789012345678901234567890", 6 | ext = "txt" 7 | )] 8 | struct MyTemplate; 9 | 10 | fn main() { 11 | } 12 | -------------------------------------------------------------------------------- /testing/templates/fragment-unused-expr.html: -------------------------------------------------------------------------------- 1 | {% extends "fragment-base.html" %} 2 | 3 | {{ not_required }} 4 | 5 | {% block body %} 6 |

{{ required }}

7 | {% endblock %} 8 | 9 | {% block other_body %} 10 | {{ not_required_2 }} 11 |

Don't render me.

12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /testing/templates/macro-import-str-cmp.html: -------------------------------------------------------------------------------- 1 | {%- import "macro-import-str-cmp-macro.html" as macros -%} 2 | 3 | A 4 | 5 | {%- call macros::strcmp("foo") -%} 6 | 7 | B 8 | 9 | {%- call macros::strcmp("bar") -%} 10 | 11 | C 12 | 13 | {%- call macros::strcmp("cat") -%} 14 | 15 | D 16 | -------------------------------------------------------------------------------- /fuzz/README.md: -------------------------------------------------------------------------------- 1 | # Fuzzing 2 | 3 | Install `cargo-fuzz`: 4 | 5 | ```sh 6 | cargo install -f cargo-fuzz 7 | ``` 8 | 9 | Run any available target where `$target` is the name of the target. 10 | 11 | ```sh 12 | cargo fuzz list # get list of targets 13 | cargo +nightly fuzz run $target 14 | ``` -------------------------------------------------------------------------------- /testing/tests/ui/duplicated_template_attribute.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | source = "🙂", 6 | ext = "txt" 7 | )] 8 | #[template( 9 | source = "🙃", 10 | ext = "txt" 11 | )] 12 | struct TwoEmojis; 13 | 14 | fn main() { 15 | } 16 | -------------------------------------------------------------------------------- /testing/templates/fragment-nested-super.html: -------------------------------------------------------------------------------- 1 | {% extends "fragment-mid-super.html" %} 2 | 3 | {% block body %} 4 |

Hello {{ name }}!

5 | {% call super() %} 6 | {% endblock %} 7 | 8 | {% block other_body %} 9 |

Don't render me.

10 | {% call super() %} 11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /testing/templates/rust-macro-args.html: -------------------------------------------------------------------------------- 1 | {{ call_a_or_b_on_tail!((a: year, b: month, c: day), call a: 2021, "July", (0+2)) }} 2 | {{ call_a_or_b_on_tail!((a: year, b: month, c: day), call b: 2021, "July", (0+2)) }} 3 | {{ call_a_or_b_on_tail!((a: year, b: day, c: month), call b: 2021, "July", (0+2)) }} 4 | -------------------------------------------------------------------------------- /testing/templates/macro-no-args.html: -------------------------------------------------------------------------------- 1 | 1 2 | 3 | {%- macro empty -%} 4 | the best thing 5 | {%- endmacro -%} 6 | 7 | 1 8 | 9 | {%- call empty() -%} 10 | 11 | 1 12 | 13 | {%- macro whole() -%} 14 | we've ever done 15 | {%- endmacro -%} 16 | 17 | 11 18 | 19 | {%- call whole -%} 20 | 21 | 11 22 | -------------------------------------------------------------------------------- /testing/tests/ui/block_and_vars.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = r#"{% extends "extend_and_import.html" %} 5 | 6 | {% let x = 12 %} 7 | {% block header -%} 8 | {{ x }} 9 | {% endblock %}"#, ext = "html")] 10 | struct A; 11 | 12 | fn main() { 13 | } 14 | -------------------------------------------------------------------------------- /testing/templates/macro-short-circuit.html: -------------------------------------------------------------------------------- 1 | {% macro foo(b) -%} 2 | {{ b }} 3 | {%- endmacro -%} 4 | {% call foo(true) -%} 5 | {% call foo(true && true) -%} 6 | {% call foo(true && true && true) -%} 7 | {% call foo(false) -%} 8 | {% call foo(false || true) -%} 9 | {% call foo(false || false || true) -%} 10 | -------------------------------------------------------------------------------- /testing/templates/fragment-nested-block.html: -------------------------------------------------------------------------------- 1 | {% extends "fragment-base.html" %} 2 | 3 | {% block body %} 4 |

Don't render me!

5 | {% block nested %} 6 |

I should be here.

7 | {% endblock %} 8 | {% endblock %} 9 | 10 | {% block other_body %} 11 |

Don't render me!

12 | {% endblock %} 13 | 14 | -------------------------------------------------------------------------------- /testing/templates/match-option-result-option.html: -------------------------------------------------------------------------------- 1 | {%- match foo -%} 2 | {%- when None -%} 3 | nothing 4 | {%- when Some(Err(err)) -%} 5 | err={{err}} 6 | {%- when Some(Ok(None)) -%} 7 | num=absent 8 | {%- when Some(Ok(Some(num))) -%} 9 | num={{num}} 10 | {%- endmatch -%} 11 | -------------------------------------------------------------------------------- /testing/tests/ui/block_and_vars.stderr: -------------------------------------------------------------------------------- 1 | error[E0609]: no field `x` on type `&A` 2 | --> tests/ui/block_and_vars.rs:3:10 3 | | 4 | 3 | #[derive(Template)] 5 | | ^^^^^^^^ unknown field 6 | | 7 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /testing/tests/ui/loop_cycle_empty.stderr: -------------------------------------------------------------------------------- 1 | error: loop.cycle(…) cannot use an empty array 2 | --> tests/ui/loop_cycle_empty.rs:3:10 3 | | 4 | 3 | #[derive(Template)] 5 | | ^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /testing/tests/ui/no_template_attribute.stderr: -------------------------------------------------------------------------------- 1 | error: no attribute 'template' found 2 | --> tests/ui/no_template_attribute.rs:3:10 3 | | 4 | 3 | #[derive(Template)] 5 | | ^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [licenses] 2 | version = 2 3 | allow = ["Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC", "MIT", "Unicode-3.0", "Unicode-DFS-2016"] 4 | private = { ignore = true } 5 | 6 | [[licenses.clarify]] 7 | name = "ring" 8 | expression = "ISC AND MIT AND OpenSSL" 9 | license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] 10 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_parser.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use askama_parser::*; 3 | use libfuzzer_sys::fuzz_target; 4 | 5 | fuzz_target!(|data: &[u8]| { 6 | // fuzzed code goes here 7 | if let Ok(data) = std::str::from_utf8(data) { 8 | let _ = Ast::from_str(data, None, &Syntax::default()).is_ok(); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /testing/templates/deep-base.html: -------------------------------------------------------------------------------- 1 | {% import "macro.html" as libb %} 2 | 3 | 4 | {% block head %} 5 | 6 | {% endblock %} 7 | 8 | 9 | {% block body %} 10 | {% call libb::thrice("nav") %} 11 | Copyright {{ year }} 12 | {% endblock %} 13 | 14 | 15 | -------------------------------------------------------------------------------- /testing/templates/if-let-with-for.html: -------------------------------------------------------------------------------- 1 | {%- if let Some(thing) = thing -%} 2 | {%- for item in thing.items -%} 3 | {{ item }} 4 | {%- endfor -%} 5 | {%- endif -%} 6 | 7 | {%- if let Some(thing) = thing -%} 8 | {%- for item in thing.items -%} 9 | {{ item }} 10 | {%- endfor -%} 11 | {%- endif -%} 12 | -------------------------------------------------------------------------------- /testing/tests/ui/loop_cycle_wrong_argument_count.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | source = r#"{% for v in values %}{{ loop.cycle("r", "g", "b") }}{{ v }},{% endfor %}"#, 6 | ext = "txt" 7 | )] 8 | struct ForCycle<'a> { 9 | values: &'a [u8], 10 | } 11 | 12 | fn main() { 13 | } 14 | -------------------------------------------------------------------------------- /testing/tests/ui/duplicated_template_attribute.stderr: -------------------------------------------------------------------------------- 1 | error: duplicated 'template' attribute 2 | --> tests/ui/duplicated_template_attribute.rs:3:10 3 | | 4 | 3 | #[derive(Template)] 5 | | ^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /testing/templates/for-break-continue.html: -------------------------------------------------------------------------------- 1 | {%- for v in values -%} 2 | x {{- v -}} 3 | {%- if matches!(v, x if *x > 9) -%} 4 | {%- if matches!(v, x if *x % 2 == 0) -%} 5 | {%- break -%} 6 | {%- else -%} 7 | {%- continue -%} 8 | {%- endif -%} 9 | {%- endif -%} 10 | y 11 | {%- endfor -%} 12 | -------------------------------------------------------------------------------- /testing/tests/ui/loop_cycle_wrong_argument_count.stderr: -------------------------------------------------------------------------------- 1 | error: loop.cycle(…) expects exactly one argument 2 | --> $DIR/loop_cycle_wrong_argument_count.rs:3:10 3 | | 4 | 3 | #[derive(Template)] 5 | | ^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /testing/templates/for-range.html: -------------------------------------------------------------------------------- 1 | {% for s in 0..2 -%} 2 | foo{% if loop.first %} (first){% endif %}{% if loop.last %} (last){% endif %} 3 | {% endfor -%} 4 | 5 | {% for s in init..1 -%} 6 | bar 7 | {% endfor -%} 8 | 9 | {% for s in 0..end -%} 10 | foo 11 | {% endfor -%} 12 | 13 | {% for s in init..end -%} 14 | bar 15 | {% endfor -%} 16 | -------------------------------------------------------------------------------- /testing/templates/macro-import-str-cmp-macro.html: -------------------------------------------------------------------------------- 1 | {% macro strcmp0(s, other) -%} 2 | {%- if s == "foo" -%} 3 | foo 4 | {%- else if s == other -%} 5 | other 6 | {%- else -%} 7 | neither 8 | {%- endif -%} 9 | {% endmacro %} 10 | 11 | {% macro strcmp(s) %} 12 | {%- call strcmp0(s, "bar") -%} 13 | {% endmacro %} 14 | -------------------------------------------------------------------------------- /testing/templates/named-end.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {# Testing named "endmacro" #} 3 | {% macro foo(b) -%} 4 | {% if b %}t{% else %}f{% endif -%} 5 | {% endmacro foo -%} 6 | {# Testing named endblock declaration #} 7 | {% block what %}{% endblock what %} 8 | {# Testing named endblock call #} 9 | {% block foo %}tadam{% endblock foo %} 10 | -------------------------------------------------------------------------------- /testing/tests/ui/lit_on_assignment_lhs.stderr: -------------------------------------------------------------------------------- 1 | error: literals are not allowed on the left-hand side of an assignment 2 | --> tests/ui/lit_on_assignment_lhs.rs:3:10 3 | | 4 | 3 | #[derive(Template)] 5 | | ^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /testing/tests/coerce.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(path = "if-coerce.html")] 5 | struct IfCoerceTemplate { 6 | t: bool, 7 | f: bool, 8 | } 9 | 10 | #[test] 11 | fn test_coerce() { 12 | let t = IfCoerceTemplate { t: true, f: false }; 13 | assert_eq!(t.render().unwrap(), "ftftfttftelseifelseif"); 14 | } 15 | -------------------------------------------------------------------------------- /testing/templates/macro.html: -------------------------------------------------------------------------------- 1 | 1 2 | 3 | {%- macro thrice(param) -%} 4 | 5 | {{ param }} {{ param }} {{ param }} 6 | 7 | {%- endmacro -%} 8 | 9 | 2 10 | 11 | {%- call thrice(s) -%} 12 | 13 | 3 14 | 15 | {%- macro twice(param) -%} 16 | 17 | {{ param }} {{ param }} 18 | 19 | {%- endmacro twice -%} 20 | 21 | 4 22 | 23 | {%- call twice(s) -%} 24 | 25 | 5 26 | -------------------------------------------------------------------------------- /testing/templates/match-custom-enum.html: -------------------------------------------------------------------------------- 1 | {% match color %} 2 | {% when Color::Rgb with {r, g: g, b: blue} %} 3 | Colorful: #{{ "{:02X}"|format(r) }}{{ "{:02X}"|format(g) }}{{ "{:02X}"|format(blue) }} 4 | {% when Color::GrayScale with (val) %} 5 | Gray: #{{ "{:02X}"|format(val) }}{{ "{:02X}"|format(val) }}{{ "{:02X}"|format(val) }} 6 | {% else %} 7 | CMYK not supported 8 | {% endmatch %} 9 | -------------------------------------------------------------------------------- /askama_parser/benches/librustdoc/type_layout_size.html: -------------------------------------------------------------------------------- 1 | {% if is_unsized %} 2 | (unsized) 3 | {% else %} 4 | {% if size == 1 %} 5 | 1 byte 6 | {% else %} 7 | {{ size +}} bytes 8 | {% endif %} 9 | {% if is_uninhabited %} 10 | {# +#} (uninhabited) 11 | {% endif %} 12 | {% endif %} 13 | -------------------------------------------------------------------------------- /testing/templates/teams.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ year }} 4 | 5 | 6 |

CSL {{ year }}

7 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /testing/tests/ui/extends.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | source = r#"{%- extends "whatever.html" %}"#, 6 | ext = "html" 7 | )] 8 | struct ExtendsPre; 9 | 10 | #[derive(Template)] 11 | #[template( 12 | source = r#"{% extends "whatever.html" -%}"#, 13 | ext = "html" 14 | )] 15 | struct ExtendsPost; 16 | 17 | fn main() { 18 | } 19 | -------------------------------------------------------------------------------- /testing/tests/ui/filter_block_ws.stderr: -------------------------------------------------------------------------------- 1 | error: failed to parse template source at row 1, column 27 near: 2 | " %}\nHELLO\n{{v}}\n{%- endfilter %}" 3 | --> tests/ui/filter_block_ws.rs:3:10 4 | | 5 | 3 | #[derive(Template)] 6 | | ^^^^^^^^ 7 | | 8 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 9 | -------------------------------------------------------------------------------- /testing/tests/ui/match_with_extra.stderr: -------------------------------------------------------------------------------- 1 | error: failed to parse template source at row 3, column 4 near: 2 | "// Help, I forgot how to write comments!"... 3 | --> tests/ui/match_with_extra.rs:3:10 4 | | 5 | 3 | #[derive(Template)] 6 | | ^^^^^^^^ 7 | | 8 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 9 | -------------------------------------------------------------------------------- /testing/tests/ui/typo_in_keyword.stderr: -------------------------------------------------------------------------------- 1 | error: failed to parse template source at row 1, column 26 near: 2 | "endfo%}\n12345678901234567890123456789012"... 3 | --> tests/ui/typo_in_keyword.rs:3:10 4 | | 5 | 3 | #[derive(Template)] 6 | | ^^^^^^^^ 7 | | 8 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 9 | -------------------------------------------------------------------------------- /testing/tests/ui/excessive_nesting.stderr: -------------------------------------------------------------------------------- 1 | error: failed to parse template source at row 14, column 34 near: 2 | "%}{%if 1%}{%if 1%}{%if 1%}{%if 1%}{%if 1"... 3 | --> tests/ui/excessive_nesting.rs:3:10 4 | | 5 | 3 | #[derive(Template)] 6 | | ^^^^^^^^ 7 | | 8 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 9 | -------------------------------------------------------------------------------- /testing/tests/ui/incorrect_path.stderr: -------------------------------------------------------------------------------- 1 | error: template "thisdoesnotexist.html" not found in directories ["$WORKSPACE/target/tests/trybuild/askama_testing/templates"] 2 | --> tests/ui/incorrect_path.rs:3:10 3 | | 4 | 3 | #[derive(Template)] 5 | | ^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /testing/tests/ui/error_file_path.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(path = "invalid_syntax.html")] 5 | struct A; 6 | 7 | #[derive(Template)] 8 | #[template(path = "include_invalid_syntax.html")] 9 | struct B; 10 | 11 | #[derive(Template)] 12 | #[template(source = r#"{% extends "include_invalid_syntax.html" %}"#, ext = "txt")] 13 | struct C; 14 | 15 | fn main() { 16 | } 17 | -------------------------------------------------------------------------------- /testing/tests/ui/match_with_extra.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | ext = "txt", 6 | source = r#" 7 | {%- match good -%} 8 | // Help, I forgot how to write comments! 9 | {%- when true %} 10 | good 11 | {%- when _ -%} 12 | bad 13 | {%- endmatch -%}"# 14 | )] 15 | struct MatchWithExtra { 16 | good: bool, 17 | } 18 | 19 | fn main() { 20 | } 21 | -------------------------------------------------------------------------------- /testing/tests/ui/macro-super.stderr: -------------------------------------------------------------------------------- 1 | error: 'super' is not a valid name for a macro 2 | failed to parse template source at row 1, column 2 near: 3 | "- macro super() -%}{%- endmacro -%}" 4 | --> tests/ui/macro-super.rs:3:10 5 | | 6 | 3 | #[derive(Template)] 7 | | ^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Askama](./askama.md) 4 | - [Getting started](./getting_started.md) 5 | - [Creating templates](./creating_templates.md) 6 | - [Debugging](./debugging.md) 7 | - [Configuration](./configuration.md) 8 | - [Template syntax](./template_syntax.md) 9 | - [Filters](./filters.md) 10 | - [Integrations](./integrations.md) 11 | - [Performance](./performance.md) 12 | - [Template expansion](./template_expansion.md) 13 | -------------------------------------------------------------------------------- /testing/tests/ui/break_outside_of_loop.stderr: -------------------------------------------------------------------------------- 1 | error: you can only `break` inside a `for` loop 2 | failed to parse template source at row 1, column 9 near: 3 | "break%}, have a parsing error!" 4 | --> tests/ui/break_outside_of_loop.rs:3:10 5 | | 6 | 3 | #[derive(Template)] 7 | | ^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /testing/tests/ui/name_mismatch_endblock.stderr: -------------------------------------------------------------------------------- 1 | error: expected name `foo` in `endblock` tag, found `not_foo` 2 | failed to parse template source at row 1, column 27 near: 3 | "not_foo %}" 4 | --> tests/ui/name_mismatch_endblock.rs:3:10 5 | | 6 | 3 | #[derive(Template)] 7 | | ^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /testing/tests/ui/name_mismatch_endmacro.stderr: -------------------------------------------------------------------------------- 1 | error: expected name `foo` in `endmacro` tag, found `not_foo` 2 | failed to parse template source at row 1, column 41 near: 3 | "not_foo %}" 4 | --> tests/ui/name_mismatch_endmacro.rs:3:10 5 | | 6 | 3 | #[derive(Template)] 7 | | ^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "askama", 4 | "askama_actix", 5 | "askama_axum", 6 | "askama_derive", 7 | "askama_escape", 8 | "askama_parser", 9 | "askama_rocket", 10 | "askama_warp", 11 | "testing", 12 | ] 13 | 14 | exclude = ["fuzz"] 15 | 16 | resolver = "2" 17 | 18 | default-members = [ 19 | "askama", 20 | "askama_derive", 21 | "askama_escape", 22 | "askama_parser", 23 | "testing", 24 | ] 25 | -------------------------------------------------------------------------------- /testing/templates/let-shadow.html: -------------------------------------------------------------------------------- 1 | {%- let a = 1 -%} 2 | {%- let b -%} 3 | 4 | {%- if cond -%} 5 | {%- let b = 22 -%} 6 | {{ b }}- 7 | 8 | {%- let b = 33 -%} 9 | {{ a }}-{{ b }}- 10 | {%- else -%} 11 | {%- let b = 222 -%} 12 | {{ b }}- 13 | 14 | {%- let b = 333 -%} 15 | {{ a }}-{{ b }}- 16 | 17 | {%- let (a, b) = Self::tuple() -%} 18 | {{ a }}-{{ b }}- 19 | {%- endif -%} 20 | 21 | {%- let a = 11 -%} 22 | {{ a }}-{{ b }} 23 | -------------------------------------------------------------------------------- /testing/tests/ui/cycle2.stderr: -------------------------------------------------------------------------------- 1 | error: cyclic dependency in graph [ 2 | "\"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle1.html/" --> \"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle1.html/"", 3 | ] 4 | --> tests/ui/cycle2.rs:3:10 5 | | 6 | 3 | #[derive(Template)] 7 | | ^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /askama_parser/benches/librustdoc/LICENSE.md: -------------------------------------------------------------------------------- 1 | The files in this folder were copied verbatim from [librustdoc/html/templates] in version 2 | eeb59f16a5f40e14dc29b95155b7f2569329e3ec. Dual licensed under MIT OR Apache-2.0. 3 | 4 | Please find the authors in [their Git history]. 5 | 6 | [librustdoc/html/templates]: 7 | [their Git history]: 8 | -------------------------------------------------------------------------------- /testing/templates/deep-mid.html: -------------------------------------------------------------------------------- 1 | {% extends "deep-base.html" %} 2 | {% import "macro.html" as libm %} 3 | 4 | {% block head %} 5 | {{ title }} 6 | {% call super() %} 7 | {% endblock %} 8 | 9 | {% block body %} 10 |
11 |
12 | {% block content %} 13 | No content found 14 | {% endblock %} 15 |
16 | 19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /askama_warp/tests/warp.rs: -------------------------------------------------------------------------------- 1 | use askama_warp::Template; 2 | use warp::Filter; 3 | 4 | #[derive(Template)] 5 | #[template(path = "hello.html")] 6 | struct HelloTemplate<'a> { 7 | name: &'a str, 8 | } 9 | 10 | #[tokio::test] 11 | async fn test_warp() { 12 | let filter = warp::get().map(|| HelloTemplate { name: "world" }); 13 | 14 | let res = warp::test::request().reply(&filter).await; 15 | 16 | assert_eq!(res.status(), 200); 17 | assert_eq!(res.body(), "Hello, world!"); 18 | } 19 | -------------------------------------------------------------------------------- /book/src/performance.md: -------------------------------------------------------------------------------- 1 | # Performance 2 | 3 | ## Slow Debug Recompilations 4 | 5 | If you experience slow compile times when iterating with lots of templates, 6 | you can compile Askama's derive macros with a higher optimization level. 7 | This can speed up recompilation times dramatically. 8 | 9 | Add the following to `Cargo.toml` or `.cargo/config.toml`: 10 | ```rust 11 | [profile.dev.package.askama_derive] 12 | opt-level = 3 13 | ``` 14 | 15 | This may affect clean compile times in debug mode, but incremental compiles 16 | will be faster. 17 | -------------------------------------------------------------------------------- /testing/tests/extend.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | source = r#"{% extends "extend_and_import.html" %} 6 | {%- import "macro.html" as m2 -%} 7 | 8 | {%- macro another(param) -%} 9 | 10 | --> {{ param }} 11 | 12 | {%- endmacro -%} 13 | 14 | {% block header -%} 15 | {% call m1::twice(1) %} 16 | {% call m2::twice(2) %} 17 | {% call another(3) %} 18 | {%- endblock -%} 19 | "#, 20 | ext = "txt" 21 | )] 22 | struct A; 23 | 24 | #[test] 25 | fn test_macro_in_block_inheritance() { 26 | assert_eq!(A.render().unwrap(), "\n\n1 1\n2 2\n--> 3"); 27 | } 28 | -------------------------------------------------------------------------------- /testing/tests/if.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template( 5 | source = r#"{%- if s == "" -%} 6 | empty 7 | {%- else if s == "b" -%} 8 | b 9 | {%- elif s == "c" -%} 10 | c 11 | {%- else -%} 12 | else 13 | {%- endif -%}"#, 14 | ext = "txt" 15 | )] 16 | struct If<'a> { 17 | s: &'a str, 18 | } 19 | 20 | #[test] 21 | fn test_if() { 22 | assert_eq!(If { s: "" }.render().unwrap(), "empty"); 23 | assert_eq!(If { s: "b" }.render().unwrap(), "b"); 24 | assert_eq!(If { s: "c" }.render().unwrap(), "c"); 25 | assert_eq!(If { s: "d" }.render().unwrap(), "else"); 26 | } 27 | -------------------------------------------------------------------------------- /askama_axum/README.md: -------------------------------------------------------------------------------- 1 | # askama_axum: Askama integration with Axum 2 | 3 | [![Documentation](https://docs.rs/askama_axum/badge.svg)](https://docs.rs/askama_axum/) 4 | [![Latest version](https://img.shields.io/crates/v/askama_axum.svg)](https://crates.io/crates/askama_axum) 5 | [![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) 6 | [![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/ZucwjE6bmT) 7 | 8 | Integration of the [Askama](https://github.com/djc/askama) templating engine in 9 | code building on the Axum web framework. 10 | -------------------------------------------------------------------------------- /askama_warp/README.md: -------------------------------------------------------------------------------- 1 | # askama_warp: Askama integration with warp 2 | 3 | [![Documentation](https://docs.rs/askama_warp/badge.svg)](https://docs.rs/askama_warp/) 4 | [![Latest version](https://img.shields.io/crates/v/askama_warp.svg)](https://crates.io/crates/askama_warp) 5 | [![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) 6 | [![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/ZucwjE6bmT) 7 | 8 | Integration of the [Askama](https://github.com/djc/askama) templating engine in 9 | code building on the warp web framework. 10 | -------------------------------------------------------------------------------- /testing/tests/ui/cycle.stderr: -------------------------------------------------------------------------------- 1 | error: cyclic dependency in graph [ 2 | "\"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle2.html/" --> \"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle1.html/"", 3 | "\"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle1.html/" --> \"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle1.html/"", 4 | ] 5 | --> tests/ui/cycle.rs:3:10 6 | | 7 | 3 | #[derive(Template)] 8 | | ^^^^^^^^ 9 | | 10 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 11 | -------------------------------------------------------------------------------- /askama_escape/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama_escape" 3 | version = "0.11.0" 4 | description = "Optimized HTML escaping code, extracted from Askama" 5 | documentation = "https://docs.rs/askama_escape" 6 | keywords = ["html", "escaping"] 7 | homepage = "https://github.com/djc/askama" 8 | repository = "https://github.com/djc/askama" 9 | license = "MIT OR Apache-2.0" 10 | workspace = ".." 11 | readme = "README.md" 12 | edition = "2021" 13 | rust-version = "1.65" 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [dev-dependencies] 19 | criterion = "0.5" 20 | 21 | [[bench]] 22 | name = "all" 23 | harness = false 24 | -------------------------------------------------------------------------------- /askama_actix/README.md: -------------------------------------------------------------------------------- 1 | # askama_actix: Askama integration with Actix-web 2 | 3 | [![Documentation](https://docs.rs/askama_actix/badge.svg)](https://docs.rs/askama_actix/) 4 | [![Latest version](https://img.shields.io/crates/v/askama_actix.svg)](https://crates.io/crates/askama_actix) 5 | [![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) 6 | [![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/ZucwjE6bmT) 7 | 8 | Integration of the [Askama](https://github.com/djc/askama) templating engine in 9 | code building on the Actix-web framework. 10 | -------------------------------------------------------------------------------- /testing/templates/operators.html: -------------------------------------------------------------------------------- 1 | {% if a * c > b -%} 2 | mul 3 | {%- endif -%} 4 | {% if c / c == a -%} 5 | div 6 | {%- endif -%} 7 | {% if a % c == b -%} 8 | mod 9 | {%- endif -%} 10 | {% if a + b == c -%} 11 | add 12 | {%- endif -%} 13 | {% if a << b == c -%} 14 | rsh 15 | {%- endif -%} 16 | {% if c >> b == a -%} 17 | lsh 18 | {%- endif -%} 19 | {% if a & b == b -%} 20 | band 21 | {%- endif -%} 22 | {% if b ^ c == a + c -%} 23 | bxor 24 | {%- endif -%} 25 | {% if (b | c) == a + c -%} 26 | bor 27 | {%- endif -%} 28 | {% if a == b && a + b == c -%} 29 | and 30 | {%- endif -%} 31 | {% if a == c || a == 1 -%} 32 | or 33 | {%- endif -%} 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Askama 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/askama?logo=rust&style=flat-square&logoColor=white "Crates.io")](https://crates.io/crates/askama) 4 | [![Book](https://img.shields.io/readthedocs/askama?label=book&logo=readthedocs&style=flat-square&logoColor=white "Book")](https://askama.readthedocs.io/) 5 | [![docs.rs](https://img.shields.io/docsrs/askama?logo=docsdotrs&style=flat-square&logoColor=white "docs.rs")](https://docs.rs/askama/) 6 | 7 | This is the old repository of the [askama](https://crates.io/crates/askama) project. 8 | 9 | Please go to **** to see the current state of the project. 10 | -------------------------------------------------------------------------------- /askama_rocket/README.md: -------------------------------------------------------------------------------- 1 | # askama_rocket: Askama integration with Rocket 2 | 3 | [![Documentation](https://docs.rs/askama_rocket/badge.svg)](https://docs.rs/askama_rocket/) 4 | [![Latest version](https://img.shields.io/crates/v/askama_rocket.svg)](https://crates.io/crates/askama_rocket) 5 | [![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) 6 | [![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/ZucwjE6bmT) 7 | 8 | Integration of the [Askama](https://github.com/djc/askama) templating engine in 9 | code building on the Rocket web framework. 10 | -------------------------------------------------------------------------------- /askama_derive/README.md: -------------------------------------------------------------------------------- 1 | # askama_derive: procedural macros for the Askama templating engine 2 | 3 | [![Documentation](https://docs.rs/askama_derive/badge.svg)](https://docs.rs/askama_derive/) 4 | [![Latest version](https://img.shields.io/crates/v/askama_derive.svg)](https://crates.io/crates/askama_derive) 5 | [![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) 6 | [![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/ZucwjE6bmT) 7 | 8 | This crate contains the procedural macros used by the 9 | [Askama](https://github.com/djc/askama) templating engine. 10 | -------------------------------------------------------------------------------- /askama_parser/README.md: -------------------------------------------------------------------------------- 1 | # askama_parser: template parser for the Askama templating engine 2 | 3 | [![Documentation](https://docs.rs/askama_parser/badge.svg)](https://docs.rs/askama_parser/) 4 | [![Latest version](https://img.shields.io/crates/v/askama_parser.svg)](https://crates.io/crates/askama_parser) 5 | [![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) 6 | [![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/ZucwjE6bmT) 7 | 8 | This crate contains the procedural macros used by the 9 | [Askama](https://github.com/djc/askama) templating engine. 10 | -------------------------------------------------------------------------------- /testing/tests/hello.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] // this will generate the code... 4 | #[template(path = "hello.html")] // using the template in this path, relative 5 | // to the templates dir in the crate root 6 | struct HelloTemplate<'a> { 7 | // the name of the struct can be anything 8 | name: &'a str, // the field name should match the variable name 9 | // in your template 10 | } 11 | 12 | #[test] 13 | fn main() { 14 | let hello = HelloTemplate { name: "world" }; // instantiate your struct 15 | assert_eq!("Hello, world!", hello.render().unwrap()); // then render it. 16 | } 17 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama-fuzz" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | 7 | [package.metadata] 8 | cargo-fuzz = true 9 | 10 | [dependencies] 11 | libfuzzer-sys = "0.4" 12 | arbitrary = "1.3.2" 13 | askama = { path = "../askama" } 14 | askama_parser = { path = "../askama_parser" } 15 | askama_escape = { path = "../askama_escape" } 16 | 17 | [profile.release] 18 | debug = 1 19 | 20 | [[bin]] 21 | name = "askama_parser-fuzz_parser" 22 | path = "fuzz_targets/fuzz_parser.rs" 23 | test = false 24 | doc = false 25 | 26 | [[bin]] 27 | name = "fuzz_filters" 28 | path = "fuzz_targets/fuzz_filters.rs" 29 | test = false 30 | doc = false 31 | -------------------------------------------------------------------------------- /askama_escape/README.md: -------------------------------------------------------------------------------- 1 | # askama_escape: escaping utilities for the Askama templating engine 2 | 3 | [![Documentation](https://docs.rs/askama_escape/badge.svg)](https://docs.rs/askama_escape/) 4 | [![Latest version](https://img.shields.io/crates/v/askama_escape.svg)](https://crates.io/crates/askama_escape) 5 | [![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) 6 | [![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/ZucwjE6bmT) 7 | 8 | This crate contains helper code for HTML escaping used by the 9 | [Askama](https://github.com/djc/askama) templating engine. 10 | -------------------------------------------------------------------------------- /testing/tests/ui/macro.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{%- macro thrice(param) -%} 5 | {{ param }} 6 | {%- endmacro -%} 7 | 8 | {%- call thrice(2, 3) -%}", ext = "html")] 9 | struct InvalidNumberOfArgs; 10 | 11 | #[derive(Template)] 12 | #[template(source = "{%- macro thrice(param, param2) -%} 13 | {{ param }} {{ param2 }} 14 | {%- endmacro -%} 15 | 16 | {%- call thrice() -%}", ext = "html")] 17 | struct InvalidNumberOfArgs2; 18 | 19 | #[derive(Template)] 20 | #[template(source = "{%- macro thrice() -%} 21 | {%- endmacro -%} 22 | 23 | {%- call thrice(1, 2) -%}", ext = "html")] 24 | struct InvalidNumberOfArgs3; 25 | 26 | fn main() { 27 | } 28 | -------------------------------------------------------------------------------- /askama_parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama_parser" 3 | version = "0.3.1" 4 | description = "Parser for Askama templates" 5 | documentation = "https://docs.rs/askama" 6 | keywords = ["markup", "template", "jinja2", "html"] 7 | categories = ["template-engine"] 8 | homepage = "https://github.com/djc/askama" 9 | repository = "https://github.com/djc/askama" 10 | license = "MIT OR Apache-2.0" 11 | workspace = ".." 12 | readme = "README.md" 13 | edition = "2021" 14 | rust-version = "1.65" 15 | 16 | [dependencies] 17 | nom = { version = "7", default-features = false, features = ["alloc"] } 18 | 19 | [dev-dependencies] 20 | criterion = "0.5" 21 | 22 | [[bench]] 23 | name = "from_str" 24 | harness = false 25 | -------------------------------------------------------------------------------- /testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama_testing" 3 | version = "0.1.0" 4 | authors = ["Dirkjan Ochtman "] 5 | workspace = ".." 6 | edition = "2021" 7 | rust-version = "1.65" 8 | publish = false 9 | 10 | [features] 11 | default = ["serde-json"] 12 | serde-json = ["serde_json", "askama/serde-json"] 13 | 14 | [dependencies] 15 | askama = { path = "../askama", version = "0.13" } 16 | phf = { version = "0.11", features = ["macros" ]} 17 | serde_json = { version = "1.0", optional = true } 18 | 19 | [dev-dependencies] 20 | criterion = "0.5" 21 | trybuild = "1.0.76" 22 | 23 | [[bench]] 24 | name = "all" 25 | harness = false 26 | 27 | [[bench]] 28 | name = "normalize_identifier" 29 | harness = false 30 | -------------------------------------------------------------------------------- /testing/templates/num-literals.html: -------------------------------------------------------------------------------- 1 | {% let plain_int = 9__0__ -%} 2 | {% let neg_int = -9__0__isize -%} 3 | {% let suffix_int = 9__0__i32 -%} 4 | {% let bin_int = 0b__1__0__ -%} 5 | {% let oct_int = 0o__7__0__ -%} 6 | {% let hex_int = 0x__f__0__ -%} 7 | {% let plain_float = 1__0__.5__0__ -%} 8 | {% let suffix_float = 1__0__.5__0__f32 -%} 9 | {% let exp_float = 1__0__e+__1__0__f32 -%} 10 | {% let dotexp_float = 1__0__.5__0__e+__1__0__f32 -%} 11 | [{{ plain_int }}, {{ neg_int }}, {{ suffix_int }}, {{ bin_int }}, {{ oct_int }}, {{ hex_int }}, {{ plain_float }}, {{ suffix_float }}, {{ exp_float }}, {{ dotexp_float }}] 12 | {% for value in 1..2 -%} 13 | {{ value }} 14 | {%- endfor %} 15 | {% for value in 1..=2 -%} 16 | {{ value }} 17 | {%- endfor %} 18 | -------------------------------------------------------------------------------- /testing/templates/compare.html: -------------------------------------------------------------------------------- 1 | {% if a == b -%} 2 | t 3 | {%- endif -%} 4 | {% if a == c -%} 5 | t 6 | {%- else -%} 7 | f 8 | {%- endif %} 9 | {% if a != c -%} 10 | t 11 | {%- endif %} 12 | {%- if a != b -%} 13 | t 14 | {%- else -%} 15 | f 16 | {%- endif %} 17 | {% if c >= b -%} 18 | t 19 | {%- endif -%} 20 | {% if b >= c -%} 21 | t 22 | {%- else -%} 23 | f 24 | {%- endif %} 25 | {% if c > b -%} 26 | t 27 | {%- endif -%} 28 | {% if a > c -%} 29 | t 30 | {%- else -%} 31 | f 32 | {%- endif %} 33 | {% if a <= b -%} 34 | t 35 | {%- endif -%} 36 | {% if c <= b -%} 37 | t 38 | {%- else -%} 39 | f 40 | {%- endif %} 41 | {% if a < c -%} 42 | t 43 | {%- endif %} 44 | {%- if a < b -%} 45 | t 46 | {%- else -%} 47 | f 48 | {%- endif %} 49 | -------------------------------------------------------------------------------- /testing/tests/ui/extends.stderr: -------------------------------------------------------------------------------- 1 | error: whitespace control is not allowed on `extends` 2 | failed to parse template source at row 1, column 2 near: 3 | "- extends \"whatever.html\" %}" 4 | --> tests/ui/extends.rs:3:10 5 | | 6 | 3 | #[derive(Template)] 7 | | ^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | 11 | error: whitespace control is not allowed on `extends` 12 | failed to parse template source at row 1, column 2 near: 13 | " extends \"whatever.html\" -%}" 14 | --> tests/ui/extends.rs:10:10 15 | | 16 | 10 | #[derive(Template)] 17 | | ^^^^^^^^ 18 | | 19 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 20 | -------------------------------------------------------------------------------- /askama_parser/benches/librustdoc/source.html: -------------------------------------------------------------------------------- 1 |
{# #} 2 | {# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr 3 | Do not show "1 2 3 4 5 ..." in web search results. #} 4 |
 5 |         {% for line in lines.clone() %}
 6 |             {% if embedded %}
 7 |                 {{line|safe}}
 8 |             {%~ else %}
 9 |                 {{line|safe}}
10 |             {%~ endif %}
11 |         {% endfor %}
12 |     
{# #} 13 |
 {# #}
14 |         
15 |             {% if needs_expansion %}
16 |                 
17 |             {% endif %}
18 |             {{code_html|safe}}
19 |          {# #}
20 |     
{# #} 21 |
22 | -------------------------------------------------------------------------------- /askama_rocket/tests/basic.rs: -------------------------------------------------------------------------------- 1 | use askama_rocket::Template; 2 | use rocket::http::{ContentType, Status}; 3 | use rocket::local::asynchronous::Client; 4 | 5 | #[derive(Template)] 6 | #[template(path = "hello.html")] 7 | struct HelloTemplate<'a> { 8 | name: &'a str, 9 | } 10 | 11 | #[rocket::get("/")] 12 | fn hello() -> HelloTemplate<'static> { 13 | HelloTemplate { name: "world" } 14 | } 15 | 16 | #[tokio::test] 17 | async fn test_rocket() { 18 | let rocket = rocket::build() 19 | .mount("/", rocket::routes![hello]) 20 | .ignite() 21 | .await 22 | .unwrap(); 23 | let client = Client::untracked(rocket).await.unwrap(); 24 | let rsp = client.get("/").dispatch().await; 25 | assert_eq!(rsp.status(), Status::Ok); 26 | assert_eq!(rsp.content_type(), Some(ContentType::HTML)); 27 | assert_eq!(rsp.into_string().await.as_deref(), Some("Hello, world!")); 28 | } 29 | -------------------------------------------------------------------------------- /testing/tests/ui/macro.stderr: -------------------------------------------------------------------------------- 1 | error: macro "thrice" expected 1 argument, found 2 2 | --> tests/ui/macro.rs:3:10 3 | | 4 | 3 | #[derive(Template)] 5 | | ^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error: macro "thrice" expected 2 arguments, found 0 10 | --> tests/ui/macro.rs:11:10 11 | | 12 | 11 | #[derive(Template)] 13 | | ^^^^^^^^ 14 | | 15 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | 17 | error: macro "thrice" expected 0 arguments, found 2 18 | --> tests/ui/macro.rs:19:10 19 | | 20 | 19 | #[derive(Template)] 21 | | ^^^^^^^^ 22 | | 23 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 24 | -------------------------------------------------------------------------------- /askama_actix/tests/basic.rs: -------------------------------------------------------------------------------- 1 | use actix_web::http::header::CONTENT_TYPE; 2 | use actix_web::web; 3 | use askama_actix::Template; 4 | use bytes::Bytes; 5 | 6 | #[derive(Template)] 7 | #[template(path = "hello.html")] 8 | struct HelloTemplate<'a> { 9 | name: &'a str, 10 | } 11 | 12 | #[actix_rt::test] 13 | async fn test_actix_web() { 14 | let srv = actix_test::start(|| { 15 | actix_web::App::new() 16 | .service(web::resource("/").to(|| async { HelloTemplate { name: "world" } })) 17 | }); 18 | 19 | let request = srv.get("/"); 20 | let mut response = request.send().await.unwrap(); 21 | assert!(response.status().is_success()); 22 | assert_eq!( 23 | response.headers().get(CONTENT_TYPE).unwrap(), 24 | "text/html; charset=utf-8" 25 | ); 26 | 27 | let bytes = response.body().await.unwrap(); 28 | assert_eq!(bytes, Bytes::from_static("Hello, world!".as_ref())); 29 | } 30 | -------------------------------------------------------------------------------- /testing/tests/ui/char_literal.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(path = "char-literals/char-literal-1.txt")] 5 | struct Err1; 6 | 7 | #[derive(Template)] 8 | #[template(path = "char-literals/char-literal-2.txt")] 9 | struct Err2; 10 | 11 | #[derive(Template)] 12 | #[template(path = "char-literals/char-literal-3.txt")] 13 | struct Err3; 14 | 15 | #[derive(Template)] 16 | #[template(path = "char-literals/char-literal-4.txt")] 17 | struct Err4; 18 | 19 | #[derive(Template)] 20 | #[template(path = "char-literals/char-literal-5.txt")] 21 | struct Err5; 22 | 23 | #[derive(Template)] 24 | #[template(path = "char-literals/char-literal-6.txt")] 25 | struct Err6; 26 | 27 | #[derive(Template)] 28 | #[template(path = "char-literals/char-literal-7.txt")] 29 | struct Err7; 30 | 31 | #[derive(Template)] 32 | #[template(source = "{% let s = 'aaa' %}", ext = "html")] 33 | struct Err8; 34 | 35 | fn main() { 36 | } 37 | -------------------------------------------------------------------------------- /askama_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama_derive" 3 | version = "0.13.0" 4 | description = "Procedural macro package for Askama" 5 | homepage = "https://github.com/djc/askama" 6 | repository = "https://github.com/djc/askama" 7 | license = "MIT/Apache-2.0" 8 | workspace = ".." 9 | readme = "README.md" 10 | edition = "2021" 11 | rust-version = "1.65" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [features] 17 | config = ["serde", "basic-toml"] 18 | humansize = [] 19 | urlencode = [] 20 | serde-json = [] 21 | num-traits = [] 22 | with-actix-web = [] 23 | with-axum = [] 24 | with-rocket = [] 25 | with-warp = [] 26 | 27 | [dependencies] 28 | parser = { package = "askama_parser", version = "0.3.1", path = "../askama_parser" } 29 | mime = "0.3" 30 | mime_guess = "2" 31 | proc-macro2 = "1" 32 | quote = "1" 33 | serde = { version = "1.0", optional = true, features = ["derive"] } 34 | syn = "2" 35 | basic-toml = { version = "0.1.1", optional = true } 36 | -------------------------------------------------------------------------------- /askama_warp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama_warp" 3 | version = "0.14.0" 4 | description = "Warp integration for Askama templates" 5 | documentation = "https://docs.rs/askama" 6 | keywords = ["markup", "template", "jinja2", "html"] 7 | categories = ["template-engine"] 8 | homepage = "https://github.com/djc/askama" 9 | repository = "https://github.com/djc/askama" 10 | license = "MIT OR Apache-2.0" 11 | workspace = ".." 12 | readme = "README.md" 13 | edition = "2021" 14 | rust-version = "1.65" 15 | 16 | [dependencies] 17 | askama = { version = "0.13", path = "../askama", default-features = false, features = ["with-warp"] } 18 | warp = { version = "0.3", default-features = false } 19 | 20 | [dev-dependencies] 21 | tokio = { version = "1.0", features = ["macros", "rt"] } 22 | 23 | [features] 24 | default = ["askama/default"] 25 | config = ["askama/config"] 26 | humansize = ["askama/humansize"] 27 | num-traits = ["askama/num-traits"] 28 | serde-json = ["askama/serde-json"] 29 | urlencode = ["askama/urlencode"] 30 | -------------------------------------------------------------------------------- /askama_rocket/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama_rocket" 3 | version = "0.13.0" 4 | description = "Rocket integration for Askama templates" 5 | documentation = "https://docs.rs/askama" 6 | keywords = ["markup", "template", "jinja2", "html"] 7 | categories = ["template-engine"] 8 | homepage = "https://github.com/djc/askama" 9 | repository = "https://github.com/djc/askama" 10 | license = "MIT OR Apache-2.0" 11 | workspace = ".." 12 | readme = "README.md" 13 | edition = "2021" 14 | rust-version = "1.65" 15 | 16 | [dependencies] 17 | askama = { version = "0.13", path = "../askama", default-features = false, features = ["with-rocket"] } 18 | rocket = { version = "0.5", default-features = false } 19 | 20 | [dev-dependencies] 21 | tokio = { version = "1.0", features = ["macros", "rt"] } 22 | 23 | [features] 24 | default = ["askama/default"] 25 | config = ["askama/config"] 26 | humansize = ["askama/humansize"] 27 | num-traits = ["askama/num-traits"] 28 | serde-json = ["askama/serde-json"] 29 | urlencode = ["askama/urlencode"] 30 | -------------------------------------------------------------------------------- /askama_axum/tests/basic.rs: -------------------------------------------------------------------------------- 1 | use askama_axum::Template; 2 | use axum::{ 3 | body::Body, 4 | http::{Request, StatusCode}, 5 | routing::get, 6 | Router, 7 | }; 8 | use http_body_util::BodyExt; 9 | use tower::util::ServiceExt; 10 | 11 | #[derive(Template)] 12 | #[template(path = "hello.html")] 13 | struct HelloTemplate<'a> { 14 | name: &'a str, 15 | } 16 | 17 | async fn hello() -> HelloTemplate<'static> { 18 | HelloTemplate { name: "world" } 19 | } 20 | 21 | #[tokio::test] 22 | async fn template_to_response() { 23 | let app = Router::new().route("/", get(hello)); 24 | 25 | let res = app 26 | .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) 27 | .await 28 | .unwrap(); 29 | assert_eq!(res.status(), StatusCode::OK); 30 | 31 | let headers = res.headers(); 32 | assert_eq!(headers["Content-Type"], "text/html; charset=utf-8"); 33 | 34 | let body = res.into_body().collect().await.unwrap().to_bytes(); 35 | assert_eq!(&body[..], b"Hello, world!"); 36 | } 37 | -------------------------------------------------------------------------------- /testing/templates/if-coerce.html: -------------------------------------------------------------------------------- 1 | {% macro foo(b) -%} 2 | {% if b %}t{% else %}f{% endif -%} 3 | {% endmacro -%} 4 | 5 | {% macro bar(b) -%} 6 | {%- call foo(b) -%} 7 | {% endmacro -%} 8 | 9 | {% macro baz(b) -%} 10 | {%- call bar(b) -%} 11 | {% endmacro -%} 12 | 13 | {% macro qux(b) -%} 14 | {%- call baz(b) -%} 15 | {% endmacro -%} 16 | 17 | {%- call foo(false) -%} 18 | {%- call bar(true) -%} 19 | {%- call baz(false) -%} 20 | {%- call qux(true) -%} 21 | 22 | {%- call qux(true && false) -%} 23 | {%- call qux(false || true) -%} 24 | 25 | {%- call qux(self.t) -%} 26 | {%- call qux(self.f) -%} 27 | {%- call qux(self.f || self.t) -%} 28 | 29 | {%- if false -%} 30 | if 31 | {%- else if false || true -%} 32 | elseif 33 | {%- else -%} 34 | else 35 | {%- endif -%} 36 | 37 | {%- if true && false -%} 38 | if 39 | {%- else if false -%} 40 | elseif 41 | {%- else -%} 42 | else 43 | {%- endif -%} 44 | 45 | {%- if false || true -%} 46 | if 47 | {%- else if (true && false) -%} 48 | elseif 49 | {%- else -%} 50 | else 51 | {%- endif -%} 52 | -------------------------------------------------------------------------------- /askama_axum/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | #![deny(elided_lifetimes_in_paths)] 3 | #![deny(unreachable_pub)] 4 | 5 | #[doc(no_inline)] 6 | pub use askama::*; 7 | #[doc(no_inline)] 8 | pub use axum_core; 9 | use axum_core::response::{IntoResponse, Response}; 10 | 11 | /// Render a [`Template`] into a [`Response`], or render an error page. 12 | pub fn into_response(tmpl: &T) -> Response { 13 | try_into_response(tmpl) 14 | .map_err(|err| axum_core::response::ErrorResponse::from(err.to_string())) 15 | .into_response() 16 | } 17 | 18 | /// Try to render a [`Template`] into a [`Response`]. 19 | pub fn try_into_response(tmpl: &T) -> Result { 20 | let value = tmpl.render()?.into(); 21 | Response::builder() 22 | .header( 23 | http::header::CONTENT_TYPE, 24 | http::header::HeaderValue::from_static(T::MIME_TYPE), 25 | ) 26 | .body(value) 27 | .map_err(|err| Error::Custom(err.into())) 28 | } 29 | -------------------------------------------------------------------------------- /askama_actix/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama_actix" 3 | version = "0.15.0" 4 | description = "Actix-Web integration for Askama templates" 5 | documentation = "https://docs.rs/askama" 6 | keywords = ["markup", "template", "jinja2", "html"] 7 | categories = ["template-engine"] 8 | homepage = "https://github.com/djc/askama" 9 | repository = "https://github.com/djc/askama" 10 | license = "MIT OR Apache-2.0" 11 | workspace = ".." 12 | readme = "README.md" 13 | edition = "2021" 14 | rust-version = "1.65" 15 | 16 | [dependencies] 17 | actix-web = { version = "4", default-features = false } 18 | askama = { version = "0.13", path = "../askama", default-features = false, features = ["with-actix-web"] } 19 | 20 | [dev-dependencies] 21 | actix-rt = { version = "2", default-features = false } 22 | actix-test = "0.1" 23 | bytes = { version = "1" } 24 | 25 | [features] 26 | default = ["askama/default"] 27 | config = ["askama/config"] 28 | humansize = ["askama/humansize"] 29 | num-traits = ["askama/num-traits"] 30 | serde-json = ["askama/serde-json"] 31 | urlencode = ["askama/urlencode"] 32 | -------------------------------------------------------------------------------- /askama_parser/benches/librustdoc/short_item_info.html: -------------------------------------------------------------------------------- 1 | {% match self %} 2 | {% when Self::Deprecation with { message } %} 3 |
{# #} 4 | 👎 {# #} 5 | {{message|safe}} {# #} 6 |
{# #} 7 | {% when Self::Unstable with { feature, tracking } %} 8 |
{# #} 9 | 🔬 {# #} 10 | {# #} 11 | This is a nightly-only experimental API. ({# #} 12 | {{feature}} {# #} 13 | {% match tracking %} 14 | {% when Some with ((url, num)) %} 15 |  #{{num}} {# #} 16 | {% when None %} 17 | {% endmatch %} 18 | ) {# #} 19 | {# #} 20 |
{# #} 21 | {% when Self::Portability with { message } %} 22 |
{{message|safe}}
{# #} 23 | {% endmatch %} 24 | -------------------------------------------------------------------------------- /askama_axum/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama_axum" 3 | version = "0.5.0" 4 | edition = "2021" 5 | rust-version = "1.65" 6 | description = "Axum integration for Askama templates" 7 | keywords = ["markup", "template", "jinja2", "html", "axum"] 8 | categories = ["template-engine"] 9 | homepage = "https://github.com/djc/askama" 10 | repository = "https://github.com/djc/askama" 11 | documentation = "https://docs.rs/askama" 12 | license = "MIT OR Apache-2.0" 13 | workspace = ".." 14 | readme = "README.md" 15 | 16 | [dependencies] 17 | askama = { version = "0.13", path = "../askama", default-features = false, features = ["with-axum"] } 18 | axum-core = "0.4" 19 | http = "1.0" 20 | 21 | [dev-dependencies] 22 | axum = { version = "0.7", default-features = false } 23 | http-body-util = "0.1" 24 | tokio = { version = "1.0", features = ["macros", "rt"] } 25 | tower = "0.5" 26 | 27 | [features] 28 | default = ["askama/default"] 29 | config = ["askama/config"] 30 | humansize = ["askama/humansize"] 31 | num-traits = ["askama/num-traits"] 32 | serde-json = ["askama/serde-json"] 33 | urlencode = ["askama/urlencode"] 34 | -------------------------------------------------------------------------------- /testing/tests/ui/error_file_path.stderr: -------------------------------------------------------------------------------- 1 | error: failed to parse template source 2 | --> testing/templates/invalid_syntax.html:1:14 3 | "}" 4 | --> tests/ui/error_file_path.rs:3:10 5 | | 6 | 3 | #[derive(Template)] 7 | | ^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | 11 | error: failed to parse template source 12 | --> testing/templates/invalid_syntax.html:1:14 13 | "}" 14 | --> tests/ui/error_file_path.rs:7:10 15 | | 16 | 7 | #[derive(Template)] 17 | | ^^^^^^^^ 18 | | 19 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 20 | 21 | error: failed to parse template source 22 | --> testing/templates/invalid_syntax.html:1:14 23 | "}" 24 | --> tests/ui/error_file_path.rs:11:10 25 | | 26 | 11 | #[derive(Template)] 27 | | ^^^^^^^^ 28 | | 29 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 30 | -------------------------------------------------------------------------------- /askama/src/helpers.rs: -------------------------------------------------------------------------------- 1 | use std::iter::{Enumerate, Peekable}; 2 | 3 | pub struct TemplateLoop 4 | where 5 | I: Iterator, 6 | { 7 | iter: Peekable>, 8 | } 9 | 10 | impl TemplateLoop 11 | where 12 | I: Iterator, 13 | { 14 | #[inline] 15 | pub fn new(iter: I) -> Self { 16 | TemplateLoop { 17 | iter: iter.enumerate().peekable(), 18 | } 19 | } 20 | } 21 | 22 | impl Iterator for TemplateLoop 23 | where 24 | I: Iterator, 25 | { 26 | type Item = (::Item, LoopItem); 27 | 28 | #[inline] 29 | fn next(&mut self) -> Option<(::Item, LoopItem)> { 30 | self.iter.next().map(|(index, item)| { 31 | ( 32 | item, 33 | LoopItem { 34 | index, 35 | first: index == 0, 36 | last: self.iter.peek().is_none(), 37 | }, 38 | ) 39 | }) 40 | } 41 | } 42 | 43 | #[derive(Copy, Clone)] 44 | pub struct LoopItem { 45 | pub index: usize, 46 | pub first: bool, 47 | pub last: bool, 48 | } 49 | -------------------------------------------------------------------------------- /askama_parser/benches/librustdoc/print_item.html: -------------------------------------------------------------------------------- 1 |
{# #} 2 |

3 | {{typ}} 4 | {# The breadcrumbs of the item path, like std::string #} 5 | {% for component in path_components %} 6 | {{component.name}}:: 7 | {% endfor %} 8 | {{name}} {# #} 9 | {# #} 12 |

{# #} 13 | 14 | {% if !stability_since_raw.is_empty() %} 15 | {{ stability_since_raw|safe +}} · {#+ #} 16 | {% endif %} 17 | {% match src_href %} 18 | {% when Some with (href) %} 19 | source · {#+ #} 20 | {% else %} 21 | {% endmatch %} 22 | {# #} 25 | {# #} 26 |
{# #} 27 | -------------------------------------------------------------------------------- /testing/tests/render_in_place.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | #[derive(Template)] 3 | #[template(path = "render_in_place.html")] 4 | struct RenderInPlace<'a> { 5 | s1: SectionOne<'a>, 6 | s2: SectionTwo<'a>, 7 | s3: &'a Vec>, 8 | } 9 | 10 | #[derive(Template)] 11 | #[template(source = "A={{ a }}\nB={{ b }}", ext = "html")] 12 | struct SectionOne<'a> { 13 | a: &'a str, 14 | b: &'a str, 15 | } 16 | 17 | #[derive(Template)] 18 | #[template(source = "C={{ c }}\nD={{ d }}", ext = "html")] 19 | struct SectionTwo<'a> { 20 | c: &'a str, 21 | d: &'a str, 22 | } 23 | 24 | #[test] 25 | fn test_render_in_place() { 26 | let t = RenderInPlace { 27 | s1: SectionOne { a: "A", b: "B" }, 28 | s2: SectionTwo { c: "C", d: "D" }, 29 | s3: &vec![ 30 | SectionOne { a: "1", b: "2" }, 31 | SectionOne { a: "A", b: "B" }, 32 | SectionOne { a: "a", b: "b" }, 33 | ], 34 | }; 35 | assert_eq!( 36 | t.render().unwrap(), 37 | "Section 1: A=A\nB=B\nSection 2: C=C\nD=D\nSection 3 for:\n* A=1\nB=2\n* A=A\nB=B\n* A=a\nB=b\n" 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2020 Dirkjan Ochtman 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /askama_parser/benches/librustdoc/item_union.html: -------------------------------------------------------------------------------- 1 |

 2 |     {{ self.render_attributes_in_pre()|safe }}
 3 |     {{ self.render_union()|safe }}
 4 | 
5 | {{ self.document()|safe }} 6 | {% if self.fields_iter().peek().is_some() %} 7 |

{# #} 8 | Fields§ {# #} 9 |

10 | {% for (field, ty) in self.fields_iter() %} 11 | {% let name = field.name.expect("union field name") %} 12 | {# #} 14 | § {# #} 15 | {{ name }}: {{+ self.print_ty(ty)|safe }} {# #} 16 | 17 | {% if let Some(stability_class) = self.stability_field(field) %} 18 | 19 | {% endif %} 20 | {{ self.document_field(field)|safe }} 21 | {% endfor %} 22 | {% endif %} 23 | {{ self.render_assoc_items()|safe }} 24 | {{ self.document_type_layout()|safe }} 25 | -------------------------------------------------------------------------------- /testing/tests/size_hint.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | macro_rules! test_size { 4 | ($source:literal, $expected:expr) => {{ 5 | #[derive(Template)] 6 | #[allow(dead_code)] 7 | #[template(source = $source, ext = "txt")] 8 | struct T(bool); 9 | 10 | assert_eq!(T::SIZE_HINT, $expected); 11 | }}; 12 | } 13 | 14 | #[test] 15 | fn test_cond_size_hint() { 16 | test_size!("{% if self.0 %}12345{% else %}12345{% endif %}", 10); 17 | } 18 | 19 | #[test] 20 | fn test_match_size_hint() { 21 | test_size!( 22 | "{% match self.0 %}{% when true %}12345{% else %}12345{% endmatch %}", 23 | 5 24 | ); 25 | } 26 | 27 | #[test] 28 | fn test_loop_size_hint() { 29 | test_size!("{% for i in 0..1 %}12345{% endfor %}", 7); 30 | } 31 | 32 | #[test] 33 | fn test_block_size_hint() { 34 | #[derive(Template)] 35 | #[template(path = "size-child.txt")] 36 | struct T; 37 | 38 | assert_eq!(T::SIZE_HINT, 3); 39 | } 40 | 41 | #[test] 42 | fn test_super_size_hint() { 43 | #[derive(Template)] 44 | #[template(path = "size-child-super.txt")] 45 | struct T; 46 | 47 | assert_eq!(T::SIZE_HINT, 5); 48 | } 49 | -------------------------------------------------------------------------------- /testing/tests/ui.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(windows))] 2 | 3 | use std::os::unix::fs::symlink; 4 | use std::path::Path; 5 | use trybuild::TestCases; 6 | 7 | #[test] 8 | fn ui() { 9 | let t = TestCases::new(); 10 | t.compile_fail("tests/ui/*.rs"); 11 | 12 | // To be able to use existing templates, we create a link to the `templates` folder. 13 | let manifest_dir = match std::env::var("CARGO_MANIFEST_DIR") { 14 | Ok(manifest_dir) => manifest_dir, 15 | Err(_) => panic!("you need to run tests with `cargo`"), 16 | }; 17 | let target = Path::new(&manifest_dir).join("../target/tests/trybuild/askama_testing"); 18 | if !target.exists() { 19 | if let Err(err) = std::fs::create_dir_all(&target) { 20 | panic!("failed to create folder `{}`: {err:?}", target.display()); 21 | } 22 | } 23 | let target = target.canonicalize().unwrap().join("templates"); 24 | if target.exists() { 25 | return; 26 | } 27 | let original = Path::new(&manifest_dir).join("templates"); 28 | if symlink(&original, &target).is_err() { 29 | panic!( 30 | "failed to create to create link on `{}` as `{}`", 31 | original.display(), 32 | target.display() 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /testing/tests/ui/unclosed-nodes.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{{ expr", ext = "txt")] 5 | struct Expr1; 6 | 7 | #[derive(Template)] 8 | #[template(source = "{{ expr ", ext = "txt")] 9 | struct Expr2; 10 | 11 | #[derive(Template)] 12 | #[template(source = "{{ expr -", ext = "txt")] 13 | struct Expr3; 14 | 15 | #[derive(Template)] 16 | #[template(source = "{{ expr -}", ext = "txt")] 17 | struct Expr4; 18 | 19 | #[derive(Template)] 20 | #[template(source = "{% let x", ext = "txt")] 21 | struct Node1; 22 | 23 | #[derive(Template)] 24 | #[template(source = "{% let x ", ext = "txt")] 25 | struct Node2; 26 | 27 | #[derive(Template)] 28 | #[template(source = "{% let x -", ext = "txt")] 29 | struct Node3; 30 | 31 | #[derive(Template)] 32 | #[template(source = "{% let x -%", ext = "txt")] 33 | struct Node4; 34 | 35 | #[derive(Template)] 36 | #[template(source = "{# comment", ext = "txt")] 37 | struct Comment1; 38 | 39 | #[derive(Template)] 40 | #[template(source = "{# comment ", ext = "txt")] 41 | struct Comment2; 42 | 43 | #[derive(Template)] 44 | #[template(source = "{# comment -", ext = "txt")] 45 | struct Comment3; 46 | 47 | #[derive(Template)] 48 | #[template(source = "{# comment -#", ext = "txt")] 49 | struct Comment4; 50 | 51 | fn main() {} 52 | -------------------------------------------------------------------------------- /askama_warp/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | #![deny(elided_lifetimes_in_paths)] 3 | #![deny(unreachable_pub)] 4 | 5 | #[doc(no_inline)] 6 | pub use askama::*; 7 | #[doc(no_inline)] 8 | pub use warp; 9 | use warp::reply::Response; 10 | 11 | /// Render a [`Template`] into a [`Response`], or render an error page. 12 | pub fn into_response(tmpl: &T) -> Response { 13 | match try_into_response(tmpl) { 14 | Ok(response) => response, 15 | Err(err) => warp::http::Response::builder() 16 | .status(warp::http::StatusCode::INTERNAL_SERVER_ERROR) 17 | .header( 18 | warp::http::header::CONTENT_TYPE, 19 | warp::http::HeaderValue::from_static("text/plain; charset=utf-8"), 20 | ) 21 | .body(err.to_string().into()) 22 | .unwrap(), 23 | } 24 | } 25 | 26 | /// Try to render a [`Template`] into a [`Response`]. 27 | pub fn try_into_response(tmpl: &T) -> Result { 28 | let value = tmpl.render()?.into(); 29 | warp::http::Response::builder() 30 | .status(warp::http::StatusCode::OK) 31 | .header(warp::http::header::CONTENT_TYPE, T::MIME_TYPE) 32 | .body(value) 33 | .map_err(|err| Error::Custom(err.into())) 34 | } 35 | -------------------------------------------------------------------------------- /testing/tests/methods.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{{ self.get_s() }}", ext = "txt")] 5 | struct SelfMethodTemplate<'a> { 6 | s: &'a str, 7 | } 8 | 9 | impl SelfMethodTemplate<'_> { 10 | fn get_s(&self) -> &str { 11 | self.s 12 | } 13 | } 14 | 15 | #[test] 16 | fn test_self_method() { 17 | let t = SelfMethodTemplate { s: "foo" }; 18 | assert_eq!(t.render().unwrap(), "foo"); 19 | } 20 | 21 | #[derive(Template)] 22 | #[template(source = "{{ self.type() }}", ext = "txt")] 23 | struct SelfRawIdentifierMethodTemplate {} 24 | 25 | impl SelfRawIdentifierMethodTemplate { 26 | fn r#type(&self) -> &str { 27 | "foo" 28 | } 29 | } 30 | 31 | #[test] 32 | fn test_self_raw_identifier_method() { 33 | let t = SelfRawIdentifierMethodTemplate {}; 34 | assert_eq!(t.render().unwrap(), "foo"); 35 | } 36 | 37 | #[derive(Template)] 38 | #[template(source = "{{ self.get_s() }} {{ t.get_s() }}", ext = "txt")] 39 | struct NestedSelfMethodTemplate<'a> { 40 | t: SelfMethodTemplate<'a>, 41 | } 42 | 43 | impl NestedSelfMethodTemplate<'_> { 44 | fn get_s(&self) -> &str { 45 | "bar" 46 | } 47 | } 48 | 49 | #[test] 50 | fn test_nested() { 51 | let t = NestedSelfMethodTemplate { 52 | t: SelfMethodTemplate { s: "foo" }, 53 | }; 54 | assert_eq!(t.render().unwrap(), "bar foo"); 55 | } 56 | -------------------------------------------------------------------------------- /testing/tests/include.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(path = "include.html")] 5 | struct IncludeTemplate<'a> { 6 | strs: &'a [&'a str], 7 | } 8 | 9 | #[test] 10 | fn test_include() { 11 | let strs = vec!["foo", "bar"]; 12 | let s = IncludeTemplate { strs: &strs }; 13 | assert_eq!(s.render().unwrap(), "\n INCLUDED: foo\n INCLUDED: bar") 14 | } 15 | 16 | #[derive(Template)] 17 | #[template(path = "include-extends.html")] 18 | struct IncludeExtendsTemplate<'a> { 19 | name: &'a str, 20 | } 21 | 22 | #[test] 23 | fn test_include_extends() { 24 | let template = IncludeExtendsTemplate { name: "Alice" }; 25 | 26 | assert_eq!( 27 | template.render().unwrap(), 28 | "
\n \ 29 |

Welcome

\n \ 30 |
\n \ 31 |

Below me is the header

\n \ 32 | foo\n \ 33 |

Above me is the header

\n\ 34 |
\n\ 35 | Hello, Alice!\n\ 36 |
" 37 | ); 38 | } 39 | 40 | #[derive(Template)] 41 | #[template(path = "include-macro.html")] 42 | struct IncludeMacroTemplate<'a> { 43 | name: &'a str, 44 | name2: &'a str, 45 | } 46 | 47 | #[test] 48 | fn test_include_macro() { 49 | let template = IncludeMacroTemplate { 50 | name: "Alice", 51 | name2: "Bob", 52 | }; 53 | 54 | assert_eq!(template.render().unwrap(), "Hello, Alice!\nHowdy, Bob!"); 55 | } 56 | -------------------------------------------------------------------------------- /testing/tests/ui/macro_named_argument.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{%- macro thrice(param1, param2) -%} 5 | {{ param1 }} {{ param2 }} 6 | {%- endmacro -%} 7 | 8 | {%- call thrice(param1=2, param3=3) -%}", ext = "html")] 9 | struct InvalidNamedArg; 10 | 11 | #[derive(Template)] 12 | #[template(source = "{%- macro thrice(param1, param2) -%} 13 | {{ param1 }} {{ param2 }} 14 | {%- endmacro -%} 15 | 16 | {%- call thrice(param1=2, param1=3) -%}", ext = "html")] 17 | struct InvalidNamedArg2; 18 | 19 | // Ensures that filters can't have named arguments. 20 | #[derive(Template)] 21 | #[template(source = "{%- macro thrice(param1, param2) -%} 22 | {{ param1 }} {{ param2 }} 23 | {%- endmacro -%} 24 | 25 | {%- call thrice(3, param1=2) | filter(param1=12) -%}", ext = "html")] 26 | struct InvalidNamedArg3; 27 | 28 | // Ensures that named arguments can only be passed last. 29 | #[derive(Template)] 30 | #[template(source = "{%- macro thrice(param1, param2) -%} 31 | {{ param1 }} {{ param2 }} 32 | {%- endmacro -%} 33 | {%- call thrice(param1=2, 3) -%}", ext = "html")] 34 | struct InvalidNamedArg4; 35 | 36 | // Ensures that named arguments can't be used for arguments before them. 37 | #[derive(Template)] 38 | #[template(source = "{%- macro thrice(param1, param2) -%} 39 | {{ param1 }} {{ param2 }} 40 | {%- endmacro -%} 41 | {%- call thrice(3, param1=2) -%}", ext = "html")] 42 | struct InvalidNamedArg5; 43 | 44 | fn main() { 45 | } 46 | -------------------------------------------------------------------------------- /askama_parser/benches/librustdoc/sidebar.html: -------------------------------------------------------------------------------- 1 | {% if !title.is_empty() %} 2 |

{# #} 3 | {{title_prefix}}{{title}} {# #} 4 |

5 | {% endif %} 6 | 35 | -------------------------------------------------------------------------------- /book/src/debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging and Troubleshooting 2 | 3 | You can view the parse tree for a template as well as the generated code by 4 | changing the `template` attribute item list for the template struct: 5 | 6 | ```rust 7 | #[derive(Template)] 8 | #[template(path = "hello.html", print = "all")] 9 | struct HelloTemplate<'a> { ... } 10 | ``` 11 | 12 | The `print` key can take one of four values: 13 | 14 | * `none` (the default value) 15 | * `ast` (print the parse tree) 16 | * `code` (print the generated code) 17 | * `all` (print both parse tree and code) 18 | 19 | The resulting output will be printed to `stderr` during the compilation process. 20 | 21 | The parse tree looks like this for the example template: 22 | 23 | ``` 24 | [Lit("", "Hello,", " "), Expr(WS(false, false), Var("name")), 25 | Lit("", "!", "\n")] 26 | ``` 27 | 28 | The generated code looks like this: 29 | 30 | ```rust 31 | impl < 'a > ::askama::Template for HelloTemplate< 'a > { 32 | fn render_into(&self, writer: &mut ::std::fmt::Write) -> ::askama::Result<()> { 33 | write!( 34 | writer, 35 | "Hello, {expr0}!", 36 | expr0 = &::askama::MarkupDisplay::from(&self.name), 37 | )?; 38 | Ok(()) 39 | } 40 | fn extension() -> Option<&'static str> { 41 | Some("html") 42 | } 43 | } 44 | impl < 'a > ::std::fmt::Display for HelloTemplate< 'a > { 45 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 46 | ::askama::Template::render_into(self, f).map_err(|_| ::std::fmt::Error {}) 47 | } 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /testing/tests/operators.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(path = "compare.html")] 5 | struct CompareTemplate { 6 | a: usize, 7 | b: usize, 8 | c: usize, 9 | } 10 | 11 | #[test] 12 | fn test_compare() { 13 | let t = CompareTemplate { a: 1, b: 1, c: 2 }; 14 | assert_eq!(t.render().unwrap(), "tf\ntf\ntf\ntf\ntf\ntf"); 15 | } 16 | 17 | #[derive(Template)] 18 | #[template(path = "operators.html")] 19 | struct OperatorsTemplate { 20 | a: usize, 21 | b: usize, 22 | c: usize, 23 | } 24 | 25 | #[test] 26 | fn test_operators() { 27 | let t = OperatorsTemplate { a: 1, b: 1, c: 2 }; 28 | assert_eq!(t.render().unwrap(), "muldivmodaddrshlshbandbxorborandor"); 29 | } 30 | 31 | #[derive(Template)] 32 | #[template(path = "precedence.html")] 33 | struct PrecedenceTemplate {} 34 | 35 | #[test] 36 | fn test_precedence() { 37 | let t = PrecedenceTemplate {}; 38 | assert_eq!(t.render().unwrap(), "6".repeat(7)); 39 | } 40 | 41 | #[derive(Template)] 42 | #[template(path = "ranges.txt")] 43 | struct RangesTemplate<'a> { 44 | foo: Vec<&'a str>, 45 | } 46 | 47 | #[test] 48 | fn test_ranges() { 49 | let t = RangesTemplate { 50 | foo: vec!["a", "b", "c", "d"], 51 | }; 52 | assert_eq!(t.render().unwrap(), "abcd\nbcd\n\na\nab"); 53 | } 54 | 55 | #[derive(Template)] 56 | #[template(source = "{{ true && true }}{{ false || true }}", ext = "txt")] 57 | struct ShortCircuitTemplate {} 58 | 59 | #[test] 60 | fn test_short_circuit() { 61 | let t = ShortCircuitTemplate {}; 62 | assert_eq!(t.render().unwrap(), "truetrue"); 63 | } 64 | -------------------------------------------------------------------------------- /askama_rocket/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | #![deny(elided_lifetimes_in_paths)] 3 | #![deny(unreachable_pub)] 4 | 5 | use std::io::Cursor; 6 | 7 | #[doc(no_inline)] 8 | pub use askama::*; 9 | #[doc(no_inline)] 10 | pub use rocket; 11 | use rocket::Response; 12 | 13 | #[inline] 14 | pub fn respond(tmpl: &T) -> rocket::response::Result<'static> { 15 | try_into_response(tmpl).map_err(|_| rocket::http::Status::InternalServerError) 16 | } 17 | 18 | /// Render a [`Template`] into a [`Response`], or render an error page. 19 | pub fn into_response(tmpl: &T) -> Response<'static> { 20 | match try_into_response(tmpl) { 21 | Ok(response) => response, 22 | Err(err) => { 23 | let value = err.to_string(); 24 | Response::build() 25 | .status(rocket::http::Status::InternalServerError) 26 | .header(rocket::http::Header::new( 27 | "content-type", 28 | "text/plain; charset=utf-8", 29 | )) 30 | .sized_body(value.len(), Cursor::new(value)) 31 | .finalize() 32 | } 33 | } 34 | } 35 | 36 | /// Try to render a [`Template`] into a [`Response`]. 37 | pub fn try_into_response( 38 | tmpl: &T, 39 | ) -> Result, Error> { 40 | let value = tmpl.render()?; 41 | Ok(Response::build() 42 | .header(rocket::http::Header::new("content-type", T::MIME_TYPE)) 43 | .sized_body(value.len(), Cursor::new(value)) 44 | .finalize()) 45 | } 46 | -------------------------------------------------------------------------------- /askama_actix/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | #![deny(elided_lifetimes_in_paths)] 3 | #![deny(unreachable_pub)] 4 | 5 | use std::fmt; 6 | 7 | #[doc(no_inline)] 8 | pub use actix_web; 9 | use actix_web::body::BoxBody; 10 | use actix_web::http::header::HeaderValue; 11 | use actix_web::http::StatusCode; 12 | use actix_web::{HttpResponse, HttpResponseBuilder, ResponseError}; 13 | #[doc(no_inline)] 14 | pub use askama::*; 15 | 16 | /// Render a [`Template`] into a [`HttpResponse`], or render an error page. 17 | pub fn into_response(tmpl: &T) -> HttpResponse { 18 | try_into_response(tmpl).unwrap_or_else(|err| HttpResponse::from_error(ActixError(err))) 19 | } 20 | 21 | /// Try to render a [`Template`] into a [`HttpResponse`]. 22 | pub fn try_into_response( 23 | tmpl: &T, 24 | ) -> Result, Error> { 25 | let value = tmpl.render()?; 26 | Ok(HttpResponseBuilder::new(StatusCode::OK) 27 | .content_type(HeaderValue::from_static(T::MIME_TYPE)) 28 | .body(value)) 29 | } 30 | 31 | /// Newtype to let askama::Error implement actix_web::ResponseError. 32 | struct ActixError(Error); 33 | 34 | impl fmt::Debug for ActixError { 35 | #[inline] 36 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 37 | ::fmt(&self.0, f) 38 | } 39 | } 40 | 41 | impl fmt::Display for ActixError { 42 | #[inline] 43 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 44 | ::fmt(&self.0, f) 45 | } 46 | } 47 | 48 | impl ResponseError for ActixError {} 49 | -------------------------------------------------------------------------------- /askama_parser/benches/from_str.rs: -------------------------------------------------------------------------------- 1 | use askama_parser::{Ast, Syntax}; 2 | use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; 3 | 4 | criterion_main!(benches); 5 | criterion_group!(benches, librustdoc); 6 | 7 | fn librustdoc(c: &mut Criterion) { 8 | let mut group = c.benchmark_group("librustdoc"); 9 | 10 | let mut add_benchmark = |name: &str, src: &str| { 11 | group.throughput(Throughput::Bytes(src.len() as u64)); 12 | group.bench_function(name, |b| { 13 | let syntax = &Syntax::default(); 14 | b.iter(|| Ast::from_str(black_box(src), None, black_box(syntax)).unwrap()); 15 | }); 16 | }; 17 | 18 | let all: String = LIBRUSTDOC.iter().map(|&(_, src)| src).collect(); 19 | add_benchmark("all", &all); 20 | 21 | for (name, src) in LIBRUSTDOC { 22 | add_benchmark(name, src); 23 | } 24 | 25 | group.finish(); 26 | } 27 | 28 | const LIBRUSTDOC: &[(&str, &str)] = &[ 29 | ("item_info", include_str!("./librustdoc/item_info.html")), 30 | ("item_union", include_str!("./librustdoc/item_union.html")), 31 | ("page", include_str!("./librustdoc/page.html")), 32 | ("print_item", include_str!("./librustdoc/print_item.html")), 33 | ( 34 | "short_item_info", 35 | include_str!("./librustdoc/short_item_info.html"), 36 | ), 37 | ("sidebar", include_str!("./librustdoc/sidebar.html")), 38 | ("source", include_str!("./librustdoc/source.html")), 39 | ( 40 | "type_layout_size", 41 | include_str!("./librustdoc/type_layout_size.html"), 42 | ), 43 | ("type_layout", include_str!("./librustdoc/type_layout.html")), 44 | ]; 45 | -------------------------------------------------------------------------------- /testing/tests/gen_loop_else.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | print(r'''use askama::Template; 4 | 5 | #[derive(Template)] 6 | #[template( 7 | source = "{% for v in values %}{{v}}{% else %}empty{% endfor %}", 8 | ext = "txt" 9 | )] 10 | struct ForElse<'a> { 11 | values: &'a [i32], 12 | } 13 | 14 | #[test] 15 | fn test_for_else() { 16 | let t = ForElse { values: &[1, 2, 3] }; 17 | assert_eq!(t.render().unwrap(), "123"); 18 | 19 | let t = ForElse { values: &[] }; 20 | assert_eq!(t.render().unwrap(), "empty"); 21 | } 22 | ''') 23 | 24 | for i in range(2**6): 25 | a = '-' if i & 2**0 else ' ' 26 | b = '-' if i & 2**1 else ' ' 27 | c = '-' if i & 2**2 else ' ' 28 | d = '-' if i & 2**3 else ' ' 29 | e = '-' if i & 2**4 else ' ' 30 | f = '-' if i & 2**5 else ' ' 31 | source = fr'a {{%{a}for v in values{b}%}}\t{{{{v}}}}\t{{%{c}else{d}%}}\nX\n{{%{e}endfor{f}%}} b' 32 | 33 | a = '' if i & 2**0 else r' ' 34 | b = '' if i & 2**1 else r'\t' 35 | c = '' if i & 2**2 else r'\t' 36 | d = '' if i & 2**3 else r'\n' 37 | e = '' if i & 2**4 else r'\n' 38 | f = '' if i & 2**5 else r' ' 39 | some = f'a{a}{b}1{c}{f}b' 40 | none = f'a{a}{d}X{e}{f}b' 41 | 42 | print(f'''#[derive(Template)] 43 | #[template( 44 | source = "{source}", 45 | ext = "txt" 46 | )] 47 | struct LoopElseTrim{i:02}<'a> {{ 48 | values: &'a [i32], 49 | }} 50 | 51 | #[test] 52 | fn test_loop_else_trim{i:02}() {{ 53 | let t = LoopElseTrim{i:02} {{ values: &[1] }}; 54 | assert_eq!(t.render().unwrap(), "{some}"); 55 | 56 | let t = LoopElseTrim{i:02} {{ values: &[] }}; 57 | assert_eq!(t.render().unwrap(), "{none}"); 58 | }}''') 59 | -------------------------------------------------------------------------------- /testing/tests/rust_macro.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | macro_rules! hello { 4 | () => { 5 | "world" 6 | }; 7 | } 8 | 9 | #[derive(Template)] 10 | #[template(path = "rust-macros.html")] 11 | struct RustMacrosTemplate {} 12 | 13 | #[test] 14 | fn macro_basic() { 15 | let template = RustMacrosTemplate {}; 16 | assert_eq!("Hello, world!", template.render().unwrap()); 17 | } 18 | 19 | mod foo { 20 | macro_rules! hello2 { 21 | () => { 22 | "world" 23 | }; 24 | } 25 | 26 | pub(crate) use hello2; 27 | } 28 | 29 | #[derive(Template)] 30 | #[template(path = "rust-macros-full-path.html")] 31 | struct RustMacrosFullPathTemplate {} 32 | 33 | #[test] 34 | fn macro_full_path() { 35 | let template = RustMacrosFullPathTemplate {}; 36 | assert_eq!("Hello, world!", template.render().unwrap()); 37 | } 38 | 39 | macro_rules! call_a_or_b_on_tail { 40 | ((a: $a:expr, b: $b:expr, c: $c:expr), call a: $($tail:expr),*) => { 41 | ($a)($($tail),*) 42 | }; 43 | 44 | ((a: $a:expr, b: $b:expr, c: $c:expr), call b: $($tail:expr),*) => { 45 | ($b)($($tail),*) 46 | }; 47 | 48 | ((a: $a:expr, b: $b:expr, c: $c:expr), call c: $($tail:expr),*) => { 49 | ($c)($($tail),*) 50 | }; 51 | } 52 | 53 | fn year(y: u16, _: &str, _: u8) -> u16 { 54 | y 55 | } 56 | 57 | fn month(_: u16, m: &str, _: u8) -> &str { 58 | m 59 | } 60 | 61 | fn day(_: u16, _: &str, d: u8) -> u8 { 62 | d 63 | } 64 | 65 | #[derive(Template)] 66 | #[template(path = "rust-macro-args.html")] 67 | struct RustMacrosArgTemplate {} 68 | 69 | #[test] 70 | fn macro_with_args() { 71 | let template = RustMacrosArgTemplate {}; 72 | assert_eq!("2021\nJuly\n2", template.render().unwrap()); 73 | } 74 | -------------------------------------------------------------------------------- /book/src/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | First, add the following to your crate's `Cargo.toml`: 4 | 5 | ```toml 6 | # in section [dependencies] 7 | askama = "0.12.1" 8 | ``` 9 | 10 | Now create a directory called `templates` in your crate root. 11 | In it, create a file called `hello.html`, containing the following: 12 | 13 | ``` 14 | Hello, {{ name }}! 15 | ``` 16 | 17 | In any Rust file inside your crate, add the following: 18 | 19 | ```rust 20 | use askama::Template; // bring trait in scope 21 | 22 | #[derive(Template)] // this will generate the code... 23 | #[template(path = "hello.html")] // using the template in this path, relative 24 | // to the `templates` dir in the crate root 25 | struct HelloTemplate<'a> { // the name of the struct can be anything 26 | name: &'a str, // the field name should match the variable name 27 | // in your template 28 | } 29 | 30 | fn main() { 31 | let hello = HelloTemplate { name: "world" }; // instantiate your struct 32 | println!("{}", hello.render().unwrap()); // then render it. 33 | } 34 | ``` 35 | 36 | You should now be able to compile and run this code. 37 | 38 | ## Using integrations 39 | 40 | To use one of the [integrations](./integrations.md), with axum as an example: 41 | 42 | First, add this to your `Cargo.toml` instead: 43 | 44 | ```toml 45 | # in section [dependencies] 46 | askama_axum = "0.4.0" 47 | ``` 48 | 49 | Then, import from askama_axum instead of askama: 50 | 51 | ```rust 52 | use askama_axum::Template; 53 | ``` 54 | 55 | This enables the implementation for axum's `IntoResponse` trait, 56 | so an instance of the template can be returned as a response. 57 | 58 | For other integrations, import and use their crate accordingly. 59 | -------------------------------------------------------------------------------- /testing/benches/all.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | 4 | use askama::Template; 5 | use criterion::Criterion; 6 | 7 | criterion_main!(benches); 8 | criterion_group!(benches, functions); 9 | 10 | fn functions(c: &mut Criterion) { 11 | c.bench_function("Big table", |b| big_table(b, 100)); 12 | c.bench_function("Teams", teams); 13 | } 14 | 15 | fn big_table(b: &mut criterion::Bencher, size: usize) { 16 | let mut table = Vec::with_capacity(size); 17 | for _ in 0..size { 18 | let mut inner = Vec::with_capacity(size); 19 | for i in 0..size { 20 | inner.push(i); 21 | } 22 | table.push(inner); 23 | } 24 | let ctx = BigTable { table }; 25 | b.iter(|| ctx.render().unwrap()); 26 | } 27 | 28 | #[derive(Template)] 29 | #[template(path = "big-table.html")] 30 | struct BigTable { 31 | table: Vec>, 32 | } 33 | 34 | fn teams(b: &mut criterion::Bencher) { 35 | let teams = Teams { 36 | year: 2015, 37 | teams: vec![ 38 | Team { 39 | name: "Jiangsu".into(), 40 | score: 43, 41 | }, 42 | Team { 43 | name: "Beijing".into(), 44 | score: 27, 45 | }, 46 | Team { 47 | name: "Guangzhou".into(), 48 | score: 22, 49 | }, 50 | Team { 51 | name: "Shandong".into(), 52 | score: 12, 53 | }, 54 | ], 55 | }; 56 | b.iter(|| teams.render().unwrap()); 57 | } 58 | 59 | #[derive(Template)] 60 | #[template(path = "teams.html")] 61 | struct Teams { 62 | year: u16, 63 | teams: Vec, 64 | } 65 | 66 | struct Team { 67 | name: String, 68 | score: u8, 69 | } 70 | -------------------------------------------------------------------------------- /testing/tests/ui/macro_named_argument.stderr: -------------------------------------------------------------------------------- 1 | error: no argument named `param3` in macro "thrice" 2 | --> tests/ui/macro_named_argument.rs:3:10 3 | | 4 | 3 | #[derive(Template)] 5 | | ^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error: named argument `param1` was passed more than once 10 | failed to parse template source at row 5, column 15 near: 11 | "(param1=2, param1=3) -%}" 12 | --> tests/ui/macro_named_argument.rs:11:10 13 | | 14 | 11 | #[derive(Template)] 15 | | ^^^^^^^^ 16 | | 17 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 18 | 19 | error: failed to parse template source at row 5, column 29 near: 20 | "| filter(param1=12) -%}" 21 | --> tests/ui/macro_named_argument.rs:20:10 22 | | 23 | 20 | #[derive(Template)] 24 | | ^^^^^^^^ 25 | | 26 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 27 | 28 | error: named arguments must always be passed last 29 | failed to parse template source at row 4, column 15 near: 30 | "(param1=2, 3) -%}" 31 | --> tests/ui/macro_named_argument.rs:29:10 32 | | 33 | 29 | #[derive(Template)] 34 | | ^^^^^^^^ 35 | | 36 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 37 | 38 | error: cannot have unnamed argument (`param2`) after named argument in macro "thrice" 39 | --> tests/ui/macro_named_argument.rs:37:10 40 | | 41 | 37 | #[derive(Template)] 42 | | ^^^^^^^^ 43 | | 44 | = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) 45 | -------------------------------------------------------------------------------- /testing/tests/ext.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(path = "foo.html")] 5 | struct PathHtml; 6 | 7 | #[test] 8 | fn test_path_ext_html() { 9 | let t = PathHtml; 10 | assert_eq!(t.render().unwrap(), "foo.html"); 11 | assert_eq!(PathHtml::EXTENSION, Some("html")); 12 | } 13 | 14 | #[derive(Template)] 15 | #[template(path = "foo.jinja")] 16 | struct PathJinja; 17 | 18 | #[test] 19 | fn test_path_ext_jinja() { 20 | let t = PathJinja; 21 | assert_eq!(t.render().unwrap(), "foo.jinja"); 22 | assert_eq!(PathJinja::EXTENSION, Some("jinja")); 23 | } 24 | 25 | #[derive(Template)] 26 | #[template(path = "foo.html.jinja")] 27 | struct PathHtmlJinja; 28 | 29 | #[test] 30 | fn test_path_ext_html_jinja() { 31 | let t = PathHtmlJinja; 32 | assert_eq!(t.render().unwrap(), "foo.html.jinja"); 33 | assert_eq!(PathHtmlJinja::EXTENSION, Some("html")); 34 | } 35 | 36 | #[derive(Template)] 37 | #[template(path = "foo.html", ext = "txt")] 38 | struct PathHtmlAndExtTxt; 39 | 40 | #[test] 41 | fn test_path_ext_html_and_ext_txt() { 42 | let t = PathHtmlAndExtTxt; 43 | assert_eq!(t.render().unwrap(), "foo.html"); 44 | assert_eq!(PathHtmlAndExtTxt::EXTENSION, Some("txt")); 45 | } 46 | 47 | #[derive(Template)] 48 | #[template(path = "foo.jinja", ext = "txt")] 49 | struct PathJinjaAndExtTxt; 50 | 51 | #[test] 52 | fn test_path_ext_jinja_and_ext_txt() { 53 | let t = PathJinjaAndExtTxt; 54 | assert_eq!(t.render().unwrap(), "foo.jinja"); 55 | assert_eq!(PathJinjaAndExtTxt::EXTENSION, Some("txt")); 56 | } 57 | 58 | #[derive(Template)] 59 | #[template(path = "foo.html.jinja", ext = "txt")] 60 | struct PathHtmlJinjaAndExtTxt; 61 | 62 | #[test] 63 | fn test_path_ext_html_jinja_and_ext_txt() { 64 | let t = PathHtmlJinjaAndExtTxt; 65 | assert_eq!(t.render().unwrap(), "foo.html.jinja"); 66 | assert_eq!(PathHtmlJinjaAndExtTxt::EXTENSION, Some("txt")); 67 | } 68 | -------------------------------------------------------------------------------- /askama/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "askama" 3 | version = "0.13.0" 4 | description = "Type-safe, compiled Jinja-like templates for Rust" 5 | documentation = "https://docs.rs/askama" 6 | keywords = ["markup", "template", "jinja2", "html"] 7 | categories = ["template-engine"] 8 | homepage = "https://github.com/djc/askama" 9 | repository = "https://github.com/djc/askama" 10 | license = "MIT OR Apache-2.0" 11 | workspace = ".." 12 | readme = "../README.md" 13 | edition = "2021" 14 | rust-version = "1.65" 15 | 16 | [badges] 17 | maintenance = { status = "actively-developed" } 18 | 19 | [features] 20 | default = ["config", "humansize", "num-traits", "urlencode"] 21 | config = ["askama_derive/config"] 22 | humansize = ["askama_derive/humansize", "dep:humansize"] 23 | num-traits = ["askama_derive/num-traits", "dep:num-traits"] 24 | serde_json = ["askama_derive/serde-json", "dep:serde", "dep:serde_json"] 25 | serde-json = ["serde_json"] # Alias for backwards compatibility 26 | urlencode = ["askama_derive/urlencode", "dep:percent-encoding"] 27 | with-actix-web = ["askama_derive/with-actix-web"] 28 | with-axum = ["askama_derive/with-axum"] 29 | with-rocket = ["askama_derive/with-rocket"] 30 | with-warp = ["askama_derive/with-warp"] 31 | 32 | [dependencies] 33 | askama_derive = { version = "0.13", path = "../askama_derive" } 34 | askama_escape = { version = "0.11", path = "../askama_escape" } 35 | humansize = { package = "humansize", version = "2", optional = true } 36 | num-traits = { version = "0.2.6", optional = true } 37 | percent-encoding = { version = "2.1.0", optional = true } 38 | serde = { version = "1.0", optional = true, features = ["derive"] } 39 | serde_json = { version = "1.0", optional = true } 40 | 41 | [dev-dependencies] 42 | criterion = "0.5" 43 | 44 | [[bench]] 45 | name = "to-json" 46 | harness = false 47 | required-features = ["serde-json"] 48 | 49 | [package.metadata.docs.rs] 50 | features = ["config", "humansize", "num-traits", "serde-json"] 51 | -------------------------------------------------------------------------------- /testing/templates/allow-whitespaces.html: -------------------------------------------------------------------------------- 1 | 2 | {{ tuple.0 }} 3 | {{ tuple .1 }} 4 | {{ tuple. 2 }} 5 | {{ tuple . 3 }} 6 | {% let ( t0 , t1 , t2 , t3 , ) = tuple %} 7 | 8 | {{ string }} 9 | {{ string.len( ) }} 10 | {{ string . len () }} 11 | {{ string . len () }} 12 | {{ string. len () }} 13 | {{ string . len ( ) }} 14 | 15 | {{ nested_1 . nested_2 . array [0] }} 16 | {{ nested_1 .nested_2. array [ 1 ] }} 17 | {{ nested_1 .nested_2. hash [ "key" ] }} 18 | 19 | {% let array = nested_1.nested_2.array %} 20 | {# 21 | {% let array = nested_1.nested_2.array %} 22 | {% let array = nested_1 . nested_2 . array %} 23 | #} 24 | {# 25 | {% let hash = &nested_1.nested_2.hash %} 26 | #} 27 | 28 | {{ array| json }} 29 | {{ array[..]| json }}{{ array [ .. ]| json }} 30 | {{ array[1..2]| json }}{{ array [ 1 .. 2 ]| json }} 31 | {{ array[1..=2]| json }}{{ array [ 1 ..= 2 ]| json }} 32 | {{ array[(0+1)..(3-1)]| json }}{{ array [ ( 0 + 1 ) .. ( 3 - 1 ) ]| json }} 33 | 34 | {{-1}}{{ -1 }}{{ - 1 }} 35 | {{1+2}}{{ 1+2 }}{{ 1 +2 }}{{ 1+ 2 }} {{ 1 + 2 }} 36 | {{1*2}}{{ 1*2 }}{{ 1 *2 }}{{ 1* 2 }} {{ 1 * 2 }} 37 | {{1&2}}{{ 1&2 }}{{ 1 &2 }}{{ 1& 2 }} {{ 1 & 2 }} 38 | {{1|2}}{{ 1|2 }}{{ 1 |2 }}{{ 1| 2 }} {{ 1 | 2 }} 39 | 40 | {{true}}{{false}} 41 | {{!true}}{{ !true }}{{ ! true }} 42 | {# 43 | {{true&&false}}{{ true&&false }}{{ true &&false }}{{ true&& false }} {{ true && false }} 44 | {{true||false}}{{ true||false }}{{ true ||false }}{{ true|| false }} {{ true || false }} 45 | #} 46 | 47 | {{ self.f0() }}{{ self.f0 () }}{{ self.f0 ( ) }} 48 | {{ self.f1("1") }}{{ self.f1 ( "1" ) }}{{ self.f1 ( "1" ) }} 49 | {{ self.f2("1","2") }}{{ self.f2 ( "1" ,"2" ) }}{{ self.f2 ( "1" , "2" ) }} 50 | 51 | {% for s in 0..5 %}{% endfor %} 52 | {% for s in 0 .. 5 %}{% endfor %} 53 | 54 | {% match option %} 55 | {% when Option :: Some with ( option ) %} 56 | {% when std :: option :: Option :: None %} 57 | {% endmatch %} 58 | 59 | {{ std::string::String::new () }} 60 | {# 61 | {{ ::std::string::String::new () }} 62 | #} 63 | -------------------------------------------------------------------------------- /testing/tests/try.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{% let v = self.parse()? %}{{s}}={{v}}", ext = "txt")] 5 | struct IntParserTemplate<'a> { 6 | s: &'a str, 7 | } 8 | 9 | impl IntParserTemplate<'_> { 10 | fn parse(&self) -> Result { 11 | self.s.parse() 12 | } 13 | } 14 | 15 | #[test] 16 | fn test_int_parser() { 17 | let template = IntParserTemplate { s: "💯" }; 18 | assert!(matches!(template.render(), Err(askama::Error::Custom(_)))); 19 | assert_eq!( 20 | format!("{}", &template.render().unwrap_err()), 21 | "invalid digit found in string" 22 | ); 23 | 24 | let template = IntParserTemplate { s: "100" }; 25 | assert_eq!(template.render().unwrap(), "100=100"); 26 | } 27 | 28 | #[derive(Template)] 29 | #[template(source = "{{ value()? }}", ext = "txt")] 30 | struct FailFmt { 31 | value: fn() -> Result<&'static str, std::fmt::Error>, 32 | } 33 | 34 | #[test] 35 | fn fail_fmt() { 36 | let template = FailFmt { 37 | value: || Err(std::fmt::Error), 38 | }; 39 | assert!(matches!(template.render(), Err(askama::Error::Custom(_)))); 40 | assert_eq!( 41 | format!("{}", &template.render().unwrap_err()), 42 | format!("{}", std::fmt::Error) 43 | ); 44 | 45 | let template = FailFmt { 46 | value: || Ok("hello world"), 47 | }; 48 | assert_eq!(template.render().unwrap(), "hello world"); 49 | } 50 | 51 | #[derive(Template)] 52 | #[template(source = "{{ value()? }}", ext = "txt")] 53 | struct FailStr { 54 | value: fn() -> Result<&'static str, &'static str>, 55 | } 56 | 57 | #[test] 58 | fn fail_str() { 59 | let template = FailStr { 60 | value: || Err("FAIL"), 61 | }; 62 | assert!(matches!(template.render(), Err(askama::Error::Custom(_)))); 63 | assert_eq!(format!("{}", &template.render().unwrap_err()), "FAIL"); 64 | 65 | let template = FailStr { 66 | value: || Ok("hello world"), 67 | }; 68 | assert_eq!(template.render().unwrap(), "hello world"); 69 | } 70 | -------------------------------------------------------------------------------- /testing/tests/tuple.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | struct Post { 4 | id: u32, 5 | } 6 | 7 | struct Client<'a> { 8 | can_post_ids: &'a [u32], 9 | can_update_ids: &'a [u32], 10 | } 11 | 12 | impl Client<'_> { 13 | fn can_post(&self, post: &Post) -> bool { 14 | self.can_post_ids.contains(&post.id) 15 | } 16 | 17 | fn can_update(&self, post: &Post) -> bool { 18 | self.can_update_ids.contains(&post.id) 19 | } 20 | } 21 | 22 | #[derive(Template)] 23 | #[template( 24 | source = r#" 25 | {%- match (client.can_post(post), client.can_update(post)) -%} 26 | {%- when (false, false) -%} 27 | No! 28 | {%- when (can_post, can_update) -%} 29 |
    30 | {%- if can_post -%}
  • post
  • {%- endif -%} 31 | {%- if can_update -%}
  • update
  • {%- endif -%} 32 |
33 | {%- endmatch -%} 34 | "#, 35 | ext = "txt" 36 | )] 37 | struct TupleTemplate<'a> { 38 | client: &'a Client<'a>, 39 | post: &'a Post, 40 | } 41 | 42 | #[test] 43 | fn test_tuple() { 44 | let template = TupleTemplate { 45 | client: &Client { 46 | can_post_ids: &[1, 2], 47 | can_update_ids: &[2, 3], 48 | }, 49 | post: &Post { id: 1 }, 50 | }; 51 | assert_eq!(template.render().unwrap(), "
  • post
"); 52 | 53 | let template = TupleTemplate { 54 | client: &Client { 55 | can_post_ids: &[1, 2], 56 | can_update_ids: &[2, 3], 57 | }, 58 | post: &Post { id: 2 }, 59 | }; 60 | assert_eq!( 61 | template.render().unwrap(), 62 | "
  • post
  • update
" 63 | ); 64 | 65 | let template = TupleTemplate { 66 | client: &Client { 67 | can_post_ids: &[1, 2], 68 | can_update_ids: &[2, 3], 69 | }, 70 | post: &Post { id: 3 }, 71 | }; 72 | assert_eq!(template.render().unwrap(), "
  • update
"); 73 | 74 | let template = TupleTemplate { 75 | client: &Client { 76 | can_post_ids: &[1, 2], 77 | can_update_ids: &[2, 3], 78 | }, 79 | post: &Post { id: 4 }, 80 | }; 81 | assert_eq!(template.render().unwrap(), "No!"); 82 | } 83 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_filters.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use arbitrary::{Arbitrary, Unstructured}; 3 | use askama::filters::*; 4 | use askama_escape::Html; 5 | use libfuzzer_sys::fuzz_target; 6 | use std::str::{self}; 7 | 8 | macro_rules! fuzz { 9 | ($name: ident, $func: ident) => { 10 | fn $name(data: &[u8]) { 11 | if let Ok(data) = str::from_utf8(data) { 12 | if let Ok(d) = $func(data) { 13 | let _ = d.to_string(); 14 | } 15 | } 16 | } 17 | }; 18 | ($name: ident, $func: ident, $arg_type: ty) => { 19 | fn $name(data: &[u8]) { 20 | if let Some(adata) = get_arbitrary_data::<$arg_type>(data) { 21 | if let Ok(sdata) = str::from_utf8(data) { 22 | if let Ok(d) = $func(sdata, adata) { 23 | let _ = d.to_string(); 24 | } 25 | } 26 | } 27 | } 28 | }; 29 | } 30 | 31 | fn get_arbitrary_data<'a, T>(data: &'a [u8]) -> Option 32 | where 33 | T: Arbitrary<'a>, 34 | { 35 | T::arbitrary(&mut Unstructured::new(data)).ok() 36 | } 37 | 38 | fuzz!(fuzz_urlencode, urlencode); 39 | fuzz!(fuzz_urlencode_strict, urlencode_strict); 40 | fuzz!(fuzz_linebreaks, linebreaks); 41 | fuzz!(fuzz_paragraphbreaks, paragraphbreaks); 42 | fuzz!(fuzz_trim, trim); 43 | fuzz!(fuzz_title, title); 44 | fuzz!(fuzz_capitalize, capitalize); 45 | fuzz!(fuzz_truncate, truncate, usize); 46 | fuzz!(fuzz_indent, indent, usize); 47 | fuzz!(fuzz_center, center, usize); 48 | 49 | fn fuzz_escape(data: &[u8]) { 50 | if let Ok(data) = str::from_utf8(data) { 51 | let _ = askama_escape::escape(data, Html); 52 | } 53 | } 54 | 55 | fuzz_target!(|data: &[u8]| { 56 | if data.is_empty() { 57 | return; 58 | } 59 | let idx = data[0] % 11; 60 | let data = &data[1..]; 61 | match idx { 62 | 0 => fuzz_urlencode(data), 63 | 1 => fuzz_urlencode_strict(data), 64 | 2 => fuzz_linebreaks(data), 65 | 3 => fuzz_paragraphbreaks(data), 66 | 4 => fuzz_trim(data), 67 | 5 => fuzz_title(data), 68 | 6 => fuzz_capitalize(data), 69 | 7 => fuzz_truncate(data), 70 | 8 => fuzz_indent(data), 71 | 9 => fuzz_escape(data), 72 | _ => fuzz_center(data), 73 | } 74 | }); 75 | -------------------------------------------------------------------------------- /testing/tests/calls.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template)] 4 | #[template(source = "{{ func(value) }}", ext = "txt")] 5 | struct OneFunction { 6 | func: fn(&i32) -> i32, 7 | value: i32, 8 | } 9 | 10 | #[test] 11 | fn test_one_func() { 12 | let t = OneFunction { 13 | func: |&i| 2 * i, 14 | value: 123, 15 | }; 16 | assert_eq!(t.render().unwrap(), "246"); 17 | } 18 | 19 | #[derive(Template)] 20 | #[template(source = "{{ self.func(value) }}", ext = "txt")] 21 | struct OneFunctionSelf { 22 | value: i32, 23 | } 24 | 25 | impl OneFunctionSelf { 26 | fn func(&self, i: &i32) -> i32 { 27 | 2 * i 28 | } 29 | } 30 | 31 | #[test] 32 | fn test_one_func_self() { 33 | let t = OneFunctionSelf { value: 123 }; 34 | assert_eq!(t.render().unwrap(), "246"); 35 | } 36 | 37 | #[derive(Template)] 38 | #[template(source = "{{ func[index](value) }}", ext = "txt")] 39 | struct OneFunctionIndex<'a> { 40 | func: &'a [fn(&i32) -> i32], 41 | value: i32, 42 | index: usize, 43 | } 44 | 45 | #[test] 46 | fn test_one_func_index() { 47 | let t = OneFunctionIndex { 48 | func: &[|_| panic!(), |&i| 2 * i, |_| panic!(), |_| panic!()], 49 | value: 123, 50 | index: 1, 51 | }; 52 | assert_eq!(t.render().unwrap(), "246"); 53 | } 54 | 55 | struct AddToGetAFunction; 56 | 57 | impl std::ops::Add for &AddToGetAFunction { 58 | type Output = fn(&i32) -> i32; 59 | 60 | fn add(self, rhs: usize) -> Self::Output { 61 | assert_eq!(rhs, 1); 62 | |&i| 2 * i 63 | } 64 | } 65 | 66 | #[derive(Template)] 67 | #[template(source = "{{ (func + index)(value) }}", ext = "txt")] 68 | struct OneFunctionBinop<'a> { 69 | func: &'a AddToGetAFunction, 70 | value: i32, 71 | index: usize, 72 | } 73 | 74 | #[test] 75 | fn test_one_func_binop() { 76 | let t = OneFunctionBinop { 77 | func: &AddToGetAFunction, 78 | value: 123, 79 | index: 1, 80 | }; 81 | assert_eq!(t.render().unwrap(), "246"); 82 | } 83 | 84 | fn double_attr_arg_helper(x: u32) -> u32 { 85 | x * x + x 86 | } 87 | 88 | #[derive(askama::Template)] 89 | #[template( 90 | source = "{{ self::double_attr_arg_helper(self.x.0 + 2) }}", 91 | ext = "txt" 92 | )] 93 | struct DoubleAttrArg { 94 | x: (u32,), 95 | } 96 | 97 | #[test] 98 | fn test_double_attr_arg() { 99 | let t = DoubleAttrArg { x: (10,) }; 100 | assert_eq!(t.render().unwrap(), "156"); 101 | } 102 | -------------------------------------------------------------------------------- /book/src/askama.md: -------------------------------------------------------------------------------- 1 | # Askama 2 | 3 | [![Documentation](https://docs.rs/askama/badge.svg)](https://docs.rs/askama/) 4 | [![Latest version](https://img.shields.io/crates/v/askama.svg)](https://crates.io/crates/askama) 5 | [![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) 6 | [![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/ZucwjE6bmT) 7 | 8 | Askama implements a template rendering engine based on [Jinja](https://jinja.palletsprojects.com/). 9 | It generates Rust code from your templates at compile time 10 | based on a user-defined `struct` to hold the template's context. 11 | See below for an example, or read [the book][docs]. 12 | 13 | **"Pretty exciting. I would love to use this already."** -- 14 | [Armin Ronacher][mitsuhiko], creator of Jinja 15 | 16 | All feedback welcome. Feel free to file bugs, requests for documentation and 17 | any other feedback to the [issue tracker][issues] or [tweet me][twitter]. 18 | 19 | Askama was created by and is maintained by Dirkjan Ochtman. If you are in a 20 | position to support ongoing maintenance and further development or use it 21 | in a for-profit context, please consider supporting my open source work on 22 | [Patreon][patreon]. 23 | 24 | ### Feature highlights 25 | 26 | * Construct templates using a familiar, easy-to-use syntax 27 | * Benefit from the safety provided by Rust's type system 28 | * Template code is compiled into your crate for [optimal performance][benchmarks] 29 | * Optional built-in support for Actix, Axum, Gotham, Mendes, Rocket, tide, and warp web frameworks 30 | * Debugging features to assist you in template development 31 | * Templates must be valid UTF-8 and produce UTF-8 when rendered 32 | * IDE support available in [JetBrains products](https://plugins.jetbrains.com/plugin/16591-askama-template-support) 33 | * Works on stable Rust 34 | 35 | ### Supported in templates 36 | 37 | * Template inheritance 38 | * Loops, if/else statements and include support 39 | * Macro support 40 | * Variables (no mutability allowed) 41 | * Some built-in filters, and the ability to use your own 42 | * Whitespace suppressing with '-' markers 43 | * Opt-out HTML escaping 44 | * Syntax customization 45 | 46 | [docs]: https://djc.github.io/askama/ 47 | [fafhrd91]: https://github.com/fafhrd91 48 | [mitsuhiko]: http://lucumr.pocoo.org/ 49 | [issues]: https://github.com/djc/askama/issues 50 | [twitter]: https://twitter.com/djco/ 51 | [patreon]: https://www.patreon.com/dochtman 52 | [benchmarks]: https://github.com/djc/template-benchmarks-rs 53 | -------------------------------------------------------------------------------- /book/src/integrations.md: -------------------------------------------------------------------------------- 1 | # Integrations 2 | 3 | ## Rocket integration 4 | 5 | In your template definitions, replace `askama::Template` with 6 | [`askama_rocket::Template`][askama_rocket]. 7 | 8 | Enabling the `with-rocket` feature appends an implementation of Rocket's 9 | `Responder` trait for each template type. This makes it easy to trivially 10 | return a value of that type in a Rocket handler. See 11 | [the example](https://github.com/djc/askama/blob/main/askama_rocket/tests/basic.rs) 12 | from the Askama test suite for more on how to integrate. 13 | 14 | In case a run-time error occurs during templating, a `500 Internal Server 15 | Error` `Status` value will be returned, so that this can be further 16 | handled by your error catcher. 17 | 18 | ## Actix-web integration 19 | 20 | In your template definitions, replace `askama::Template` with 21 | [`askama_actix::Template`][askama_actix]. 22 | 23 | Enabling the `with-actix-web` feature appends an implementation of Actix-web's 24 | `Responder` trait for each template type. This makes it easy to trivially return 25 | a value of that type in an Actix-web handler. See 26 | [the example](https://github.com/djc/askama/blob/main/askama_actix/tests/basic.rs) 27 | from the Askama test suite for more on how to integrate. 28 | 29 | ## Axum integration 30 | 31 | In your template definitions, replace `askama::Template` with 32 | [`askama_axum::Template`][askama_axum]. 33 | 34 | Enabling the `with-axum` feature appends an implementation of Axum's 35 | `IntoResponse` trait for each template type. This makes it easy to trivially 36 | return a value of that type in a Axum handler. See 37 | [the example](https://github.com/djc/askama/blob/main/askama_axum/tests/basic.rs) 38 | from the Askama test suite for more on how to integrate. 39 | 40 | In case of a run-time error occurring during templating, the response will be of the same 41 | signature, with a status code of `500 Internal Server Error`, mime `*/*`, and an empty `Body`. 42 | This preserves the response chain if any custom error handling needs to occur. 43 | 44 | ## Warp integration 45 | 46 | In your template definitions, replace `askama::Template` with 47 | [`askama_warp::Template`][askama_warp]. 48 | 49 | Enabling the `with-warp` feature appends an implementation of Warp's `Reply` 50 | trait for each template type. This makes it simple to return a template from 51 | a Warp filter. See [the example](https://github.com/djc/askama/blob/main/askama_warp/tests/warp.rs) 52 | from the Askama test suite for more on how to integrate. 53 | 54 | [askama_rocket]: https://docs.rs/askama_rocket 55 | [askama_actix]: https://docs.rs/askama_actix 56 | [askama_axum]: https://docs.rs/askama_axum 57 | [askama_warp]: https://docs.rs/askama_warp 58 | -------------------------------------------------------------------------------- /askama/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::convert::Infallible; 2 | use std::fmt::{self, Display}; 3 | 4 | pub type Result = ::std::result::Result; 5 | 6 | /// askama error type 7 | /// 8 | /// # Feature Interaction 9 | /// 10 | /// If the feature `serde_json` is enabled an 11 | /// additional error variant `Json` is added. 12 | /// 13 | /// # Why not `failure`/`error-chain`? 14 | /// 15 | /// Error from `error-chain` are not `Sync` which 16 | /// can lead to problems e.g. when this is used 17 | /// by a crate which use `failure`. Implementing 18 | /// `Fail` on the other hand prevents the implementation 19 | /// of `std::error::Error` until specialization lands 20 | /// on stable. While errors impl. `Fail` can be 21 | /// converted to a type impl. `std::error::Error` 22 | /// using a adapter the benefits `failure` would 23 | /// bring to this crate are small, which is why 24 | /// `std::error::Error` was used. 25 | /// 26 | #[non_exhaustive] 27 | #[derive(Debug)] 28 | pub enum Error { 29 | /// formatting error 30 | Fmt(fmt::Error), 31 | 32 | /// an error raised by using `?` in a template 33 | Custom(Box), 34 | 35 | /// json conversion error 36 | #[cfg(feature = "serde_json")] 37 | Json(::serde_json::Error), 38 | } 39 | 40 | impl std::error::Error for Error { 41 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 42 | match *self { 43 | Error::Fmt(ref err) => Some(err), 44 | Error::Custom(ref err) => Some(err.as_ref()), 45 | #[cfg(feature = "serde_json")] 46 | Error::Json(ref err) => Some(err), 47 | } 48 | } 49 | } 50 | 51 | impl Display for Error { 52 | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 53 | match self { 54 | Error::Fmt(err) => write!(formatter, "formatting error: {err}"), 55 | Error::Custom(err) => write!(formatter, "{err}"), 56 | #[cfg(feature = "serde_json")] 57 | Error::Json(err) => write!(formatter, "json conversion error: {err}"), 58 | } 59 | } 60 | } 61 | 62 | impl From for Error { 63 | fn from(err: fmt::Error) -> Self { 64 | Error::Fmt(err) 65 | } 66 | } 67 | 68 | #[cfg(feature = "serde_json")] 69 | impl From<::serde_json::Error> for Error { 70 | fn from(err: ::serde_json::Error) -> Self { 71 | Error::Json(err) 72 | } 73 | } 74 | 75 | impl From for Error { 76 | #[inline] 77 | fn from(value: Infallible) -> Self { 78 | match value {} 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | use super::Error; 85 | 86 | #[allow(dead_code)] 87 | trait AssertSendSyncStatic: Send + Sync + 'static {} 88 | impl AssertSendSyncStatic for Error {} 89 | } 90 | -------------------------------------------------------------------------------- /askama_parser/benches/librustdoc/type_layout.html: -------------------------------------------------------------------------------- 1 |

{# #} 2 | Layout§ {# #} 3 |

{# #} 4 |
{# #} 5 | {% match type_layout_size %} 6 | {% when Ok(type_layout_size) %} 7 |
{# #} 8 |

{# #} 9 | Note: Most layout information is completely {#+ #} 10 | unstable and may even differ between compilations. {#+ #} 11 | The only exception is types with certain repr(...) {#+ #} 12 | attributes. Please see the Rust Reference's {#+ #} 13 | “Type Layout” {#+ #} 14 | chapter for details on type layout guarantees. {# #} 15 |

{# #} 16 |
{# #} 17 |

Size: {{+ type_layout_size|safe }}

{# #} 18 | {% if !variants.is_empty() %} 19 |

{# #} 20 | Size for each variant: {# #} 21 |

{# #} 22 |
    {# #} 23 | {% for (name, layout_size) in variants %} 24 |
  • {# #} 25 | {{ name }}: {#+ #} 26 | {{ layout_size|safe }} 27 |
  • {# #} 28 | {% endfor %} 29 |
{# #} 30 | {% endif %} 31 | {# This kind of layout error can occur with valid code, e.g. if you try to 32 | get the layout of a generic type such as `Vec`. #} 33 | {% when Err(LayoutError::Unknown(_)) %} 34 |

{# #} 35 | Note: Unable to compute type layout, {#+ #} 36 | possibly due to this type having generic parameters. {#+ #} 37 | Layout can only be computed for concrete, fully-instantiated types. {# #} 38 |

{# #} 39 | {# This kind of error probably can't happen with valid code, but we don't 40 | want to panic and prevent the docs from building, so we just let the 41 | user know that we couldn't compute the layout. #} 42 | {% when Err(LayoutError::SizeOverflow(_)) %} 43 |

{# #} 44 | Note: Encountered an error during type layout; {#+ #} 45 | the type was too big. {# #} 46 |

{# #} 47 | {% when Err(LayoutError::ReferencesError(_)) %} 48 |

{# #} 49 | Note: Encountered an error during type layout; {#+ #} 50 | the type references errors. {# #} 51 |

{# #} 52 | {% when Err(LayoutError::NormalizationFailure(_, _)) %} 53 |

{# #} 54 | Note: Encountered an error during type layout; {#+ #} 55 | the type failed to be normalized. {# #} 56 |

{# #} 57 | {% when Err(LayoutError::Cycle(_)) %} 58 |

{# #} 59 | Note: Encountered an error during type layout; {#+ #} 60 | the type's layout depended on the type's layout itself. {# #} 61 |

{# #} 62 | {% endmatch %} 63 |
{# #} 64 | -------------------------------------------------------------------------------- /askama/src/filters/json.rs: -------------------------------------------------------------------------------- 1 | use std::convert::Infallible; 2 | use std::{fmt, io, str}; 3 | 4 | use serde::Serialize; 5 | use serde_json::to_writer_pretty; 6 | 7 | /// Serialize to JSON (requires `json` feature) 8 | /// 9 | /// The generated string does not contain ampersands `&`, chevrons `< >`, or apostrophes `'`. 10 | /// To use it in a ` 16 | /// ``` 17 | /// 18 | /// To use it in HTML attributes, you can either use it in quotation marks `"{{data|json}}"` as is, 19 | /// or in apostrophes with the (optional) safe filter `'{{data|json|safe}}'`. 20 | /// In HTML texts the output of e.g. `
{{data|json|safe}}
` is safe, too. 21 | #[inline] 22 | pub fn json(s: S) -> Result { 23 | Ok(ToJson(s)) 24 | } 25 | 26 | #[derive(Debug, Clone)] 27 | struct ToJson(S); 28 | 29 | impl fmt::Display for ToJson { 30 | #[inline] 31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 32 | to_writer_pretty(JsonWriter(f), &self.0).map_err(|_| fmt::Error) 33 | } 34 | } 35 | 36 | struct JsonWriter<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>); 37 | 38 | impl io::Write for JsonWriter<'_, '_> { 39 | #[inline] 40 | fn write(&mut self, bytes: &[u8]) -> io::Result { 41 | self.write_all(bytes)?; 42 | Ok(bytes.len()) 43 | } 44 | 45 | #[inline] 46 | fn write_all(&mut self, bytes: &[u8]) -> io::Result<()> { 47 | write(self.0, bytes).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err)) 48 | } 49 | 50 | #[inline] 51 | fn flush(&mut self) -> io::Result<()> { 52 | Ok(()) 53 | } 54 | } 55 | 56 | fn write(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result { 57 | let mut last = 0; 58 | for (index, byte) in bytes.iter().enumerate() { 59 | let escaped = match byte { 60 | b'&' => Some(br"\u0026"), 61 | b'\'' => Some(br"\u0027"), 62 | b'<' => Some(br"\u003c"), 63 | b'>' => Some(br"\u003e"), 64 | _ => None, 65 | }; 66 | if let Some(escaped) = escaped { 67 | f.write_str(unsafe { str::from_utf8_unchecked(&bytes[last..index]) })?; 68 | f.write_str(unsafe { str::from_utf8_unchecked(escaped) })?; 69 | last = index + 1; 70 | } 71 | } 72 | f.write_str(unsafe { str::from_utf8_unchecked(&bytes[last..]) }) 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | 79 | #[test] 80 | fn test_json() { 81 | assert_eq!(json(true).unwrap().to_string(), "true"); 82 | assert_eq!(json("foo").unwrap().to_string(), r#""foo""#); 83 | assert_eq!(json(true).unwrap().to_string(), "true"); 84 | assert_eq!(json("foo").unwrap().to_string(), r#""foo""#); 85 | assert_eq!( 86 | json("