Skip to content

Commit 5c762ab

Browse files
Add new create tag
1 parent 650efd9 commit 5c762ab

File tree

10 files changed

+130
-8
lines changed

10 files changed

+130
-8
lines changed

askama_derive/src/generator/expr.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,6 @@ impl<'a> Generator<'a, '_> {
960960
DisplayWrap::Unwrapped
961961
}
962962

963-
// FIXME: This function should have a `Span`, but `cond.target` isn't `WithSpan`.
964963
pub(super) fn visit_target(
965964
&mut self,
966965
ctx: &Context<'_>,

askama_derive/src/generator/node.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::str::FromStr;
77

88
use parser::expr::BinOp;
99
use parser::node::{
10-
Call, Comment, Cond, CondTest, FilterBlock, If, Include, Let, Lit, Loop, Match, Whitespace, Ws,
10+
Call, Comment, Cond, CondTest, Create, FilterBlock, If, Include, Let, Lit, Loop, Match, Whitespace, Ws,
1111
};
1212
use parser::{Expr, Node, Span, Target, WithSpan};
1313
use proc_macro2::TokenStream;
@@ -103,6 +103,9 @@ impl<'a> Generator<'a, '_> {
103103
Node::Let(ref l) => {
104104
self.write_let(ctx, buf, l)?;
105105
}
106+
Node::Create(ref c) => {
107+
self.write_create(ctx, buf, c)?;
108+
}
106109
Node::If(ref i) => {
107110
size_hint += self.write_if(ctx, buf, i)?;
108111
}
@@ -913,6 +916,32 @@ impl<'a> Generator<'a, '_> {
913916
Ok(())
914917
}
915918

919+
fn write_create(
920+
&mut self,
921+
ctx: &Context<'_>,
922+
buf: &mut Buffer,
923+
c: &'a WithSpan<Create<'_>>,
924+
) -> Result<(), CompileError> {
925+
let span = ctx.span_for_node(c.span());
926+
if *c.var_name == "_" {
927+
return Err(ctx.generate_error(
928+
"`_` cannot be used when there is no value assigned, use `let` instead",
929+
c.var_name.span(),
930+
));
931+
}
932+
self.handle_ws(c.ws);
933+
934+
self.write_buf_writable(ctx, buf)?;
935+
buf.write_token(Token![let], span);
936+
if c.is_mutable {
937+
buf.write_token(Token![mut], span);
938+
}
939+
self.visit_target(ctx, buf, false, true, &Target::Name(c.var_name), span);
940+
buf.write_token(Token![;], span);
941+
942+
Ok(())
943+
}
944+
916945
// If `name` is `Some`, this is a call to a block definition, and we have to find
917946
// the first block for that name from the ancestry chain. If name is `None`, this
918947
// is from a `super()` call, and we can get the name from `self.super_block`.

askama_derive/src/input.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ impl TemplateInput<'_> {
310310
| Node::Expr(_, _)
311311
| Node::Extends(_)
312312
| Node::Let(_)
313+
| Node::Create(_)
313314
| Node::Import(_)
314315
| Node::Macro(_)
315316
| Node::Raw(_)

askama_parser/src/node.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub enum Node<'a> {
2222
Expr(Ws, WithSpan<Box<Expr<'a>>>),
2323
Call(WithSpan<Call<'a>>),
2424
Let(WithSpan<Let<'a>>),
25+
Create(WithSpan<Create<'a>>),
2526
If(WithSpan<If<'a>>),
2627
Match(WithSpan<Match<'a>>),
2728
Loop(WithSpan<Loop<'a>>),
@@ -93,6 +94,7 @@ impl<'a: 'l, 'l> Node<'a> {
9394

9495
let func = match tag {
9596
"call" => Call::parse,
97+
"create" => Create::parse,
9698
"let" | "set" => Let::parse,
9799
"if" => If::parse,
98100
"for" => Loop::parse,
@@ -190,6 +192,7 @@ impl<'a: 'l, 'l> Node<'a> {
190192
Self::Expr(_, span) => span.span,
191193
Self::Call(span) => span.span,
192194
Self::Let(span) => span.span,
195+
Self::Create(span) => span.span,
193196
Self::If(span) => span.span,
194197
Self::Match(span) => span.span,
195198
Self::Loop(span) => span.span,
@@ -1198,6 +1201,35 @@ impl<'a: 'l, 'l> Raw<'a> {
11981201
}
11991202
}
12001203

1204+
#[derive(Debug, PartialEq)]
1205+
pub struct Create<'a> {
1206+
pub ws: Ws,
1207+
pub var_name: WithSpan<&'a str>,
1208+
pub is_mutable: bool,
1209+
}
1210+
1211+
impl<'a: 'l, 'l> Create<'a> {
1212+
fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1213+
let mut p = (
1214+
opt(Whitespace::parse),
1215+
ws(keyword("create").span()),
1216+
ws(opt(keyword("mut").span())),
1217+
ws(identifier.with_span()),
1218+
opt(Whitespace::parse),
1219+
);
1220+
let (pws, span, is_mut, (var_name, var_name_span), nws) = p.parse_next(i)?;
1221+
1222+
Ok(Box::new(Node::Create(WithSpan::new(
1223+
Create {
1224+
ws: Ws(pws, nws),
1225+
var_name: WithSpan::new(var_name, var_name_span),
1226+
is_mutable: is_mut.is_some(),
1227+
},
1228+
span,
1229+
))))
1230+
}
1231+
}
1232+
12011233
#[derive(Debug, PartialEq)]
12021234
pub struct Let<'a> {
12031235
pub ws: Ws,

testing/tests/create.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use askama::Template;
2+
3+
// This test ensures that rust macro calls in `let`/`set` statements are not prepended with `&`.
4+
#[test]
5+
fn create() {
6+
#[derive(Template)]
7+
#[template(
8+
source = r#"{%- create x -%}
9+
{%- if y -%}
10+
{%- let x = String::new() %}
11+
{%- else -%}
12+
{%- let x = format!("blob") %}
13+
{%- endif -%}
14+
{{ x }}"#,
15+
ext = "html"
16+
)]
17+
struct A {
18+
y: bool,
19+
}
20+
21+
assert_eq!(A { y: false }.render().unwrap(), "blob");
22+
assert_eq!(A { y: true }.render().unwrap(), "");
23+
}

testing/tests/let.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ fn let_macro() {
1818
y: bool,
1919
}
2020

21-
let template = A { y: false };
22-
assert_eq!(template.render().unwrap(), "blob")
21+
assert_eq!(A { y: false }.render().unwrap(), "blob");
22+
assert_eq!(A { y: true }.render().unwrap(), "");
2323
}
2424

2525
// Ensures that variables name can start with `_`.

testing/tests/ui/create.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use askama::Template;
2+
3+
#[derive(Template)]
4+
#[template(source = "{% create x = 'a' %}", ext = "html")]
5+
struct Create1;
6+
7+
fn main() {}

testing/tests/ui/create.stderr

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
error: failed to parse template source
2+
--> <source attribute>:1:12
3+
"= 'a' %}"
4+
--> tests/ui/create.rs:4:21
5+
|
6+
4 | #[template(source = "{% create x = 'a' %}", ext = "html")]
7+
| ^^^^^^^^^^^^^^^^^^^^^^

testing/tests/ui/let.rs renamed to testing/tests/ui/underscore.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,12 @@ struct UnderscoreErr3;
1616
#[template(source = r#"{% match _ %}{% endmatch %}"#, ext = "html")]
1717
struct UnderscoreErr4;
1818

19+
#[derive(Template)]
20+
#[template(source = r#"{% let _ %}"#, ext = "html")]
21+
struct UnderscoreErr5;
22+
23+
#[derive(Template)]
24+
#[template(source = r#"{% create _ %}"#, ext = "html")]
25+
struct UnderscoreErr6;
26+
1927
fn main() {}
Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,47 @@
11
error: reserved keyword `_` cannot be used here
22
--> <source attribute>:1:12
33
"_] %}"
4-
--> tests/ui/let.rs:4:21
4+
--> tests/ui/underscore.rs:4:21
55
|
66
4 | #[template(source = r#"{% let x = [_] %}"#, ext = "html")]
77
| ^^^^^^^^^^^^^^^^^^^^^^
88

99
error: reserved keyword `_` cannot be used here
1010
--> <source attribute>:1:7
1111
"_ + 12) != 0 %}{% endif %}"
12-
--> tests/ui/let.rs:8:21
12+
--> tests/ui/underscore.rs:8:21
1313
|
1414
8 | #[template(source = r#"{% if (_ + 12) != 0 %}{% endif %}"#, ext = "html")]
1515
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1616

1717
error: reserved keyword `_` cannot be used here
1818
--> <source attribute>:1:12
1919
"_ %}{% endif %}"
20-
--> tests/ui/let.rs:12:21
20+
--> tests/ui/underscore.rs:12:21
2121
|
2222
12 | #[template(source = r#"{% if 12 == _ %}{% endif %}"#, ext = "html")]
2323
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2424

2525
error: reserved keyword `_` cannot be used here
2626
--> <source attribute>:1:9
2727
"_ %}{% endmatch %}"
28-
--> tests/ui/let.rs:16:21
28+
--> tests/ui/underscore.rs:16:21
2929
|
3030
16 | #[template(source = r#"{% match _ %}{% endmatch %}"#, ext = "html")]
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
33+
error: when you forward-define a variable, you cannot use a pattern in place of a variable name
34+
--> <source attribute>:1:7
35+
"_ %}"
36+
--> tests/ui/underscore.rs:20:21
37+
|
38+
20 | #[template(source = r#"{% let _ %}"#, ext = "html")]
39+
| ^^^^^^^^^^^^^^^^
40+
41+
error: `_` cannot be used when there is no value assigned, use `let` instead
42+
--> UnderscoreErr6.html:1:10
43+
"_ %}"
44+
--> tests/ui/underscore.rs:24:21
45+
|
46+
24 | #[template(source = r#"{% create _ %}"#, ext = "html")]
47+
| ^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)