From 98cb4233e2869b7c079039c8500781da71204ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Sun, 9 Feb 2025 03:32:03 +0100 Subject: [PATCH] Make `derive` optional This is the same as in e.g. `serde` with the feature of the same name. If you manually depend on `rinja_derive`, then you can make better use of your multi-core setup. --- .github/workflows/rust.yml | 6 +-- bench-build/.rustfmt.toml | 1 + bench-build/Cargo.toml | 18 +++++++++ bench-build/LICENSE-APACHE | 1 + bench-build/LICENSE-MIT | 1 + bench-build/README.md | 55 ++++++++++++++++++++++++++ bench-build/_typos.toml | 1 + bench-build/clippy.toml | 1 + bench-build/deny.toml | 1 + bench-build/run.sh | 20 ++++++++++ bench-build/src/main.rs | 47 ++++++++++++++++++++++ bench-build/templates/greeting.html | 11 ++++++ bench-build/templates/hello_world.html | 1 + bench-build/tomlfmt.toml | 1 + book/src/features.md | 20 +++++++++- rinja/Cargo.toml | 28 +++++++------ rinja/src/lib.rs | 5 ++- rinja_derive/Cargo.toml | 7 +++- rinja_derive_standalone/Cargo.toml | 5 ++- testing-alloc/Cargo.toml | 2 +- testing-no-std/Cargo.toml | 2 +- testing-renamed/Cargo.toml | 2 +- 22 files changed, 212 insertions(+), 24 deletions(-) create mode 120000 bench-build/.rustfmt.toml create mode 100644 bench-build/Cargo.toml create mode 120000 bench-build/LICENSE-APACHE create mode 120000 bench-build/LICENSE-MIT create mode 100644 bench-build/README.md create mode 120000 bench-build/_typos.toml create mode 120000 bench-build/clippy.toml create mode 120000 bench-build/deny.toml create mode 100755 bench-build/run.sh create mode 100644 bench-build/src/main.rs create mode 100644 bench-build/templates/greeting.html create mode 100644 bench-build/templates/hello_world.html create mode 120000 bench-build/tomlfmt.toml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 627a75308..21b4e686c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,7 +26,7 @@ jobs: - run: | set -eu for PKG in \ - examples/actix-web-app examples/axum-app examples/poem-app examples/rocket-app examples/salvo-app examples/warp-app fuzzing \ + bench-build examples/actix-web-app examples/axum-app examples/poem-app examples/rocket-app examples/salvo-app examples/warp-app fuzzing \ rinja rinja_derive rinja_derive_standalone rinja_parser \ testing testing-alloc testing-no-std testing-renamed do @@ -116,7 +116,7 @@ jobs: cargo sort --check --check-format --grouped set -eu for PKG in \ - examples/actix-web-app examples/axum-app examples/poem-app examples/rocket-app examples/salvo-app examples/warp-app fuzzing \ + bench-build examples/actix-web-app examples/axum-app examples/poem-app examples/rocket-app examples/salvo-app examples/warp-app fuzzing \ rinja rinja_derive rinja_derive_standalone rinja_parser \ testing testing-alloc testing-no-std testing-renamed do @@ -159,7 +159,7 @@ jobs: strategy: matrix: package: [ - examples/actix-web-app, examples/axum-app, examples/poem-app, examples/rocket-app, examples/salvo-app, examples/warp-app, fuzzing, + bench-build, examples/actix-web-app, examples/axum-app, examples/poem-app, examples/rocket-app, examples/salvo-app, examples/warp-app, fuzzing, rinja, rinja_derive, rinja_derive_standalone, rinja_parser, testing, testing-alloc, testing-no-std, testing-renamed, ] diff --git a/bench-build/.rustfmt.toml b/bench-build/.rustfmt.toml new file mode 120000 index 000000000..a7ef950cc --- /dev/null +++ b/bench-build/.rustfmt.toml @@ -0,0 +1 @@ +../.rustfmt.toml \ No newline at end of file diff --git a/bench-build/Cargo.toml b/bench-build/Cargo.toml new file mode 100644 index 000000000..f0562b8e8 --- /dev/null +++ b/bench-build/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "bench-build" +version = "0.3.5" +authors = ["rinja-rs developers"] +edition = "2021" +rust-version = "1.81" +publish = false + +[dependencies] +rinja = { path = "../rinja", version = "0.3.5", default-features = false, features = ["std"] } +rinja_derive = { path = "../rinja_derive", version = "0.3.5", features = ["std"] } + +[features] +default = [] +derive = ["rinja/derive"] + +[workspace] +members = ["."] diff --git a/bench-build/LICENSE-APACHE b/bench-build/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/bench-build/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/bench-build/LICENSE-MIT b/bench-build/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/bench-build/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/bench-build/README.md b/bench-build/README.md new file mode 100644 index 000000000..1cab79353 --- /dev/null +++ b/bench-build/README.md @@ -0,0 +1,55 @@ +Run the script `./run.sh` in this directory to compare the compile compile of `rinja` + +* uses feature `derive` vs +* it does not use that feature. + +The output might look like: + +```text +Benchmark 1: cargo run --features=derive + Time (mean ± σ): 3.378 s ± 0.041 s [User: 7.944 s, System: 1.018 s] + Range (min … max): 3.345 s … 3.424 s 3 runs + +Benchmark 2: cargo run + Time (mean ± σ): 3.283 s ± 0.130 s [User: 8.400 s, System: 1.091 s] + Range (min … max): 3.141 s … 3.398 s 3 runs + +Summary + cargo run ran + 1.03 ± 0.04 times faster than cargo run --features=derive + +---------- + +Benchmark 1: cargo run --release --features=derive + Time (mean ± σ): 4.733 s ± 0.050 s [User: 9.026 s, System: 0.749 s] + Range (min … max): 4.689 s … 4.788 s 3 runs + +Benchmark 2: cargo run --release + Time (mean ± σ): 4.504 s ± 0.032 s [User: 9.010 s, System: 0.733 s] + Range (min … max): 4.481 s … 4.541 s 3 runs + +Summary + cargo run --release ran + 1.05 ± 0.01 times faster than cargo run --release --features=derive +``` + +This shows that – while it is less convenient – for small projects it might be better +to use the following setup. +This might be especially true if you are using `rinja` in a library. +Without the feature, `cargo` will be able to compile more dependencies in parallel. + +```toml +# Cargo.toml +[dependencies] +rinja = { version = "0.3.5", default-features = false, features = ["std"] } +rinja_derive = { version = "0.3.5", features = ["std"] } +``` + +```rust +// lib.rs +use rinja::Template as _; +use rinja_derive::Template; +``` + +The script uses [hyperfine](https://crates.io/crates/hyperfine). +Install it with `cargo install hyperfine`. diff --git a/bench-build/_typos.toml b/bench-build/_typos.toml new file mode 120000 index 000000000..2264b5db8 --- /dev/null +++ b/bench-build/_typos.toml @@ -0,0 +1 @@ +../_typos.toml \ No newline at end of file diff --git a/bench-build/clippy.toml b/bench-build/clippy.toml new file mode 120000 index 000000000..85f6167cb --- /dev/null +++ b/bench-build/clippy.toml @@ -0,0 +1 @@ +../clippy.toml \ No newline at end of file diff --git a/bench-build/deny.toml b/bench-build/deny.toml new file mode 120000 index 000000000..a65e17bb8 --- /dev/null +++ b/bench-build/deny.toml @@ -0,0 +1 @@ +../deny.toml \ No newline at end of file diff --git a/bench-build/run.sh b/bench-build/run.sh new file mode 100755 index 000000000..4bea04c6b --- /dev/null +++ b/bench-build/run.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")" + +mkdir -p target +hyperfine \ + --runs=3 \ + --warmup=1 \ + --prepare='rm -r target' \ + 'cargo run --features=derive' \ + 'cargo run' +echo +echo ---------- +echo +hyperfine \ + --runs=3 \ + --warmup=1 \ + --prepare='rm -r target' \ + 'cargo run --release --features=derive' \ + 'cargo run --release' diff --git a/bench-build/src/main.rs b/bench-build/src/main.rs new file mode 100644 index 000000000..8fa218a37 --- /dev/null +++ b/bench-build/src/main.rs @@ -0,0 +1,47 @@ +use std::str::FromStr; + +use rinja::Template as _; +use rinja_derive::Template; + +fn main() { + let mut args = std::env::args().fuse().skip(1); + let greeting = args.next(); + let user = args.next(); + + let tmpl = HelloWorld { + greeting: greeting.as_deref().unwrap_or("hi").parse().unwrap(), + user: user.as_deref().unwrap_or("user"), + }; + println!("{}", tmpl.render().unwrap()); +} + +#[derive(Debug, Clone, Copy, Template)] +#[template(path = "hello_world.html")] +struct HelloWorld<'a> { + greeting: Greeting, + user: &'a str, +} + +#[derive(Debug, Clone, Copy, Template)] +#[template(path = "greeting.html")] +enum Greeting { + #[template(block = "hello")] + Hello, + #[template(block = "hey")] + Hey, + #[template(block = "hi")] + Hi, +} + +impl FromStr for Greeting { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "hello" => Ok(Self::Hello), + "hey" => Ok(Self::Hey), + "hi" => Ok(Self::Hi), + _ => Err("Valid greetings: "), + } + } +} diff --git a/bench-build/templates/greeting.html b/bench-build/templates/greeting.html new file mode 100644 index 000000000..99b20e33e --- /dev/null +++ b/bench-build/templates/greeting.html @@ -0,0 +1,11 @@ +{%- block hello -%} + Hello +{%- endblock -%} + +{%- block hey -%} + Hey +{%- endblock -%} + +{%- block hi -%} + Hi +{%- endblock -%} diff --git a/bench-build/templates/hello_world.html b/bench-build/templates/hello_world.html new file mode 100644 index 000000000..7e5c752d8 --- /dev/null +++ b/bench-build/templates/hello_world.html @@ -0,0 +1 @@ +

{{greeting}}, {{user}}!

diff --git a/bench-build/tomlfmt.toml b/bench-build/tomlfmt.toml new file mode 120000 index 000000000..053191ccf --- /dev/null +++ b/bench-build/tomlfmt.toml @@ -0,0 +1 @@ +../tomlfmt.toml \ No newline at end of file diff --git a/book/src/features.md b/book/src/features.md index e6508b9c9..eae5f1566 100644 --- a/book/src/features.md +++ b/book/src/features.md @@ -35,7 +35,7 @@ Without `default-features = false`, i.e with default features enabled, the following features are automatically selected for you: ```toml -default = ["config", "std", "urlencode"] +default = ["config", "derive", "std", "urlencode"] ``` This should encompass most features an average user of rinja might need. @@ -44,6 +44,24 @@ This should encompass most features an average user of rinja might need. and if you want it to be usable in by other users and in **other projects**, then you should probably **opt-out of features you do not need**.* +### `"derive"` + +
+enabled by "default" +
+ +This feature enables `#[derive(Template)]`. Without it the trait `rinja::Template` will still be +available, but if you want to derive a template, you have to manually depend on `rinja_derive`. +`rinja_derive` should be used with the same features as `rinja`. + +Not using this feature might be useful e.g. if you are writing a library with manual filters +for rinja, without any templates. It might also very slightly speed-up the compilation, +because more dependencies can be compiled in parallel, because `rinja` won't transitively depend +on e.g. `syn` or `proc-macro2`. On the author's PC the compilation of a trivial hello-world example +was about 0.2s faster without the feature when compiled in release mode. + +*If you are writing a library that uses rinja, consider **not using** this default-feature.* + ### `"config"`
diff --git a/rinja/Cargo.toml b/rinja/Cargo.toml index 5fa70f0d3..d061508c7 100644 --- a/rinja/Cargo.toml +++ b/rinja/Cargo.toml @@ -24,10 +24,11 @@ name = "escape" harness = false [dependencies] -rinja_derive = { version = "=0.3.5", path = "../rinja_derive" } - itoa = "1.0.11" +# needed by feature "derive" +rinja_derive = { version = "=0.3.5", path = "../rinja_derive", default-features = false, optional = true } + # needed by feature "serde_json" serde = { version = "1.0", optional = true, default-features = false } serde_json = { version = "1.0", optional = true, default-features = false } @@ -43,24 +44,25 @@ criterion = "0.5" maintenance = { status = "actively-developed" } [features] -default = ["config", "std", "urlencode"] -full = ["default", "blocks", "code-in-doc", "serde_json"] +default = ["config", "derive", "std", "urlencode", "rinja_derive?/default"] +full = ["default", "blocks", "code-in-doc", "serde_json", "rinja_derive?/full"] alloc = [ - "rinja_derive/alloc", + "rinja_derive?/alloc", "serde?/alloc", "serde_json?/alloc", - "percent-encoding?/alloc" + "percent-encoding?/alloc", ] -blocks = ["rinja_derive/blocks"] -code-in-doc = ["rinja_derive/code-in-doc"] -config = ["rinja_derive/config"] -serde_json = ["rinja_derive/serde_json", "dep:serde", "dep:serde_json"] +blocks = ["rinja_derive?/blocks"] +code-in-doc = ["rinja_derive?/code-in-doc"] +config = ["rinja_derive?/config"] +derive = ["rinja_derive"] +serde_json = ["rinja_derive?/serde_json", "dep:serde", "dep:serde_json"] std = [ "alloc", - "rinja_derive/std", + "rinja_derive?/std", "serde?/std", "serde_json?/std", - "percent-encoding?/std" + "percent-encoding?/std", ] -urlencode = ["rinja_derive/urlencode", "dep:percent-encoding"] +urlencode = ["rinja_derive?/urlencode", "dep:percent-encoding"] diff --git a/rinja/src/lib.rs b/rinja/src/lib.rs index eecca307c..c04c98cd5 100644 --- a/rinja/src/lib.rs +++ b/rinja/src/lib.rs @@ -34,8 +34,8 @@ //! } //! //! assert_eq!( -//! Footer { year: 2024, enterprise: "Rinja developers" }.to_string(), -//! "

© 2024 <EM>RINJA</EM> DEVELOPERS

", +//! Footer { year: 2025, enterprise: "Rinja developers" }.to_string(), +//! "

© 2025 <EM>RINJA</EM> DEVELOPERS

", //! ); //! // In here you see can Rinja's auto-escaping. You, the developer, //! // can easily disable the auto-escaping with the `|safe` filter, @@ -78,6 +78,7 @@ use core::fmt; #[cfg(feature = "std")] use std::io; +#[cfg(feature = "derive")] pub use rinja_derive::Template; #[doc(hidden)] diff --git a/rinja_derive/Cargo.toml b/rinja_derive/Cargo.toml index ea00ee7d2..1e88412f8 100644 --- a/rinja_derive/Cargo.toml +++ b/rinja_derive/Cargo.toml @@ -38,14 +38,19 @@ prettyplease = "0.2.20" similar = "2.6.0" syn = { version = "2.0.3", features = ["full"] } +# must be the same feature list as for rinja [features] +default = ["config", "derive", "std", "urlencode"] +full = ["default", "blocks", "code-in-doc", "serde_json"] + alloc = [] blocks = ["syn/full"] code-in-doc = ["dep:pulldown-cmark"] config = ["dep:basic-toml", "dep:serde", "dep:serde_derive", "parser/config"] -urlencode = [] +derive = [] serde_json = [] std = ["alloc"] +urlencode = [] [lints.rust] # Used in `rinja_derive_standalone` which uses the same source folder, but is not a proc-macro. diff --git a/rinja_derive_standalone/Cargo.toml b/rinja_derive_standalone/Cargo.toml index 5f1e9e93e..42af01190 100644 --- a/rinja_derive_standalone/Cargo.toml +++ b/rinja_derive_standalone/Cargo.toml @@ -47,11 +47,14 @@ syn = { version = "2.0.3", features = ["full"] } default = ["__standalone"] __standalone = [] +alloc = [] blocks = ["syn/full"] code-in-doc = ["dep:pulldown-cmark"] config = ["dep:basic-toml", "dep:serde", "dep:serde_derive", "parser/config"] -urlencode = [] +derive = [] serde_json = [] +std = ["alloc"] +urlencode = [] [lints.rust] # Used in `rinja_derive` which uses the same source folder, but is a proc-macro. diff --git a/testing-alloc/Cargo.toml b/testing-alloc/Cargo.toml index 11f170903..b3ef449a3 100644 --- a/testing-alloc/Cargo.toml +++ b/testing-alloc/Cargo.toml @@ -7,6 +7,6 @@ rust-version = "1.81" publish = false [dev-dependencies] -rinja = { path = "../rinja", version = "0.3.5", default-features = false, features = ["alloc"] } +rinja = { path = "../rinja", version = "0.3.5", default-features = false, features = ["alloc", "derive"] } assert_matches = "1.5.0" diff --git a/testing-no-std/Cargo.toml b/testing-no-std/Cargo.toml index 90626f6da..d79e30e4e 100644 --- a/testing-no-std/Cargo.toml +++ b/testing-no-std/Cargo.toml @@ -7,6 +7,6 @@ rust-version = "1.81" publish = false [dev-dependencies] -rinja = { path = "../rinja", version = "0.3.5", default-features = false } +rinja = { path = "../rinja", version = "0.3.5", default-features = false, features = ["derive"] } assert_matches = "1.5.0" diff --git a/testing-renamed/Cargo.toml b/testing-renamed/Cargo.toml index 7ad2c2346..7b9a2722e 100644 --- a/testing-renamed/Cargo.toml +++ b/testing-renamed/Cargo.toml @@ -7,6 +7,6 @@ rust-version = "1.81" publish = false [dev-dependencies] -some_name = { package = "rinja", path = "../rinja", version = "0.3.5", default-features = false } +some_name = { package = "rinja", path = "../rinja", version = "0.3.5", default-features = false, features = ["derive"] } assert_matches = "1.5.0"