diff --git a/rinja/benches/to-json.rs b/rinja/benches/to-json.rs index bd8711ca..78f31908 100644 --- a/rinja/benches/to-json.rs +++ b/rinja/benches/to-json.rs @@ -1,5 +1,5 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use rinja::filters::{escape, json, json_pretty, Html}; +use rinja::Template; criterion_main!(benches); criterion_group!(benches, functions); @@ -12,34 +12,58 @@ fn functions(c: &mut Criterion) { } fn escape_json(b: &mut criterion::Bencher<'_>) { + #[derive(Template)] + #[template(ext = "html", source = "{{self.0|json|safe}}")] + struct Tmpl(&'static str); + b.iter(|| { + let mut len = 0; for &s in STRINGS { - format!("{}", json(s).unwrap()); + len += Tmpl(s).to_string().len(); } + len }); } fn escape_json_pretty(b: &mut criterion::Bencher<'_>) { + #[derive(Template)] + #[template(ext = "html", source = "{{self.0|json(2)|safe}}")] + struct Tmpl(&'static str); + b.iter(|| { + let mut len = 0; for &s in STRINGS { - format!("{}", json_pretty(s, 2).unwrap()); + len += Tmpl(s).to_string().len(); } + len }); } fn escape_json_for_html(b: &mut criterion::Bencher<'_>) { + #[derive(Template)] + #[template(ext = "html", source = "{{self.0|json}}")] + struct Tmpl(&'static str); + b.iter(|| { + let mut len = 0; for &s in STRINGS { - format!("{}", escape(json(s).unwrap(), Html).unwrap()); + len += Tmpl(s).to_string().len(); } + len }); } fn escape_json_for_html_pretty(b: &mut criterion::Bencher<'_>) { + #[derive(Template)] + #[template(ext = "html", source = "{{self.0|json(2)}}")] + struct Tmpl(&'static str); + b.iter(|| { + let mut len = 0; for &s in STRINGS { - format!("{}", escape(json_pretty(s, 2).unwrap(), Html).unwrap(),); + len += Tmpl(s).to_string().len(); } + len }); } diff --git a/rinja/src/filters/json.rs b/rinja/src/filters/json.rs index 14b3f40b..688b5fa6 100644 --- a/rinja/src/filters/json.rs +++ b/rinja/src/filters/json.rs @@ -4,6 +4,8 @@ use std::{fmt, io, str}; use serde::Serialize; use serde_json::ser::{to_writer, PrettyFormatter, Serializer}; +use super::FastWritable; + /// Serialize to JSON (requires `json` feature) /// /// The generated string does not contain ampersands `&`, chevrons `< >`, or apostrophes `'`. @@ -119,26 +121,50 @@ impl AsIndent for std::sync::Arc { } } +impl FastWritable for ToJson { + #[inline] + fn write_into(&self, f: &mut W) -> fmt::Result { + fmt_json(f, &self.value) + } +} + impl fmt::Display for ToJson { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - to_writer(JsonWriter(f), &self.value).map_err(|_| fmt::Error) + fmt_json(f, &self.value) + } +} + +impl FastWritable for ToJsonPretty { + #[inline] + fn write_into(&self, f: &mut W) -> fmt::Result { + fmt_json_pretty(f, &self.value, self.indent.as_indent()) } } impl fmt::Display for ToJsonPretty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let indent = self.indent.as_indent(); - let formatter = PrettyFormatter::with_indent(indent.as_bytes()); - let mut serializer = Serializer::with_formatter(JsonWriter(f), formatter); - self.value - .serialize(&mut serializer) - .map_err(|_| fmt::Error) + fmt_json_pretty(f, &self.value, self.indent.as_indent()) } } -struct JsonWriter<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>); +fn fmt_json(dest: &mut W, value: &S) -> fmt::Result { + to_writer(JsonWriter(dest), value).map_err(|_| fmt::Error) +} + +fn fmt_json_pretty( + dest: &mut W, + value: &S, + indent: &str, +) -> fmt::Result { + let formatter = PrettyFormatter::with_indent(indent.as_bytes()); + let mut serializer = Serializer::with_formatter(JsonWriter(dest), formatter); + value.serialize(&mut serializer).map_err(|_| fmt::Error) +} + +struct JsonWriter<'a, W: fmt::Write + ?Sized>(&'a mut W); -impl io::Write for JsonWriter<'_, '_> { +impl io::Write for JsonWriter<'_, W> { #[inline] fn write(&mut self, bytes: &[u8]) -> io::Result { self.write_all(bytes)?; @@ -156,7 +182,7 @@ impl io::Write for JsonWriter<'_, '_> { } } -fn write(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result { +fn write(f: &mut W, bytes: &[u8]) -> fmt::Result { let mut last = 0; for (index, byte) in bytes.iter().enumerate() { let escaped = match byte {