diff --git a/Cargo.toml b/Cargo.toml index b4a7b8e..5d423ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "tmpls/markup", "tmpls/minijinja", "tmpls/rinja", + "tmpls/rinja_git", "tmpls/ructe", "tmpls/sailfish", "tmpls/tera", @@ -24,7 +25,7 @@ license = "Apache-2.0" [features] default = ["compiled", "interpreted"] -compiled = ["askama", "horrorshow", "markup", "maud", "rinja", "ructe", "sailfish"] +compiled = ["askama", "horrorshow", "markup", "maud", "rinja", "rinja_git", "ructe", "sailfish"] interpreted = ["handlebars", "minijinja", "tera", "tinytemplate"] askama = ["dep:askama", "_contains_compiled"] @@ -34,6 +35,7 @@ markup = ["dep:markup", "_contains_compiled"] maud = ["dep:maud", "_contains_compiled"] minijinja = ["dep:minijinja", "_contains_interpreted"] rinja = ["dep:rinja", "_contains_compiled"] +rinja_git = ["dep:rinja_git", "_contains_compiled"] ructe = ["dep:ructe", "_contains_compiled"] sailfish = ["dep:sailfish", "_contains_compiled"] tera = ["dep:tera", "_contains_interpreted"] @@ -52,6 +54,7 @@ markup = { version = "*", optional = true, path = "tmpls/markup", package = "tmp maud = { version = "*", optional = true, path = "tmpls/maud", package = "tmpl-maud" } minijinja = { version = "*", optional = true, path = "tmpls/minijinja", package = "tmpl-minijinja" } rinja = { version = "*", optional = true, path = "tmpls/rinja", package = "tmpl-rinja" } +rinja_git = { version = "*", optional = true, path = "tmpls/rinja_git", package = "tmpl-rinja_git" } ructe = { version = "*", optional = true, path = "tmpls/ructe", package = "tmpl-ructe" } sailfish = { version = "*", optional = true, path = "tmpls/sailfish", package = "tmpl-sailfish" } tera = { version = "*", optional = true, path = "tmpls/tera", package = "tmpl-tera" } @@ -61,9 +64,8 @@ ahash = { version = "0.8.11", features = ["no-rng"] } criterion = { version = "0.5.1", features = ["html_reports"] } [build-dependencies] +self_cell = "1.0.4" pretty-error-debug = "0.3.0" -serde = { version = "1.0.207", features = ["derive"] } -serde_json = "1.0.124" thiserror = "1.0.63" [[bench]] diff --git a/build.rs b/build.rs index 07e6b9c..7625487 100644 --- a/build.rs +++ b/build.rs @@ -1,16 +1,64 @@ -use std::borrow::Cow; -use std::collections::HashMap; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::process::{Command, Stdio}; -use serde::Deserialize; -use serde_json::from_slice; - fn main() -> Result<(), Error> { println!("cargo:rerun-if-changed=Cargo.lock"); - let output = Command::new(var_os("CARGO")?) - .args(["metadata", "--locked", "--format-version=1"]) - .current_dir(var_os("CARGO_MANIFEST_DIR")?) + + let cargo = var_os("CARGO")?; + let root = var_os("CARGO_MANIFEST_DIR")?; + for &(benchmark_name, _) in &cargo_tree(&cargo, &root, "template-benchmark")? { + let Some(engine_name) = benchmark_name.strip_prefix("tmpl-") else { + continue; + }; + let bare_engine_name = engine_name.strip_suffix("_git").unwrap_or(engine_name); + + for &(name, info) in &cargo_tree(&cargo, &root, benchmark_name)? { + if name != bare_engine_name { + continue; + } + if let Some((version, hash)) = info + .split_once(' ') + .and_then(|(v, s)| Some((v, s.rsplit_once('#')?))) + .and_then(|(v, (_, s))| Some((v, s.strip_suffix(')')?))) + { + println!( + r#"cargo::rustc-env=VERSION_{p}={n} {v} (git-{h})"#, + p = engine_name, + n = name, + v = version, + h = hash, + ) + } else { + println!( + r#"cargo::rustc-env=VERSION_{p}={n} {v}"#, + p = engine_name, + n = name, + v = info, + ) + } + break; + } + } + Ok(()) +} + +fn var_os(key: &'static str) -> Result { + std::env::var_os(key).ok_or(Error::Var(key)) +} + +fn cargo_tree(cargo: &OsStr, root: &OsStr, package: &str) -> Result { + let output = Command::new(cargo) + .args([ + "tree", + "--locked", + "--edges=normal", + "--depth=1", + "--charset=ascii", + "--all-features", + "--package", + package, + ]) + .current_dir(root) .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::inherit()) @@ -22,89 +70,45 @@ fn main() -> Result<(), Error> { return Err(Error::Status(output.status)); } - let metadata: Metadata<'_> = from_slice(&output.stdout).map_err(Error::FromSlice)?; - let packages = metadata - .packages - .iter() - .map(|p| (&*p.name, p)) - .collect::>(); - for p in &metadata.packages { - if p.source.is_some() { - continue; - } - let Some(name_suffix) = p.name.strip_prefix("tmpl-") else { - continue; - }; - for dep in &p.dependencies { - let (dep_prefix, _) = dep.name.split_once('_').unwrap_or((&dep.name, "")); - if dep_prefix != name_suffix { - continue; - } - let Some(dep) = packages.get(&*dep.name) else { - break; - }; - let Some(source) = dep.source.as_deref() else { - break; - }; - match source - .strip_prefix("git+") - .and_then(|s| s.rsplit_once('#')?.1.get(..8)) - { - Some(shorthash) => println!( - r#"cargo::rustc-env=VERSION_{n}={n} v{v} (git-{h})"#, - n = name_suffix, - v = dep.version, - h = shorthash, - ), - None => println!( - r#"cargo::rustc-env=VERSION_{n}={n} v{v}"#, - n = name_suffix, - v = dep.version, - ), - } - } + Ok(CargoTreeOutput::new( + String::from_utf8(output.stdout).map_err(|_| Error::Utf8)?, + |s| { + s.lines() + .filter_map(|l| (l.get(1..4)? == "-- ").then(|| l.get(4..)?.split_once(' '))?) + .collect() + }, + )) +} + +self_cell::self_cell! { + struct CargoTreeOutput { + owner: String, + #[covariant] + dependent: CargoTreeOutputLines, + } +} + +impl<'a> IntoIterator for &'a CargoTreeOutput { + type Item = <&'a CargoTreeOutputLines<'a> as IntoIterator>::Item; + type IntoIter = <&'a CargoTreeOutputLines<'a> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.borrow_dependent().iter() } - Ok(()) } +type CargoTreeOutputLines<'a> = Vec<(&'a str, &'a str)>; + #[derive(thiserror::Error, pretty_error_debug::Debug)] enum Error { #[error("environment variable {:?} not found", 1)] Var(&'static str), - #[error("could not spawn `cargo metadata`")] + #[error("could not spawn `cargo`")] Spawn(#[source] std::io::Error), - #[error("could not wait for `cargo metadata`")] + #[error("could not wait for `cargo`")] Wait(#[source] std::io::Error), - #[error("subprocess `cargo metadata` exited with an error: {}", .0)] + #[error("`cargo` exited with an error: {}", .0)] Status(std::process::ExitStatus), - #[error("could not parse `cargo metadata`'s output")] - FromSlice(#[source] serde_json::Error), -} - -fn var_os(key: &'static str) -> Result { - std::env::var_os(key).ok_or(Error::Var(key)) -} - -#[derive(Debug, Deserialize)] -struct Metadata<'a> { - #[serde(borrow)] - packages: Vec>, -} - -#[derive(Debug, Deserialize)] -struct Package<'a> { - #[serde(borrow)] - name: Cow<'a, str>, - #[serde(borrow)] - version: Cow<'a, str>, - #[serde(borrow)] - source: Option>, - #[serde(borrow)] - dependencies: Vec>, -} - -#[derive(Debug, Deserialize)] -struct Dependency<'a> { - #[serde(borrow)] - name: Cow<'a, str>, + #[error("`cargo` returned non-UTF-8 data")] + Utf8, } diff --git a/results.svg b/results.svg deleted file mode 100644 index 15aa4bc..0000000 --- a/results.svg +++ /dev/null @@ -1,910 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/lib.rs b/src/lib.rs index 064a3d8..0c453f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,8 @@ macro_rules! for_each { for_each!(maud, group, $input:$Input, $func); #[cfg(feature = "rinja")] for_each!(rinja, group, $input:$Input, $func); + #[cfg(feature = "rinja_git")] + for_each!(rinja_git, group, $input:$Input, $func); #[cfg(feature = "ructe")] for_each!(ructe, group, $input:$Input, $func); #[cfg(feature = "sailfish")] diff --git a/tmpls/rinja_git/Cargo.toml b/tmpls/rinja_git/Cargo.toml new file mode 100644 index 0000000..e6b03ff --- /dev/null +++ b/tmpls/rinja_git/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "tmpl-rinja_git" +version = "0.1.0" +edition = "2021" +publish = false +license = "Apache-2.0" + +[dependencies] +tmpls = { version = "*", path = ".." } + +rinja = { version = "0.3.0", git = "https://github.com/rinja-rs/rinja", branch = "master" } diff --git a/tmpls/rinja_git/src/lib.rs b/tmpls/rinja_git/src/lib.rs new file mode 100644 index 0000000..6e7baa7 --- /dev/null +++ b/tmpls/rinja_git/src/lib.rs @@ -0,0 +1,50 @@ +use std::ops::Deref; + +use rinja::{Error, Template}; +use tmpls::{BigTable, Teams}; + +#[derive(Debug, Default)] +pub struct Benchmark; + +impl tmpls::Benchmark for Benchmark { + type Output = String; + type Error = Error; + + fn big_table( + &mut self, + output: &mut Self::Output, + input: &BigTable, + ) -> Result<(), Self::Error> { + #[derive(Template)] + #[template(path = "big-table.html")] + struct Tmpl<'a>(&'a BigTable); + + impl Deref for Tmpl<'_> { + type Target = BigTable; + + #[inline] + fn deref(&self) -> &Self::Target { + self.0 + } + } + + Tmpl(input).render_into(output) + } + + fn teams(&mut self, output: &mut Self::Output, input: &Teams) -> Result<(), Self::Error> { + #[derive(Template)] + #[template(path = "teams.html")] + struct Tmpl<'a>(&'a Teams); + + impl Deref for Tmpl<'_> { + type Target = Teams; + + #[inline] + fn deref(&self) -> &Self::Target { + self.0 + } + } + + Tmpl(input).render_into(output) + } +} diff --git a/tmpls/rinja_git/templates b/tmpls/rinja_git/templates new file mode 120000 index 0000000..8465398 --- /dev/null +++ b/tmpls/rinja_git/templates @@ -0,0 +1 @@ +../rinja/templates/ \ No newline at end of file