Skip to content

Commit ac68829

Browse files
committed
derive: escape strings at compile-time when possible
1 parent f8b0481 commit ac68829

File tree

2 files changed

+57
-64
lines changed

2 files changed

+57
-64
lines changed

rinja_derive/src/generator.rs

+47-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use quote::quote;
1414

1515
use crate::config::WhitespaceHandling;
1616
use crate::heritage::{Context, Heritage};
17+
use crate::html::write_escaped_str;
1718
use crate::input::{Source, TemplateInput};
1819
use crate::{CompileError, MsgValidEscapers, CRATE};
1920

@@ -1162,8 +1163,53 @@ impl<'a> Generator<'a> {
11621163
}
11631164

11641165
fn write_expr(&mut self, ws: Ws, s: &'a WithSpan<'a, Expr<'a>>) {
1166+
let writable = (|| -> Option<Writable<'a>> {
1167+
enum InputKind<'a> {
1168+
StrLit(&'a str),
1169+
CharLit(&'a str),
1170+
}
1171+
enum OutputKind {
1172+
Html,
1173+
Text,
1174+
}
1175+
1176+
let input = match &**s {
1177+
Expr::StrLit(input) => InputKind::StrLit(input),
1178+
Expr::CharLit(input) => InputKind::CharLit(input),
1179+
_ => return None,
1180+
};
1181+
let output = match self.input.escaper.strip_prefix(CRATE)? {
1182+
"::filters::Html" => OutputKind::Html,
1183+
"::filters::Text" => OutputKind::Text,
1184+
_ => return None,
1185+
};
1186+
let unescaped = match input {
1187+
InputKind::StrLit(input) => {
1188+
let input = format!(r#""{input}""#);
1189+
let input = input.parse().ok()?;
1190+
let input = syn::parse2::<syn::LitStr>(input).ok()?;
1191+
input.value()
1192+
}
1193+
InputKind::CharLit(input) => {
1194+
let input = format!(r#"'{input}'"#);
1195+
let input = input.parse().ok()?;
1196+
let input = syn::parse2::<syn::LitChar>(input).ok()?;
1197+
input.value().to_string()
1198+
}
1199+
};
1200+
match output {
1201+
OutputKind::Html => {
1202+
let mut escaped = String::with_capacity(unescaped.len() + 20);
1203+
write_escaped_str(&mut escaped, &unescaped).ok()?;
1204+
Some(Writable::Lit(Cow::Owned(escaped)))
1205+
}
1206+
OutputKind::Text => return Some(Writable::Lit(Cow::Owned(unescaped))),
1207+
}
1208+
})()
1209+
.unwrap_or(Writable::Expr(s));
1210+
11651211
self.handle_ws(ws);
1166-
self.buf_writable.push(Writable::Expr(s));
1212+
self.buf_writable.push(writable);
11671213
}
11681214

11691215
// Write expression buffer and empty

rinja_derive/src/tests.rs

+10-63
Original file line numberDiff line numberDiff line change
@@ -440,77 +440,24 @@ fn check_escaping_at_compile_time() {
440440
r#"writer.write_str("The card is")?;
441441
match &self.suit {
442442
Suit::Clubs | Suit::Spades => {
443-
match (
444-
&((&&::rinja::filters::AutoEscaper::new(&(" black"), ::rinja::filters::Text)).rinja_auto_escape()?),
445-
) {
446-
(expr0,) => {
447-
(&&::rinja::filters::Writable(expr0)).rinja_write(writer)?;
448-
}
449-
}
450-
}
451-
Suit::Diamonds | Suit::Hearts => {
452-
match (
453-
&((&&::rinja::filters::AutoEscaper::new(&(" red"), ::rinja::filters::Text)).rinja_auto_escape()?),
454-
) {
455-
(expr0,) => {
456-
(&&::rinja::filters::Writable(expr0)).rinja_write(writer)?;
457-
}
458-
}
443+
writer.write_str(" black")?;
444+
}
445+
Suit::Diamonds | Suit::Hearts => {
446+
writer.write_str(" red")?;
459447
}
460448
}"#,
461449
&[("suit", "Suit")],
462-
14,
450+
16,
463451
);
464452

465453
compare(
466454
r#"{{ '\x41' }}{{ '\n' }}{{ '\r' }}{{ '\t' }}{{ '\\' }}{{ '\u{2665}' }}{{ '\'' }}{{ '\"' }}{{ '"' }}
467455
{{ "\x41\n\r\t\\\u{2665}\'\"'" }}"#,
468-
r#"
469-
match (
470-
&((&&::rinja::filters::AutoEscaper::new(&('\x41'), ::rinja::filters::Text))
471-
.rinja_auto_escape()?),
472-
&((&&::rinja::filters::AutoEscaper::new(&('\n'), ::rinja::filters::Text))
473-
.rinja_auto_escape()?),
474-
&((&&::rinja::filters::AutoEscaper::new(&('\r'), ::rinja::filters::Text))
475-
.rinja_auto_escape()?),
476-
&((&&::rinja::filters::AutoEscaper::new(&('\t'), ::rinja::filters::Text))
477-
.rinja_auto_escape()?),
478-
&((&&::rinja::filters::AutoEscaper::new(&('\\'), ::rinja::filters::Text))
479-
.rinja_auto_escape()?),
480-
&((&&::rinja::filters::AutoEscaper::new(
481-
&('\u{2665}'),
482-
::rinja::filters::Text,
483-
))
484-
.rinja_auto_escape()?),
485-
&((&&::rinja::filters::AutoEscaper::new(&('\''), ::rinja::filters::Text))
486-
.rinja_auto_escape()?),
487-
&((&&::rinja::filters::AutoEscaper::new(&('\"'), ::rinja::filters::Text))
488-
.rinja_auto_escape()?),
489-
&((&&::rinja::filters::AutoEscaper::new(&('"'), ::rinja::filters::Text))
490-
.rinja_auto_escape()?),
491-
&((&&::rinja::filters::AutoEscaper::new(
492-
&("\x41\n\r\t\\\u{2665}\'\"'"),
493-
::rinja::filters::Text,
494-
))
495-
.rinja_auto_escape()?),
496-
) {
497-
(expr0, expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8, expr10) => {
498-
(&&::rinja::filters::Writable(expr0)).rinja_write(writer)?;
499-
(&&::rinja::filters::Writable(expr1)).rinja_write(writer)?;
500-
(&&::rinja::filters::Writable(expr2)).rinja_write(writer)?;
501-
(&&::rinja::filters::Writable(expr3)).rinja_write(writer)?;
502-
(&&::rinja::filters::Writable(expr4)).rinja_write(writer)?;
503-
(&&::rinja::filters::Writable(expr5)).rinja_write(writer)?;
504-
(&&::rinja::filters::Writable(expr6)).rinja_write(writer)?;
505-
(&&::rinja::filters::Writable(expr7)).rinja_write(writer)?;
506-
(&&::rinja::filters::Writable(expr8)).rinja_write(writer)?;
507-
writer.write_str("
508-
")?;
509-
(&&::rinja::filters::Writable(expr10)).rinja_write(writer)?;
510-
}
511-
}
512-
"#,
456+
r#"writer.write_str("A
457+
\r \\♥'\"\"
458+
A
459+
\r \\♥'\"'")?;"#,
513460
&[],
514-
31,
461+
23,
515462
);
516463
}

0 commit comments

Comments
 (0)