Bug 1964493 - Upgrade to UniFFI 0.29.2, r=markh,supply-chain-reviewers
The eventual goal is to enable using the new IR pipeline code, but this commit simply switches over to the new version. The main change is that UniFFI is back to using askama as the template engine, since rinja project has been merged back in. Differential Revision: https://phabricator.services.mozilla.com/D247843
This commit is contained in:
committed by
bdeankawamura@mozilla.com
parent
1beaa4e22e
commit
27ff199fb4
159
Cargo.lock
generated
159
Cargo.lock
generated
@@ -183,6 +183,48 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7"
|
||||||
|
dependencies = [
|
||||||
|
"askama_derive",
|
||||||
|
"itoa",
|
||||||
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama_derive"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac"
|
||||||
|
dependencies = [
|
||||||
|
"askama_parser",
|
||||||
|
"basic-toml",
|
||||||
|
"memchr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustc-hash 2.1.1",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama_parser"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-task"
|
name = "async-task"
|
||||||
version = "4.3.0"
|
version = "4.3.0"
|
||||||
@@ -678,16 +720,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cargo_metadata"
|
name = "cargo_metadata"
|
||||||
version = "0.15.3"
|
version = "0.19.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07"
|
checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"camino",
|
"camino",
|
||||||
"cargo-platform",
|
"cargo-platform",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 1.999.999",
|
"thiserror 2.0.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5617,45 +5659,6 @@ dependencies = [
|
|||||||
"cache-padded",
|
"cache-padded",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rinja"
|
|
||||||
version = "0.3.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5"
|
|
||||||
dependencies = [
|
|
||||||
"itoa",
|
|
||||||
"rinja_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rinja_derive"
|
|
||||||
version = "0.3.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b"
|
|
||||||
dependencies = [
|
|
||||||
"basic-toml",
|
|
||||||
"memchr",
|
|
||||||
"mime",
|
|
||||||
"mime_guess",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rinja_parser",
|
|
||||||
"rustc-hash 2.1.1",
|
|
||||||
"serde",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rinja_parser"
|
|
||||||
version = "0.3.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
"nom",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rkv"
|
name = "rkv"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
@@ -6948,9 +6951,9 @@ checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi"
|
name = "uniffi"
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe34585ac0275accf6c284d0080cc2840f3898c551cda869ec291b5a4218712c"
|
checksum = "4dcd1d240101ba3b9d7532ae86d9cb64d9a7ff63e13a2b7b9e94a32a601d8233"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
@@ -6958,6 +6961,7 @@ dependencies = [
|
|||||||
"uniffi_build",
|
"uniffi_build",
|
||||||
"uniffi_core",
|
"uniffi_core",
|
||||||
"uniffi_macros",
|
"uniffi_macros",
|
||||||
|
"uniffi_pipeline",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6965,12 +6969,12 @@ name = "uniffi-bindgen-gecko-js"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"askama",
|
||||||
"camino",
|
"camino",
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
"clap",
|
"clap",
|
||||||
"extend",
|
"extend",
|
||||||
"heck",
|
"heck",
|
||||||
"rinja",
|
|
||||||
"serde",
|
"serde",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"toml",
|
"toml",
|
||||||
@@ -7070,33 +7074,36 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_bindgen"
|
name = "uniffi_bindgen"
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a792af1424cc8b3c43b44c1a6cb7935ed1fbe5584a74f70e8bab9799740266d"
|
checksum = "6d0525f06d749ea80d8049dc0bb038bb87941e3d909eefa76b6f0a5589b59ac5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"askama",
|
||||||
"camino",
|
"camino",
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"glob",
|
"glob",
|
||||||
"goblin 0.8.999",
|
"goblin 0.8.999",
|
||||||
"heck",
|
"heck",
|
||||||
|
"indexmap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"paste",
|
|
||||||
"rinja",
|
|
||||||
"serde",
|
"serde",
|
||||||
|
"tempfile",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"toml",
|
"toml",
|
||||||
|
"uniffi_internal_macros",
|
||||||
"uniffi_meta",
|
"uniffi_meta",
|
||||||
|
"uniffi_pipeline",
|
||||||
"uniffi_testing",
|
"uniffi_testing",
|
||||||
"uniffi_udl",
|
"uniffi_udl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_build"
|
name = "uniffi_build"
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00c4138211f2ae951018fcce6a978e1fcd1a47c3fd0bc0d5472a520520060db1"
|
checksum = "aed2f0204e942bb9c11c9f11a323b4abf70cf11b2e5957d60b3f2728430f6c6f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"camino",
|
"camino",
|
||||||
@@ -7105,9 +7112,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_core"
|
name = "uniffi_core"
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c18baace68a52666d33d12d73ca335ecf27a302202cefb53b1f974512bb72417"
|
checksum = "c3fa8eb4d825b4ed095cb13483cba6927c3002b9eb603cef9b7688758cc3772e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -7117,19 +7124,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_internal_macros"
|
name = "uniffi_internal_macros"
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f9902d4ed16c65e6c0222241024dd0bfeed07ea3deb7c470eb175e5f5ef406cd"
|
checksum = "83b547d69d699e52f2129fde4b57ae0d00b5216e59ed5b56097c95c86ba06095"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"indexmap",
|
||||||
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_macros"
|
name = "uniffi_macros"
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d82c82ef945c51082d8763635334b994e63e77650f09d0fae6d28dd08b1de83"
|
checksum = "00f1de72edc8cb9201c7d650e3678840d143e4499004571aac49e6cb1b17da43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"camino",
|
"camino",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
@@ -7144,20 +7154,34 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_meta"
|
name = "uniffi_meta"
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d6027b971c2aa86350dd180aee9819729c7b99bacd381534511ff29d2c09cea"
|
checksum = "3acc9204632f6a555b2cba7c8852c5523bc1aa5f3eff605c64af5054ea28b72e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"siphasher",
|
"siphasher",
|
||||||
"uniffi_internal_macros",
|
"uniffi_internal_macros",
|
||||||
|
"uniffi_pipeline",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uniffi_pipeline"
|
||||||
|
version = "0.29.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54b5336a9a925b358183837d31541d12590b7fcec373256d3770de02dff24c69"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"indexmap",
|
||||||
|
"tempfile",
|
||||||
|
"uniffi_internal_macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_testing"
|
name = "uniffi_testing"
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6301bcb50098dabcd304485318ba73f0f4db5e5d9d3c385c60b967810344ce90"
|
checksum = "69d997638de55f9e7011549a950be85db4f0a7e050b3aaa2922ccde12f237dd4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"camino",
|
"camino",
|
||||||
@@ -7168,9 +7192,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_udl"
|
name = "uniffi_udl"
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52300b7a4ab02dc159a038a13d5bfe27aefbad300d91b0b501b3dda094c1e0a2"
|
checksum = "f95e73373d85f04736bc51997d3e6855721144ec4384cae9ca8513c80615e129"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
@@ -7791,6 +7815,15 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ rust-version = "1.82.0"
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
# Shared across multiple UniFFI consumers.
|
# Shared across multiple UniFFI consumers.
|
||||||
uniffi = "0.29.1"
|
uniffi = "0.29.2"
|
||||||
uniffi_bindgen = "0.29.1"
|
uniffi_bindgen = "0.29.2"
|
||||||
# Shared across multiple application-services consumers.
|
# Shared across multiple application-services consumers.
|
||||||
rusqlite = "0.33.0"
|
rusqlite = "0.33.0"
|
||||||
# Shared across multiple glean consumers.
|
# Shared across multiple glean consumers.
|
||||||
|
|||||||
@@ -542,7 +542,7 @@ notes = "Maintained by the Glean and Application Services teams"
|
|||||||
[[wildcard-audits.uniffi_internal_macros]]
|
[[wildcard-audits.uniffi_internal_macros]]
|
||||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
user-id = 111105 # Mark Hammond (mhammond)
|
user-id = 111105
|
||||||
start = "2025-03-18"
|
start = "2025-03-18"
|
||||||
end = "2026-03-25"
|
end = "2026-03-25"
|
||||||
|
|
||||||
@@ -578,6 +578,14 @@ start = "2022-08-31"
|
|||||||
end = "2026-02-01"
|
end = "2026-02-01"
|
||||||
notes = "Maintained by the Glean and Application Services teams"
|
notes = "Maintained by the Glean and Application Services teams"
|
||||||
|
|
||||||
|
[[wildcard-audits.uniffi_pipeline]]
|
||||||
|
who = "Ben Dean-Kawamura <bdk@mozilla.com>"
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
user-id = 127697 # bendk
|
||||||
|
start = "2021-10-27"
|
||||||
|
end = "2026-02-01"
|
||||||
|
notes = "Maintained by the Glean and Application Services teams"
|
||||||
|
|
||||||
[[wildcard-audits.uniffi_testing]]
|
[[wildcard-audits.uniffi_testing]]
|
||||||
who = "Ben Dean-Kawamura <bdk@mozilla.com>"
|
who = "Ben Dean-Kawamura <bdk@mozilla.com>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
@@ -846,6 +854,46 @@ Just contains some traits and re-exports for use by a broader package of related
|
|||||||
crates. No unsafe code or ambient capability usage.
|
crates. No unsafe code or ambient capability usage.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
[[audits.askama]]
|
||||||
|
who = "Ben Dean-Kawamura <bdk@mozilla.com>"
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
version = "0.13.1"
|
||||||
|
notes = """
|
||||||
|
Template crate. This is only used to generate the Rust/JS code for UniFFI.
|
||||||
|
|
||||||
|
We used to use askama, then we switched to rinja which was a fork. Now rinja and
|
||||||
|
askama have merged again.
|
||||||
|
|
||||||
|
The differences from askama 0.12, are pretty straightforward and don't seem risky to me. There's
|
||||||
|
some unsafe code and macros, but nothing that complicated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[audits.askama_derive]]
|
||||||
|
who = "Ben Dean-Kawamura <bdk@mozilla.com>"
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
version = "0.13.1"
|
||||||
|
notes = """
|
||||||
|
Template crate. This is only used to generate the Rust/JS code for UniFFI.
|
||||||
|
|
||||||
|
We used to use askama, then we switched to rinja which was a fork. Now rinja and
|
||||||
|
askama have merged again.
|
||||||
|
|
||||||
|
I did a quick scan of the current code and couldn't find any issues.
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[audits.askama_parser]]
|
||||||
|
who = "Ben Dean-Kawamura <bdk@mozilla.com>"
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
version = "0.13.0"
|
||||||
|
notes = """
|
||||||
|
Template crate. This is only used to generate the Rust/JS code for UniFFI.
|
||||||
|
|
||||||
|
We used to use askama, then we switched to rinja which was a fork. Now rinja and
|
||||||
|
askama have merged again.
|
||||||
|
|
||||||
|
I did a quick scan of the current code and couldn't find any issues.
|
||||||
|
"""
|
||||||
|
|
||||||
[[audits.async-task]]
|
[[audits.async-task]]
|
||||||
who = "Nika Layzell <nika@thelayzells.com>"
|
who = "Nika Layzell <nika@thelayzells.com>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
@@ -6971,6 +7019,12 @@ user-id = 64539 # Kenny Kerr (kennykerr)
|
|||||||
start = "2021-11-15"
|
start = "2021-11-15"
|
||||||
end = "2024-09-12"
|
end = "2024-09-12"
|
||||||
|
|
||||||
|
[[trusted.winnow]]
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
user-id = 6743 # Ed Page (epage)
|
||||||
|
start = "2023-02-22"
|
||||||
|
end = "2026-05-05"
|
||||||
|
|
||||||
[[trusted.zlib-rs]]
|
[[trusted.zlib-rs]]
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
user-id = 1303
|
user-id = 1303
|
||||||
|
|||||||
@@ -704,67 +704,64 @@ user-login = "Manishearth"
|
|||||||
user-name = "Manish Goregaokar"
|
user-name = "Manish Goregaokar"
|
||||||
|
|
||||||
[[publisher.uniffi]]
|
[[publisher.uniffi]]
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
when = "2025-03-18"
|
when = "2025-05-05"
|
||||||
user-id = 111105
|
user-id = 127697
|
||||||
user-login = "mhammond"
|
user-login = "bendk"
|
||||||
user-name = "Mark Hammond"
|
|
||||||
|
|
||||||
[[publisher.uniffi_bindgen]]
|
[[publisher.uniffi_bindgen]]
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
when = "2025-03-18"
|
when = "2025-05-05"
|
||||||
user-id = 111105
|
user-id = 127697
|
||||||
user-login = "mhammond"
|
user-login = "bendk"
|
||||||
user-name = "Mark Hammond"
|
|
||||||
|
|
||||||
[[publisher.uniffi_build]]
|
[[publisher.uniffi_build]]
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
when = "2025-03-18"
|
when = "2025-05-05"
|
||||||
user-id = 111105
|
user-id = 127697
|
||||||
user-login = "mhammond"
|
user-login = "bendk"
|
||||||
user-name = "Mark Hammond"
|
|
||||||
|
|
||||||
[[publisher.uniffi_core]]
|
[[publisher.uniffi_core]]
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
when = "2025-03-18"
|
when = "2025-05-05"
|
||||||
user-id = 111105
|
user-id = 127697
|
||||||
user-login = "mhammond"
|
user-login = "bendk"
|
||||||
user-name = "Mark Hammond"
|
|
||||||
|
|
||||||
[[publisher.uniffi_internal_macros]]
|
[[publisher.uniffi_internal_macros]]
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
when = "2025-03-18"
|
when = "2025-05-05"
|
||||||
user-id = 111105
|
user-id = 127697
|
||||||
user-login = "mhammond"
|
user-login = "bendk"
|
||||||
user-name = "Mark Hammond"
|
|
||||||
|
|
||||||
[[publisher.uniffi_macros]]
|
[[publisher.uniffi_macros]]
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
when = "2025-03-18"
|
when = "2025-05-05"
|
||||||
user-id = 111105
|
user-id = 127697
|
||||||
user-login = "mhammond"
|
user-login = "bendk"
|
||||||
user-name = "Mark Hammond"
|
|
||||||
|
|
||||||
[[publisher.uniffi_meta]]
|
[[publisher.uniffi_meta]]
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
when = "2025-03-18"
|
when = "2025-05-05"
|
||||||
user-id = 111105
|
user-id = 127697
|
||||||
user-login = "mhammond"
|
user-login = "bendk"
|
||||||
user-name = "Mark Hammond"
|
|
||||||
|
[[publisher.uniffi_pipeline]]
|
||||||
|
version = "0.29.2"
|
||||||
|
when = "2025-05-05"
|
||||||
|
user-id = 127697
|
||||||
|
user-login = "bendk"
|
||||||
|
|
||||||
[[publisher.uniffi_testing]]
|
[[publisher.uniffi_testing]]
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
when = "2025-03-18"
|
when = "2025-05-05"
|
||||||
user-id = 111105
|
user-id = 127697
|
||||||
user-login = "mhammond"
|
user-login = "bendk"
|
||||||
user-name = "Mark Hammond"
|
|
||||||
|
|
||||||
[[publisher.uniffi_udl]]
|
[[publisher.uniffi_udl]]
|
||||||
version = "0.29.1"
|
version = "0.29.2"
|
||||||
when = "2025-03-18"
|
when = "2025-05-05"
|
||||||
user-id = 111105
|
user-id = 127697
|
||||||
user-login = "mhammond"
|
user-login = "bendk"
|
||||||
user-name = "Mark Hammond"
|
|
||||||
|
|
||||||
[[publisher.utf8_iter]]
|
[[publisher.utf8_iter]]
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -880,6 +877,13 @@ user-id = 64539
|
|||||||
user-login = "kennykerr"
|
user-login = "kennykerr"
|
||||||
user-name = "Kenny Kerr"
|
user-name = "Kenny Kerr"
|
||||||
|
|
||||||
|
[[publisher.winnow]]
|
||||||
|
version = "0.7.9"
|
||||||
|
when = "2025-05-02"
|
||||||
|
user-id = 6743
|
||||||
|
user-login = "epage"
|
||||||
|
user-name = "Ed Page"
|
||||||
|
|
||||||
[[publisher.wit-bindgen-rt]]
|
[[publisher.wit-bindgen-rt]]
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
when = "2024-03-04"
|
when = "2024-03-04"
|
||||||
@@ -1052,6 +1056,24 @@ criteria = "safe-to-deploy"
|
|||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
notes = "no build, no ambient capabilities, no unsafe"
|
notes = "no build, no ambient capabilities, no unsafe"
|
||||||
|
|
||||||
|
[[audits.bytecode-alliance.audits.cargo_metadata]]
|
||||||
|
who = "Pat Hickey <phickey@fastly.com>"
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
version = "0.15.3"
|
||||||
|
notes = "no build, no unsafe, inputs to cargo command are reasonably sanitized"
|
||||||
|
|
||||||
|
[[audits.bytecode-alliance.audits.cargo_metadata]]
|
||||||
|
who = "Alex Crichton <alex@alexcrichton.com>"
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
delta = "0.17.0 -> 0.18.1"
|
||||||
|
notes = "No major changes, no unsafe code here."
|
||||||
|
|
||||||
|
[[audits.bytecode-alliance.audits.cargo_metadata]]
|
||||||
|
who = "Alex Crichton <alex@alexcrichton.com>"
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
delta = "0.18.1 -> 0.19.2"
|
||||||
|
notes = "Dependency updates and minor changes, nothing suspicious."
|
||||||
|
|
||||||
[[audits.bytecode-alliance.audits.cfg-if]]
|
[[audits.bytecode-alliance.audits.cfg-if]]
|
||||||
who = "Alex Crichton <alex@alexcrichton.com>"
|
who = "Alex Crichton <alex@alexcrichton.com>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
@@ -1414,6 +1436,18 @@ who = "Johan Andersson <opensource@embark-studios.com>"
|
|||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
version = "1.0.58"
|
version = "1.0.58"
|
||||||
|
|
||||||
|
[[audits.embark-studios.audits.cargo_metadata]]
|
||||||
|
who = "Johan Andersson <opensource@embark-studios.com>"
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
delta = "0.15.3 -> 0.15.4"
|
||||||
|
notes = "No notable changes"
|
||||||
|
|
||||||
|
[[audits.embark-studios.audits.cargo_metadata]]
|
||||||
|
who = "Johan Andersson <opensource@embark-studios.com>"
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
delta = "0.15.4 -> 0.17.0"
|
||||||
|
notes = "No notable changes"
|
||||||
|
|
||||||
[[audits.embark-studios.audits.cfg_aliases]]
|
[[audits.embark-studios.audits.cfg_aliases]]
|
||||||
who = "Johan Andersson <opensource@embark-studios.com>"
|
who = "Johan Andersson <opensource@embark-studios.com>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
@@ -2202,68 +2236,12 @@ who = "Ameer Ghani <inahga@divviup.org>"
|
|||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
delta = "0.4.1 -> 0.4.2"
|
delta = "0.4.1 -> 0.4.2"
|
||||||
|
|
||||||
[[audits.mozilla.wildcard-audits.uniffi]]
|
[[audits.mozilla.wildcard-audits.uniffi_internal_macros]]
|
||||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
user-id = 111105 # Mark Hammond (mhammond)
|
user-id = 127697 # bendk
|
||||||
start = "2021-11-22"
|
start = "2025-02-06"
|
||||||
end = "2026-01-13"
|
end = "2026-03-14"
|
||||||
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
|
|
||||||
|
|
||||||
[[audits.mozilla.wildcard-audits.uniffi_bindgen]]
|
|
||||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
|
||||||
criteria = "safe-to-deploy"
|
|
||||||
user-id = 111105 # Mark Hammond (mhammond)
|
|
||||||
start = "2021-11-22"
|
|
||||||
end = "2026-01-13"
|
|
||||||
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
|
|
||||||
|
|
||||||
[[audits.mozilla.wildcard-audits.uniffi_build]]
|
|
||||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
|
||||||
criteria = "safe-to-deploy"
|
|
||||||
user-id = 111105 # Mark Hammond (mhammond)
|
|
||||||
start = "2021-11-22"
|
|
||||||
end = "2026-01-13"
|
|
||||||
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
|
|
||||||
|
|
||||||
[[audits.mozilla.wildcard-audits.uniffi_core]]
|
|
||||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
|
||||||
criteria = "safe-to-deploy"
|
|
||||||
user-id = 111105 # Mark Hammond (mhammond)
|
|
||||||
start = "2023-11-20"
|
|
||||||
end = "2026-01-13"
|
|
||||||
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
|
|
||||||
|
|
||||||
[[audits.mozilla.wildcard-audits.uniffi_macros]]
|
|
||||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
|
||||||
criteria = "safe-to-deploy"
|
|
||||||
user-id = 111105 # Mark Hammond (mhammond)
|
|
||||||
start = "2021-11-22"
|
|
||||||
end = "2026-01-13"
|
|
||||||
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
|
|
||||||
|
|
||||||
[[audits.mozilla.wildcard-audits.uniffi_meta]]
|
|
||||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
|
||||||
criteria = "safe-to-deploy"
|
|
||||||
user-id = 111105 # Mark Hammond (mhammond)
|
|
||||||
start = "2023-11-20"
|
|
||||||
end = "2026-01-13"
|
|
||||||
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
|
|
||||||
|
|
||||||
[[audits.mozilla.wildcard-audits.uniffi_testing]]
|
|
||||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
|
||||||
criteria = "safe-to-deploy"
|
|
||||||
user-id = 111105 # Mark Hammond (mhammond)
|
|
||||||
start = "2023-11-20"
|
|
||||||
end = "2026-01-13"
|
|
||||||
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
|
|
||||||
|
|
||||||
[[audits.mozilla.wildcard-audits.uniffi_udl]]
|
|
||||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
|
||||||
criteria = "safe-to-deploy"
|
|
||||||
user-id = 111105 # Mark Hammond (mhammond)
|
|
||||||
start = "2023-11-20"
|
|
||||||
end = "2026-01-13"
|
|
||||||
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
|
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
|
||||||
|
|
||||||
[[audits.mozilla.wildcard-audits.weedle2]]
|
[[audits.mozilla.wildcard-audits.weedle2]]
|
||||||
|
|||||||
1
third_party/rust/askama/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/askama/.cargo-checksum.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"files":{"Cargo.lock":"cad3d8819bf20edbd9c47036cefa5310b2d22b195eb476336730da9719d58a26","Cargo.toml":"5ebc58027a0fd606bd36036295f375c358d0b21f8cad26d777e95840bfdfb24e","LICENSE-APACHE":"87cb0d734c723c083e51c825930ff42bce28596b52dee15567f6b28f19c195e3","LICENSE-MIT":"df20e0180764bf5bd76f74d47bc9e8c0069a666401629c390003a1d5eba99c92","README.md":"eae12ef134f50f2b4439996b72074190e11d04f4cf9d19b6adb282801eaa0ae4","_typos.toml":"72588ef2385568368e7ec251c8451f75a16343d5a06b4c2b312beba2cdf5b308","benches/escape.rs":"bbf2e79702f8216cce9b2496cd028d8163ab261dfc4d4d6507a6dbfdca02ca5b","benches/strings.inc":"76b8ac1b79abeaced7831a03aaedf8dd85057eac78e0cecbe68f589d38f0592b","benches/to-json.rs":"ed7beddc73149e91e35408302b6314c615286bf5be360e63fe5e9364411a1f56","clippy.toml":"d141089a5edd91c83ae211257638df2ef1ca53f66b30faa98b22ea1447faeeff","deny.toml":"b7637b42164b1bbba883c422187292cf08ad624691145b5124eaf10edf38c164","src/ascii_str.rs":"272ed728e3e024cefbf16b9a004ad806781cff95f613d34c35a4e8479224d16f","src/error.rs":"0b5615a5b9d8bacedb473ca2c5f8f0d6b41faf58f5c5fc12bb72c1bea7b7efe2","src/filters/alloc.rs":"b3595393fe932043f1df0c579e4f7792f56d2ffd6ee351861d0087fbf3e49aec","src/filters/builtin.rs":"34d6dd5b6603c94b9a3f393f84adf4001e99addf50e60412c559e4748dc3eac6","src/filters/escape.rs":"e4dc1ab7a3b92c3ec402ad4e87989c08be4aa0d892eee65124735f41a0acdeec","src/filters/humansize.rs":"7d139ab50da3c90856b271fac68ed172d014d8a6ed43892e78a11e8cbdb7b0ac","src/filters/json.rs":"0e880bbc49787b83c880760038c8d900c2b1870d81d07211fa0873f77f171825","src/filters/mod.rs":"9a58841eb3173e73225bb40116d783d0b3a59d8f6b452bbe4efd5f0fd7dfd43a","src/filters/urlencode.rs":"5adbe8d4c0811146af02b061a4c648afdf45e29b0ee271d20d116b30479373cd","src/helpers.rs":"3e3e4a9a7698892f96c48a9be1305a88852b91ea278c22f2eacdfd833fde31a7","src/html.rs":"91ee580663d8ad08460373d9e5c8ed196ab0ce61eb113dc8d43ad19ca6671422","src/lib.rs":"dd806557cf5f54928f272a425d0630de850516b58dcb9f944d34b6bfef6b528a","src/values.rs":"7162ffa2dc5469d8f819ab61f9ed4fbe3b5f478cfb9eed74d141fb184c0c1161","tomlfmt.toml":"a33c21547346656aead5f46f2f07e1965f4cf0c6f2a16deb8ec06019619267aa"},"package":"5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7"}
|
||||||
701
third_party/rust/askama/Cargo.lock
generated
vendored
Normal file
701
third_party/rust/askama/Cargo.lock
generated
vendored
Normal file
@@ -0,0 +1,701 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anes"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama"
|
||||||
|
version = "0.13.1"
|
||||||
|
dependencies = [
|
||||||
|
"askama_derive",
|
||||||
|
"assert_matches",
|
||||||
|
"criterion",
|
||||||
|
"itoa",
|
||||||
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama_derive"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac"
|
||||||
|
dependencies = [
|
||||||
|
"askama_parser",
|
||||||
|
"basic-toml",
|
||||||
|
"memchr",
|
||||||
|
"proc-macro2",
|
||||||
|
"pulldown-cmark",
|
||||||
|
"quote",
|
||||||
|
"rustc-hash",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama_parser"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assert_matches"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "basic-toml"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cast"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
||||||
|
dependencies = [
|
||||||
|
"ciborium-io",
|
||||||
|
"ciborium-ll",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium-io"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium-ll"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
||||||
|
dependencies = [
|
||||||
|
"ciborium-io",
|
||||||
|
"half",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
||||||
|
dependencies = [
|
||||||
|
"anes",
|
||||||
|
"cast",
|
||||||
|
"ciborium",
|
||||||
|
"clap",
|
||||||
|
"criterion-plot",
|
||||||
|
"is-terminal",
|
||||||
|
"itertools",
|
||||||
|
"num-traits",
|
||||||
|
"once_cell",
|
||||||
|
"oorandom",
|
||||||
|
"plotters",
|
||||||
|
"rayon",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"tinytemplate",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion-plot"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||||
|
dependencies = [
|
||||||
|
"cast",
|
||||||
|
"itertools",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "half"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.171"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oorandom"
|
||||||
|
version = "11.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"plotters-backend",
|
||||||
|
"plotters-svg",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-backend"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-svg"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
|
||||||
|
dependencies = [
|
||||||
|
"plotters-backend",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulldown-cmark"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"memchr",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.140"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinytemplate"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
@@ -11,17 +11,17 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.71"
|
rust-version = "1.81"
|
||||||
name = "rinja"
|
name = "askama"
|
||||||
version = "0.3.5"
|
version = "0.13.1"
|
||||||
build = false
|
build = false
|
||||||
|
autolib = false
|
||||||
autobins = false
|
autobins = false
|
||||||
autoexamples = false
|
autoexamples = false
|
||||||
autotests = false
|
autotests = false
|
||||||
autobenches = false
|
autobenches = false
|
||||||
description = "Type-safe, compiled Jinja-like templates for Rust"
|
description = "Type-safe, compiled Jinja-like templates for Rust"
|
||||||
homepage = "https://rinja.readthedocs.io/"
|
homepage = "https://askama.readthedocs.io/"
|
||||||
documentation = "https://docs.rs/rinja"
|
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = [
|
keywords = [
|
||||||
"markup",
|
"markup",
|
||||||
@@ -31,7 +31,7 @@ keywords = [
|
|||||||
]
|
]
|
||||||
categories = ["template-engine"]
|
categories = ["template-engine"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/rinja-rs/rinja"
|
repository = "https://github.com/askama-rs/askama"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["full"]
|
features = ["full"]
|
||||||
@@ -40,8 +40,54 @@ rustdoc-args = [
|
|||||||
"--cfg=docsrs",
|
"--cfg=docsrs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[badges.maintenance]
|
||||||
|
status = "actively-developed"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = [
|
||||||
|
"askama_derive?/alloc",
|
||||||
|
"serde?/alloc",
|
||||||
|
"serde_json?/alloc",
|
||||||
|
"percent-encoding?/alloc",
|
||||||
|
]
|
||||||
|
blocks = ["askama_derive?/blocks"]
|
||||||
|
code-in-doc = ["askama_derive?/code-in-doc"]
|
||||||
|
config = ["askama_derive?/config"]
|
||||||
|
default = [
|
||||||
|
"config",
|
||||||
|
"derive",
|
||||||
|
"std",
|
||||||
|
"urlencode",
|
||||||
|
"askama_derive?/default",
|
||||||
|
]
|
||||||
|
derive = ["askama_derive"]
|
||||||
|
full = [
|
||||||
|
"default",
|
||||||
|
"blocks",
|
||||||
|
"code-in-doc",
|
||||||
|
"serde_json",
|
||||||
|
"askama_derive?/full",
|
||||||
|
]
|
||||||
|
serde_json = [
|
||||||
|
"std",
|
||||||
|
"askama_derive?/serde_json",
|
||||||
|
"dep:serde",
|
||||||
|
"dep:serde_json",
|
||||||
|
]
|
||||||
|
std = [
|
||||||
|
"alloc",
|
||||||
|
"askama_derive?/std",
|
||||||
|
"serde?/std",
|
||||||
|
"serde_json?/std",
|
||||||
|
"percent-encoding?/std",
|
||||||
|
]
|
||||||
|
urlencode = [
|
||||||
|
"askama_derive?/urlencode",
|
||||||
|
"dep:percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "rinja"
|
name = "askama"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
@@ -55,65 +101,31 @@ path = "benches/to-json.rs"
|
|||||||
harness = false
|
harness = false
|
||||||
required-features = ["serde_json"]
|
required-features = ["serde_json"]
|
||||||
|
|
||||||
[dependencies.humansize]
|
[dependencies.askama_derive]
|
||||||
version = "2"
|
version = "=0.13.1"
|
||||||
optional = true
|
optional = true
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dependencies.itoa]
|
[dependencies.itoa]
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
|
||||||
[dependencies.num-traits]
|
|
||||||
version = "0.2.6"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.percent-encoding]
|
[dependencies.percent-encoding]
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
default-features = false
|
||||||
[dependencies.rinja_derive]
|
|
||||||
version = "=0.3.5"
|
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dependencies.serde_json]
|
[dependencies.serde_json]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dev-dependencies.assert_matches]
|
||||||
|
version = "1.5.0"
|
||||||
|
|
||||||
[dev-dependencies.criterion]
|
[dev-dependencies.criterion]
|
||||||
version = "0.5"
|
version = "0.5"
|
||||||
|
|
||||||
[features]
|
|
||||||
code-in-doc = ["rinja_derive/code-in-doc"]
|
|
||||||
config = ["rinja_derive/config"]
|
|
||||||
default = [
|
|
||||||
"config",
|
|
||||||
"humansize",
|
|
||||||
"urlencode",
|
|
||||||
]
|
|
||||||
full = [
|
|
||||||
"default",
|
|
||||||
"code-in-doc",
|
|
||||||
"serde_json",
|
|
||||||
]
|
|
||||||
humansize = [
|
|
||||||
"rinja_derive/humansize",
|
|
||||||
"dep:humansize",
|
|
||||||
]
|
|
||||||
serde_json = [
|
|
||||||
"rinja_derive/serde_json",
|
|
||||||
"dep:serde",
|
|
||||||
"dep:serde_json",
|
|
||||||
]
|
|
||||||
urlencode = [
|
|
||||||
"rinja_derive/urlencode",
|
|
||||||
"dep:percent-encoding",
|
|
||||||
]
|
|
||||||
with-actix-web = ["rinja_derive/with-actix-web"]
|
|
||||||
with-axum = ["rinja_derive/with-axum"]
|
|
||||||
with-rocket = ["rinja_derive/with-rocket"]
|
|
||||||
with-warp = ["rinja_derive/with-warp"]
|
|
||||||
|
|
||||||
[badges.maintenance]
|
|
||||||
status = "actively-developed"
|
|
||||||
@@ -1,30 +1,31 @@
|
|||||||
# rinja
|
# askama
|
||||||
|
|
||||||
[](https://crates.io/crates/rinja)
|
[](https://crates.io/crates/askama)
|
||||||
[](https://github.com/rinja-rs/rinja/actions/workflows/rust.yml)
|
[](https://github.com/askama-rs/askama/actions/workflows/rust.yml)
|
||||||
[](https://rinja.readthedocs.io/)
|
[](https://askama.readthedocs.io/)
|
||||||
[](https://docs.rs/rinja/)
|
[](https://docs.rs/askama/)
|
||||||
|
|
||||||
**Rinja** implements a template rendering engine based on [Jinja](https://jinja.palletsprojects.com/),
|
**Askama** implements a template rendering engine based on [Jinja](https://jinja.palletsprojects.com/),
|
||||||
and generates type-safe Rust code from your templates at compile time
|
and generates type-safe Rust code from your templates at compile time
|
||||||
based on a user-defined `struct` to hold the template's context.
|
based on a user-defined `struct` to hold the template's context.
|
||||||
See below for an example. It is a fork of [Askama](https://crates.io/crates/askama).
|
|
||||||
|
At some point, **Askama** got forked into **Rinja** (explained [here](https://blog.guillaume-gomez.fr/articles/2024-07-31+docs.rs+switching+jinja+template+framework+from+tera+to+rinja))
|
||||||
|
before getting merged back into **Askama**.
|
||||||
|
|
||||||
All feedback welcome! Feel free to file bugs, requests for documentation and
|
All feedback welcome! Feel free to file bugs, requests for documentation and
|
||||||
any other feedback to the [issue tracker][issues].
|
any other feedback to the [issue tracker][issues].
|
||||||
|
|
||||||
You can find the documentation about our syntax, features, configuration in our book:
|
You can find the documentation about our syntax, features, configuration in our book:
|
||||||
[rinja.readthedocs.io](https://rinja.readthedocs.io/).
|
[askama.readthedocs.io](https://askama.readthedocs.io/).
|
||||||
|
|
||||||
Have a look at our [*Rinja Playground*](https://rinja-rs.github.io/play-rinja/),
|
Have a look at our [*Askama Playground*](https://askama-rs.github.io/askama_playground/),
|
||||||
if you want to try out rinja's code generation online.
|
if you want to try out askama's code generation online.
|
||||||
|
|
||||||
### Feature highlights
|
### Feature highlights
|
||||||
|
|
||||||
* Construct templates using a familiar, easy-to-use syntax
|
* Construct templates using a familiar, easy-to-use syntax
|
||||||
* Benefit from the safety provided by Rust's type system
|
* Benefit from the safety provided by Rust's type system
|
||||||
* Template code is compiled into your crate for optimal performance
|
* Template code is compiled into your crate for optimal performance
|
||||||
* Optional built-in support for Actix, Axum, Rocket, and warp web frameworks
|
|
||||||
* Debugging features to assist you in template development
|
* Debugging features to assist you in template development
|
||||||
* Templates must be valid UTF-8 and produce UTF-8 when rendered
|
* Templates must be valid UTF-8 and produce UTF-8 when rendered
|
||||||
* Works on stable Rust
|
* Works on stable Rust
|
||||||
@@ -40,16 +41,16 @@ if you want to try out rinja's code generation online.
|
|||||||
* Opt-out HTML escaping
|
* Opt-out HTML escaping
|
||||||
* Syntax customization
|
* Syntax customization
|
||||||
|
|
||||||
[issues]: https://github.com/rinja-rs/rinja/issues
|
[issues]: https://github.com/askama-rs/askama/issues
|
||||||
|
|
||||||
|
|
||||||
How to get started
|
How to get started
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
First, add the rinja dependency to your crate's `Cargo.toml`:
|
First, add the askama dependency to your crate's `Cargo.toml`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo add rinja
|
cargo add askama
|
||||||
```
|
```
|
||||||
|
|
||||||
Now create a directory called `templates` in your crate root.
|
Now create a directory called `templates` in your crate root.
|
||||||
@@ -62,7 +63,7 @@ Hello, {{ name }}!
|
|||||||
In any Rust file inside your crate, add the following:
|
In any Rust file inside your crate, add the following:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rinja::Template; // bring trait in scope
|
use askama::Template; // bring trait in scope
|
||||||
|
|
||||||
#[derive(Template)] // this will generate the code...
|
#[derive(Template)] // this will generate the code...
|
||||||
#[template(path = "hello.html")] // using the template in this path, relative
|
#[template(path = "hello.html")] // using the template in this path, relative
|
||||||
@@ -8,17 +8,25 @@ extend-exclude = [
|
|||||||
"fuzzing/fuzz/artifacts/",
|
"fuzzing/fuzz/artifacts/",
|
||||||
"fuzzing/fuzz/corpus/",
|
"fuzzing/fuzz/corpus/",
|
||||||
"target/",
|
"target/",
|
||||||
"rinja_parser/tests/*.txt",
|
"askama_parser/tests/*.txt",
|
||||||
# fillter texts
|
"testing/templates/fuzzed-*",
|
||||||
"rinja/benches/strings.inc",
|
# we copied the files verbatim including any typos :)
|
||||||
|
"askama_parser/benches",
|
||||||
|
"askama_derive_standalone/benches",
|
||||||
|
# filler texts
|
||||||
|
"*/benches/strings.inc",
|
||||||
# too many false positives
|
# too many false positives
|
||||||
"testing/tests/gen_ws_tests.py",
|
"testing/tests/gen_ws_tests.py",
|
||||||
]
|
]
|
||||||
|
|
||||||
[default.extend-words]
|
[default.extend-words]
|
||||||
|
# It's actually called that in the ASCII standard
|
||||||
|
Enquiry = "Enquiry"
|
||||||
|
|
||||||
# French words
|
# French words
|
||||||
exemple = "exemple"
|
exemple = "exemple"
|
||||||
existant = "existant"
|
existant = "existant"
|
||||||
|
|
||||||
# used in tests
|
# used in tests
|
||||||
Ba = "Ba"
|
Ba = "Ba"
|
||||||
fo = "fo"
|
fo = "fo"
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
use askama::filters::{Html, escape};
|
||||||
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||||
use rinja::filters::{Html, escape};
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
criterion_group!(benches, functions);
|
criterion_group!(benches, functions);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
use askama::Template;
|
||||||
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||||
use rinja::Template;
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
criterion_group!(benches, functions);
|
criterion_group!(benches, functions);
|
||||||
1
third_party/rust/askama/clippy.toml
vendored
Normal file
1
third_party/rust/askama/clippy.toml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
msrv = "1.81.0"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[licenses]
|
[licenses]
|
||||||
version = 2
|
version = 2
|
||||||
allow = ["Apache-2.0", "MIT", "MIT-0", "Unicode-DFS-2016"]
|
allow = ["Apache-2.0", "MIT", "MIT-0", "Unicode-3.0"]
|
||||||
private = { ignore = true }
|
private = { ignore = true }
|
||||||
|
|
||||||
[[licenses.clarify]]
|
[[licenses.clarify]]
|
||||||
144
third_party/rust/askama/src/ascii_str.rs
vendored
Normal file
144
third_party/rust/askama/src/ascii_str.rs
vendored
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// FIXME: Replace `AsciiChar` with `[core:ascii::Char]` once [#110998] is stable
|
||||||
|
// [#110998]: https://github.com/rust-lang/rust/issues/110998
|
||||||
|
|
||||||
|
#![allow(unreachable_pub)]
|
||||||
|
|
||||||
|
use core::ops::{Deref, Index, IndexMut};
|
||||||
|
|
||||||
|
pub use _ascii_char::AsciiChar;
|
||||||
|
|
||||||
|
/// A string that only contains ASCII characters, same layout as [`str`].
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct AsciiStr([AsciiChar]);
|
||||||
|
|
||||||
|
impl AsciiStr {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new_sized<const N: usize>(src: &str) -> [AsciiChar; N] {
|
||||||
|
if !src.is_ascii() || src.len() > N {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
|
||||||
|
let src = src.as_bytes();
|
||||||
|
let mut result = [AsciiChar::NULL; N];
|
||||||
|
let mut i = 0;
|
||||||
|
while i < src.len() {
|
||||||
|
result[i] = AsciiChar::new(src[i]);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn from_slice(src: &[AsciiChar]) -> &Self {
|
||||||
|
// SAFETY: `Self` is transparent over `[AsciiChar]`.
|
||||||
|
unsafe { core::mem::transmute::<&[AsciiChar], &AsciiStr>(src) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn as_str(&self) -> &str {
|
||||||
|
// SAFETY: `Self` has the same layout as `str`,
|
||||||
|
// and all ASCII characters are valid UTF-8 characters.
|
||||||
|
unsafe { core::mem::transmute::<&AsciiStr, &str>(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must not implement `DerefMut`. Not every `char` is an ASCII character.
|
||||||
|
impl Deref for AsciiStr {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Idx> Index<Idx> for AsciiStr
|
||||||
|
where
|
||||||
|
[AsciiChar]: Index<Idx, Output = [AsciiChar]>,
|
||||||
|
{
|
||||||
|
type Output = [AsciiChar];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: Idx) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Idx> IndexMut<Idx> for AsciiStr
|
||||||
|
where
|
||||||
|
[AsciiChar]: IndexMut<Idx, Output = [AsciiChar]>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn index_mut(&mut self, index: Idx) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for &'static AsciiStr {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
// SAFETY: `Self` has the same layout as `str`.
|
||||||
|
unsafe { core::mem::transmute::<&str, &AsciiStr>("") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsciiChar {
|
||||||
|
pub const NULL: AsciiChar = AsciiChar::new(0);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn slice_as_bytes<const N: usize>(src: &[AsciiChar; N]) -> &[u8; N] {
|
||||||
|
// SAFETY: `[AsciiChar]` has the same layout as `[u8]`.
|
||||||
|
unsafe { core::mem::transmute::<&[AsciiChar; N], &[u8; N]>(src) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn two_digits(d: u32) -> [Self; 2] {
|
||||||
|
const ALPHABET: &[u8; 10] = b"0123456789";
|
||||||
|
|
||||||
|
if d >= ALPHABET.len().pow(2) as u32 {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
[
|
||||||
|
Self::new(ALPHABET[d as usize / ALPHABET.len()]),
|
||||||
|
Self::new(ALPHABET[d as usize % ALPHABET.len()]),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn two_hex_digits(d: u32) -> [Self; 2] {
|
||||||
|
const ALPHABET: &[u8; 16] = b"0123456789abcdef";
|
||||||
|
|
||||||
|
if d >= ALPHABET.len().pow(2) as u32 {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
[
|
||||||
|
Self::new(ALPHABET[d as usize / ALPHABET.len()]),
|
||||||
|
Self::new(ALPHABET[d as usize % ALPHABET.len()]),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod _ascii_char {
|
||||||
|
/// A character that is known to be in ASCII range, same layout as [`u8`].
|
||||||
|
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct AsciiChar(u8);
|
||||||
|
|
||||||
|
impl AsciiChar {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(c: u8) -> Self {
|
||||||
|
if c.is_ascii() { Self(c) } else { panic!() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
307
third_party/rust/askama/src/error.rs
vendored
Normal file
307
third_party/rust/askama/src/error.rs
vendored
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::error::Error as StdError;
|
||||||
|
use core::fmt;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
/// The [`Result`](std::result::Result) type with [`Error`] as default error type
|
||||||
|
pub type Result<I, E = Error> = core::result::Result<I, E>;
|
||||||
|
|
||||||
|
/// askama's error type
|
||||||
|
///
|
||||||
|
/// Used as error value for e.g. [`Template::render()`][crate::Template::render()]
|
||||||
|
/// and custom filters.
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Generic, unspecified formatting error
|
||||||
|
Fmt,
|
||||||
|
/// Key not present in [`Values`][crate::Values]
|
||||||
|
ValueMissing,
|
||||||
|
/// Incompatible value type for key in [`Values`][crate::Values]
|
||||||
|
ValueType,
|
||||||
|
/// An error raised by using `?` in a template
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
Custom(Box<dyn StdError + Send + Sync>),
|
||||||
|
/// JSON conversion error
|
||||||
|
#[cfg(feature = "serde_json")]
|
||||||
|
Json(serde_json::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
/// Capture an [`StdError`]
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub fn custom(err: impl Into<Box<dyn StdError + Send + Sync>>) -> Self {
|
||||||
|
Self::Custom(err.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this [`Error`] into a
|
||||||
|
/// <code>[Box]<dyn [StdError] + [Send] + [Sync]></code>
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub fn into_box(self) -> Box<dyn StdError + Send + Sync> {
|
||||||
|
match self {
|
||||||
|
Error::Fmt => fmt::Error.into(),
|
||||||
|
Error::ValueMissing => Box::new(Error::ValueMissing),
|
||||||
|
Error::ValueType => Box::new(Error::ValueType),
|
||||||
|
Error::Custom(err) => err,
|
||||||
|
#[cfg(feature = "serde_json")]
|
||||||
|
Error::Json(err) => err.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this [`Error`] into an [`io::Error`]
|
||||||
|
///
|
||||||
|
/// Not this error itself, but the contained [`source`][StdError::source] is returned.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn into_io_error(self) -> io::Error {
|
||||||
|
io::Error::other(match self {
|
||||||
|
Error::Custom(err) => match err.downcast() {
|
||||||
|
Ok(err) => return *err,
|
||||||
|
Err(err) => err,
|
||||||
|
},
|
||||||
|
err => err.into_box(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdError for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||||
|
match self {
|
||||||
|
Error::Fmt => Some(&fmt::Error),
|
||||||
|
Error::ValueMissing => None,
|
||||||
|
Error::ValueType => None,
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
Error::Custom(err) => Some(err.as_ref()),
|
||||||
|
#[cfg(feature = "serde_json")]
|
||||||
|
Error::Json(err) => Some(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::Fmt => fmt::Error.fmt(f),
|
||||||
|
Error::ValueMissing => f.write_str("key missing in values"),
|
||||||
|
Error::ValueType => f.write_str("value has wrong type"),
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
Error::Custom(err) => err.fmt(f),
|
||||||
|
#[cfg(feature = "serde_json")]
|
||||||
|
Error::Json(err) => err.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for fmt::Error {
|
||||||
|
#[inline]
|
||||||
|
fn from(_: Error) -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl From<Error> for io::Error {
|
||||||
|
#[inline]
|
||||||
|
fn from(err: Error) -> Self {
|
||||||
|
err.into_io_error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<fmt::Error> for Error {
|
||||||
|
#[inline]
|
||||||
|
fn from(_: fmt::Error) -> Self {
|
||||||
|
Error::Fmt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This conversion inspects the argument and chooses the best fitting [`Error`] variant
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl From<Box<dyn StdError + Send + Sync>> for Error {
|
||||||
|
#[inline]
|
||||||
|
fn from(err: Box<dyn StdError + Send + Sync>) -> Self {
|
||||||
|
error_from_stderror(err, MAX_ERROR_UNWRAP_COUNT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This conversion inspects the argument and chooses the best fitting [`Error`] variant
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
#[inline]
|
||||||
|
fn from(err: io::Error) -> Self {
|
||||||
|
error_from_io_error(err, MAX_ERROR_UNWRAP_COUNT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
const MAX_ERROR_UNWRAP_COUNT: usize = 5;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn error_from_stderror(err: Box<dyn StdError + Send + Sync>, unwraps: usize) -> Error {
|
||||||
|
let Some(unwraps) = unwraps.checked_sub(1) else {
|
||||||
|
return Error::Custom(err);
|
||||||
|
};
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
let _ = unwraps;
|
||||||
|
match ErrorKind::inspect(err.as_ref()) {
|
||||||
|
ErrorKind::Fmt => Error::Fmt,
|
||||||
|
ErrorKind::Custom => Error::Custom(err),
|
||||||
|
#[cfg(feature = "serde_json")]
|
||||||
|
ErrorKind::Json => match err.downcast() {
|
||||||
|
Ok(err) => Error::Json(*err),
|
||||||
|
Err(_) => Error::Fmt, // unreachable
|
||||||
|
},
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
ErrorKind::Io => match err.downcast() {
|
||||||
|
Ok(err) => error_from_io_error(*err, unwraps),
|
||||||
|
Err(_) => Error::Fmt, // unreachable
|
||||||
|
},
|
||||||
|
ErrorKind::Askama => match err.downcast() {
|
||||||
|
Ok(err) => *err,
|
||||||
|
Err(_) => Error::Fmt, // unreachable
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn error_from_io_error(err: io::Error, unwraps: usize) -> Error {
|
||||||
|
let Some(inner) = err.get_ref() else {
|
||||||
|
return Error::custom(err);
|
||||||
|
};
|
||||||
|
let Some(unwraps) = unwraps.checked_sub(1) else {
|
||||||
|
return match err.into_inner() {
|
||||||
|
Some(err) => Error::Custom(err),
|
||||||
|
None => Error::Fmt, // unreachable
|
||||||
|
};
|
||||||
|
};
|
||||||
|
match ErrorKind::inspect(inner) {
|
||||||
|
ErrorKind::Fmt => Error::Fmt,
|
||||||
|
ErrorKind::Askama => match err.downcast() {
|
||||||
|
Ok(err) => err,
|
||||||
|
Err(_) => Error::Fmt, // unreachable
|
||||||
|
},
|
||||||
|
#[cfg(feature = "serde_json")]
|
||||||
|
ErrorKind::Json => match err.downcast() {
|
||||||
|
Ok(err) => Error::Json(err),
|
||||||
|
Err(_) => Error::Fmt, // unreachable
|
||||||
|
},
|
||||||
|
ErrorKind::Custom => match err.into_inner() {
|
||||||
|
Some(err) => Error::Custom(err),
|
||||||
|
None => Error::Fmt, // unreachable
|
||||||
|
},
|
||||||
|
ErrorKind::Io => match err.downcast() {
|
||||||
|
Ok(inner) => error_from_io_error(inner, unwraps),
|
||||||
|
Err(_) => Error::Fmt, // unreachable
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
enum ErrorKind {
|
||||||
|
Fmt,
|
||||||
|
Custom,
|
||||||
|
#[cfg(feature = "serde_json")]
|
||||||
|
Json,
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
Io,
|
||||||
|
Askama,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl ErrorKind {
|
||||||
|
fn inspect(err: &(dyn StdError + 'static)) -> ErrorKind {
|
||||||
|
if err.is::<fmt::Error>() {
|
||||||
|
return ErrorKind::Fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
if err.is::<io::Error>() {
|
||||||
|
return ErrorKind::Io;
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.is::<Error>() {
|
||||||
|
return ErrorKind::Askama;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde_json")]
|
||||||
|
if err.is::<serde_json::Error>() {
|
||||||
|
return ErrorKind::Json;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorKind::Custom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde_json")]
|
||||||
|
impl From<serde_json::Error> for Error {
|
||||||
|
#[inline]
|
||||||
|
fn from(err: serde_json::Error) -> Self {
|
||||||
|
Error::Json(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Infallible> for Error {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: Infallible) -> Self {
|
||||||
|
match value {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
const _: () = {
|
||||||
|
trait AssertSendSyncStatic: Send + Sync + 'static {}
|
||||||
|
impl AssertSendSyncStatic for Error {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper trait to convert a custom `?` call into a [`crate::Result`]
|
||||||
|
pub trait ResultConverter {
|
||||||
|
/// Okay Value type of the output
|
||||||
|
type Value;
|
||||||
|
/// Input type
|
||||||
|
type Input;
|
||||||
|
|
||||||
|
/// Consume an interior mutable `self`, and turn it into a [`crate::Result`]
|
||||||
|
fn askama_conv_result(self, result: Self::Input) -> Result<Self::Value, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper marker to be used with [`ResultConverter`]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ErrorMarker<T>(PhantomData<Result<T>>);
|
||||||
|
|
||||||
|
impl<T> ErrorMarker<T> {
|
||||||
|
/// Get marker for a [`Result`] type
|
||||||
|
#[inline]
|
||||||
|
pub fn of(_: &T) -> Self {
|
||||||
|
Self(PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<T, E> ResultConverter for &ErrorMarker<Result<T, E>>
|
||||||
|
where
|
||||||
|
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
|
{
|
||||||
|
type Value = T;
|
||||||
|
type Input = Result<T, E>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn askama_conv_result(self, result: Self::Input) -> Result<Self::Value, Error> {
|
||||||
|
result.map_err(Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> ResultConverter for &&ErrorMarker<Result<T, E>>
|
||||||
|
where
|
||||||
|
E: Into<Error>,
|
||||||
|
{
|
||||||
|
type Value = T;
|
||||||
|
type Input = Result<T, E>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn askama_conv_result(self, result: Self::Input) -> Result<Self::Value, Error> {
|
||||||
|
result.map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
657
third_party/rust/askama/src/filters/alloc.rs
vendored
Normal file
657
third_party/rust/askama/src/filters/alloc.rs
vendored
Normal file
@@ -0,0 +1,657 @@
|
|||||||
|
use alloc::string::String;
|
||||||
|
use core::fmt::{self, Write};
|
||||||
|
|
||||||
|
use super::MAX_LEN;
|
||||||
|
use super::escape::HtmlSafeOutput;
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
/// Return an ephemeral `&str` for `$src: impl fmt::Display`
|
||||||
|
///
|
||||||
|
/// If `$str` is `&str` or `String`, this macro simply passes on its content.
|
||||||
|
/// If it is neither, then the formatted data is collection into `&buffer`.
|
||||||
|
///
|
||||||
|
/// `return`s with an error if the formatting failed.
|
||||||
|
macro_rules! try_to_str {
|
||||||
|
($src:expr => $buffer:ident) => {
|
||||||
|
match format_args!("{}", $src) {
|
||||||
|
args => {
|
||||||
|
if let Some(s) = args.as_str() {
|
||||||
|
s
|
||||||
|
} else {
|
||||||
|
$buffer = String::new();
|
||||||
|
$buffer.write_fmt(args)?;
|
||||||
|
&$buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Formats arguments according to the specified format
|
||||||
|
///
|
||||||
|
/// The *second* argument to this filter must be a string literal (as in normal
|
||||||
|
/// Rust). The two arguments are passed through to the `format!()`
|
||||||
|
/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
|
||||||
|
/// the Askama code generator, but the order is swapped to support filter
|
||||||
|
/// composition.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// {{ value|fmt("{:?}") }}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ value|fmt("{:?}") }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example {
|
||||||
|
/// value: (usize, usize),
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { value: (3, 4) }.to_string(),
|
||||||
|
/// "<div>(3, 4)</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Compare with [format](./fn.format.html).
|
||||||
|
pub fn fmt() {}
|
||||||
|
|
||||||
|
/// Formats arguments according to the specified format
|
||||||
|
///
|
||||||
|
/// The first argument to this filter must be a string literal (as in normal
|
||||||
|
/// Rust). All arguments are passed through to the `format!()`
|
||||||
|
/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
|
||||||
|
/// the Askama code generator.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// {{ "{:?}{:?}"|format(value, other_value) }}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ "{:?}"|format(value) }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example {
|
||||||
|
/// value: (usize, usize),
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { value: (3, 4) }.to_string(),
|
||||||
|
/// "<div>(3, 4)</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Compare with [fmt](./fn.fmt.html).
|
||||||
|
pub fn format() {}
|
||||||
|
|
||||||
|
/// Replaces line breaks in plain text with appropriate HTML
|
||||||
|
///
|
||||||
|
/// A single newline becomes an HTML line break `<br>` and a new line
|
||||||
|
/// followed by a blank line becomes a paragraph break `<p>`.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ example|linebreaks }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// example: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: "Foo\nBar\n\nBaz" }.to_string(),
|
||||||
|
/// "<div><p>Foo<br/>Bar</p><p>Baz</p></div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn linebreaks(s: impl fmt::Display) -> Result<HtmlSafeOutput<String>, fmt::Error> {
|
||||||
|
fn linebreaks(s: &str) -> String {
|
||||||
|
let linebroken = s.replace("\n\n", "</p><p>").replace('\n', "<br/>");
|
||||||
|
alloc::format!("<p>{linebroken}</p>")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buffer;
|
||||||
|
Ok(HtmlSafeOutput(linebreaks(try_to_str!(s => buffer))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts all newlines in a piece of plain text to HTML line breaks
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ lines|linebreaksbr }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// lines: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { lines: "a\nb\nc" }.to_string(),
|
||||||
|
/// "<div>a<br/>b<br/>c</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn linebreaksbr(s: impl fmt::Display) -> Result<HtmlSafeOutput<String>, fmt::Error> {
|
||||||
|
fn linebreaksbr(s: &str) -> String {
|
||||||
|
s.replace('\n', "<br/>")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buffer;
|
||||||
|
Ok(HtmlSafeOutput(linebreaksbr(try_to_str!(s => buffer))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces only paragraph breaks in plain text with appropriate HTML
|
||||||
|
///
|
||||||
|
/// A new line followed by a blank line becomes a paragraph break `<p>`.
|
||||||
|
/// Paragraph tags only wrap content; empty paragraphs are removed.
|
||||||
|
/// No `<br/>` tags are added.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// {{ lines|paragraphbreaks }}
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// lines: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { lines: "Foo\nBar\n\nBaz" }.to_string(),
|
||||||
|
/// "<p>Foo\nBar</p><p>Baz</p>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn paragraphbreaks(s: impl fmt::Display) -> Result<HtmlSafeOutput<String>, fmt::Error> {
|
||||||
|
fn paragraphbreaks(s: &str) -> String {
|
||||||
|
let linebroken = s.replace("\n\n", "</p><p>").replace("<p></p>", "");
|
||||||
|
alloc::format!("<p>{linebroken}</p>")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buffer;
|
||||||
|
Ok(HtmlSafeOutput(paragraphbreaks(try_to_str!(s => buffer))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to lowercase
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ word|lower }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// word: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { word: "FOO" }.to_string(),
|
||||||
|
/// "<div>foo</div>"
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { word: "FooBar" }.to_string(),
|
||||||
|
/// "<div>foobar</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn lower(s: impl fmt::Display) -> Result<String, fmt::Error> {
|
||||||
|
let mut buffer;
|
||||||
|
Ok(try_to_str!(s => buffer).to_lowercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to lowercase, alias for the `|lower` filter
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ word|lowercase }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// word: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { word: "FOO" }.to_string(),
|
||||||
|
/// "<div>foo</div>"
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { word: "FooBar" }.to_string(),
|
||||||
|
/// "<div>foobar</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn lowercase(s: impl fmt::Display) -> Result<String, fmt::Error> {
|
||||||
|
lower(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to uppercase
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ word|upper }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// word: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { word: "foo" }.to_string(),
|
||||||
|
/// "<div>FOO</div>"
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { word: "FooBar" }.to_string(),
|
||||||
|
/// "<div>FOOBAR</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn upper(s: impl fmt::Display) -> Result<String, fmt::Error> {
|
||||||
|
let mut buffer;
|
||||||
|
Ok(try_to_str!(s => buffer).to_uppercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to uppercase, alias for the `|upper` filter
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ word|uppercase }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// word: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { word: "foo" }.to_string(),
|
||||||
|
/// "<div>FOO</div>"
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { word: "FooBar" }.to_string(),
|
||||||
|
/// "<div>FOOBAR</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn uppercase(s: impl fmt::Display) -> Result<String, fmt::Error> {
|
||||||
|
upper(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Strip leading and trailing whitespace
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ example|trim }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// example: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: " Hello\tworld\t" }.to_string(),
|
||||||
|
/// "<div>Hello\tworld</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub fn trim<T: fmt::Display>(s: T) -> Result<String> {
|
||||||
|
struct Collector(String);
|
||||||
|
|
||||||
|
impl fmt::Write for Collector {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
match self.0.is_empty() {
|
||||||
|
true => self.0.write_str(s.trim_start()),
|
||||||
|
false => self.0.write_str(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut collector = Collector(String::new());
|
||||||
|
write!(collector, "{s}")?;
|
||||||
|
let Collector(mut s) = collector;
|
||||||
|
s.truncate(s.trim_end().len());
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indent lines with `width` spaces
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ example|indent(4) }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// example: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: "hello\nfoo\nbar" }.to_string(),
|
||||||
|
/// "<div>hello\n foo\n bar</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn indent(s: impl fmt::Display, width: usize) -> Result<String, fmt::Error> {
|
||||||
|
fn indent(args: fmt::Arguments<'_>, width: usize) -> Result<String, fmt::Error> {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
let s = if width >= MAX_LEN {
|
||||||
|
buffer.write_fmt(args)?;
|
||||||
|
return Ok(buffer);
|
||||||
|
} else if let Some(s) = args.as_str() {
|
||||||
|
if s.len() >= MAX_LEN {
|
||||||
|
return Ok(s.into());
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buffer.write_fmt(args)?;
|
||||||
|
if buffer.len() >= MAX_LEN {
|
||||||
|
return Ok(buffer);
|
||||||
|
}
|
||||||
|
buffer.as_str()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut indented = String::new();
|
||||||
|
for (i, c) in s.char_indices() {
|
||||||
|
indented.push(c);
|
||||||
|
|
||||||
|
if c == '\n' && i < s.len() - 1 {
|
||||||
|
for _ in 0..width {
|
||||||
|
indented.push(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(indented)
|
||||||
|
}
|
||||||
|
indent(format_args!("{s}"), width)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Capitalize a value. The first character will be uppercase, all others lowercase.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ example|capitalize }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// example: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: "hello" }.to_string(),
|
||||||
|
/// "<div>Hello</div>"
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: "hElLO" }.to_string(),
|
||||||
|
/// "<div>Hello</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn capitalize(s: impl fmt::Display) -> Result<String, fmt::Error> {
|
||||||
|
fn capitalize(s: &str) -> Result<String, fmt::Error> {
|
||||||
|
let mut chars = s.chars();
|
||||||
|
if let Some(c) = chars.next() {
|
||||||
|
let mut replacement = String::with_capacity(s.len());
|
||||||
|
replacement.extend(c.to_uppercase());
|
||||||
|
replacement.push_str(&chars.as_str().to_lowercase());
|
||||||
|
Ok(replacement)
|
||||||
|
} else {
|
||||||
|
Ok(String::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buffer;
|
||||||
|
capitalize(try_to_str!(s => buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Count the words in that string.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ example|wordcount }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// example: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: "askama is sort of cool" }.to_string(),
|
||||||
|
/// "<div>5</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn wordcount(s: impl fmt::Display) -> Result<usize, fmt::Error> {
|
||||||
|
let mut buffer;
|
||||||
|
Ok(try_to_str!(s => buffer).split_whitespace().count())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a title cased version of the value. Words will start with uppercase letters, all
|
||||||
|
/// remaining characters are lowercase.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ example|title }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// example: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: "hello WORLD" }.to_string(),
|
||||||
|
/// "<div>Hello World</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn title(s: impl fmt::Display) -> Result<String, fmt::Error> {
|
||||||
|
let mut buffer;
|
||||||
|
let s = try_to_str!(s => buffer);
|
||||||
|
let mut need_capitalization = true;
|
||||||
|
|
||||||
|
// Sadly enough, we can't mutate a string when iterating over its chars, likely because it could
|
||||||
|
// change the size of a char, "breaking" the char indices.
|
||||||
|
let mut output = String::with_capacity(s.len());
|
||||||
|
for c in s.chars() {
|
||||||
|
if c.is_whitespace() {
|
||||||
|
output.push(c);
|
||||||
|
need_capitalization = true;
|
||||||
|
} else if need_capitalization {
|
||||||
|
match c.is_uppercase() {
|
||||||
|
true => output.push(c),
|
||||||
|
false => output.extend(c.to_uppercase()),
|
||||||
|
}
|
||||||
|
need_capitalization = false;
|
||||||
|
} else {
|
||||||
|
match c.is_lowercase() {
|
||||||
|
true => output.push(c),
|
||||||
|
false => output.extend(c.to_lowercase()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use alloc::string::ToString;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_linebreaks() {
|
||||||
|
assert_eq!(
|
||||||
|
linebreaks("Foo\nBar Baz").unwrap().to_string(),
|
||||||
|
"<p>Foo<br/>Bar Baz</p>"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
linebreaks("Foo\nBar\n\nBaz").unwrap().to_string(),
|
||||||
|
"<p>Foo<br/>Bar</p><p>Baz</p>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_linebreaksbr() {
|
||||||
|
assert_eq!(linebreaksbr("Foo\nBar").unwrap().to_string(), "Foo<br/>Bar");
|
||||||
|
assert_eq!(
|
||||||
|
linebreaksbr("Foo\nBar\n\nBaz").unwrap().to_string(),
|
||||||
|
"Foo<br/>Bar<br/><br/>Baz"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_paragraphbreaks() {
|
||||||
|
assert_eq!(
|
||||||
|
paragraphbreaks("Foo\nBar Baz").unwrap().to_string(),
|
||||||
|
"<p>Foo\nBar Baz</p>"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
paragraphbreaks("Foo\nBar\n\nBaz").unwrap().to_string(),
|
||||||
|
"<p>Foo\nBar</p><p>Baz</p>"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
paragraphbreaks("Foo\n\n\n\n\nBar\n\nBaz")
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
"<p>Foo</p><p>\nBar</p><p>Baz</p>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lower() {
|
||||||
|
assert_eq!(lower("Foo").unwrap().to_string(), "foo");
|
||||||
|
assert_eq!(lower("FOO").unwrap().to_string(), "foo");
|
||||||
|
assert_eq!(lower("FooBar").unwrap().to_string(), "foobar");
|
||||||
|
assert_eq!(lower("foo").unwrap().to_string(), "foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_upper() {
|
||||||
|
assert_eq!(upper("Foo").unwrap().to_string(), "FOO");
|
||||||
|
assert_eq!(upper("FOO").unwrap().to_string(), "FOO");
|
||||||
|
assert_eq!(upper("FooBar").unwrap().to_string(), "FOOBAR");
|
||||||
|
assert_eq!(upper("foo").unwrap().to_string(), "FOO");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trim() {
|
||||||
|
assert_eq!(trim(" Hello\tworld\t").unwrap().to_string(), "Hello\tworld");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_indent() {
|
||||||
|
assert_eq!(indent("hello", 2).unwrap().to_string(), "hello");
|
||||||
|
assert_eq!(indent("hello\n", 2).unwrap().to_string(), "hello\n");
|
||||||
|
assert_eq!(indent("hello\nfoo", 2).unwrap().to_string(), "hello\n foo");
|
||||||
|
assert_eq!(
|
||||||
|
indent("hello\nfoo\n bar", 4).unwrap().to_string(),
|
||||||
|
"hello\n foo\n bar"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
indent("hello", 267_332_238_858).unwrap().to_string(),
|
||||||
|
"hello"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_capitalize() {
|
||||||
|
assert_eq!(capitalize("foo").unwrap().to_string(), "Foo".to_string());
|
||||||
|
assert_eq!(capitalize("f").unwrap().to_string(), "F".to_string());
|
||||||
|
assert_eq!(capitalize("fO").unwrap().to_string(), "Fo".to_string());
|
||||||
|
assert_eq!(capitalize("").unwrap().to_string(), String::new());
|
||||||
|
assert_eq!(capitalize("FoO").unwrap().to_string(), "Foo".to_string());
|
||||||
|
assert_eq!(
|
||||||
|
capitalize("foO BAR").unwrap().to_string(),
|
||||||
|
"Foo bar".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
capitalize("äØÄÅÖ").unwrap().to_string(),
|
||||||
|
"Äøäåö".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(capitalize("ß").unwrap().to_string(), "SS".to_string());
|
||||||
|
assert_eq!(capitalize("ßß").unwrap().to_string(), "SSß".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wordcount() {
|
||||||
|
assert_eq!(wordcount("").unwrap(), 0);
|
||||||
|
assert_eq!(wordcount(" \n\t").unwrap(), 0);
|
||||||
|
assert_eq!(wordcount("foo").unwrap(), 1);
|
||||||
|
assert_eq!(wordcount("foo bar").unwrap(), 2);
|
||||||
|
assert_eq!(wordcount("foo bar").unwrap(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_title() {
|
||||||
|
assert_eq!(&title("").unwrap(), "");
|
||||||
|
assert_eq!(&title(" \n\t").unwrap(), " \n\t");
|
||||||
|
assert_eq!(&title("foo").unwrap(), "Foo");
|
||||||
|
assert_eq!(&title(" foo").unwrap(), " Foo");
|
||||||
|
assert_eq!(&title("foo bar").unwrap(), "Foo Bar");
|
||||||
|
assert_eq!(&title("foo bar ").unwrap(), "Foo Bar ");
|
||||||
|
assert_eq!(&title("fOO").unwrap(), "Foo");
|
||||||
|
assert_eq!(&title("fOo BaR").unwrap(), "Foo Bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fuzzed_indent_filter() {
|
||||||
|
let s = "hello\nfoo\nbar".to_string().repeat(1024);
|
||||||
|
assert_eq!(indent(s.clone(), 4).unwrap().to_string(), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
507
third_party/rust/askama/src/filters/builtin.rs
vendored
Normal file
507
third_party/rust/askama/src/filters/builtin.rs
vendored
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
use core::cell::Cell;
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::fmt::{self, Write};
|
||||||
|
use core::ops::Deref;
|
||||||
|
use core::pin::Pin;
|
||||||
|
|
||||||
|
use super::MAX_LEN;
|
||||||
|
use super::escape::FastWritable;
|
||||||
|
use crate::{Error, Result};
|
||||||
|
|
||||||
|
/// Limit string length, appends '...' if truncated
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ example|truncate(2) }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// example: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: "hello" }.to_string(),
|
||||||
|
/// "<div>he...</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn truncate<S: fmt::Display>(
|
||||||
|
source: S,
|
||||||
|
remaining: usize,
|
||||||
|
) -> Result<TruncateFilter<S>, Infallible> {
|
||||||
|
Ok(TruncateFilter { source, remaining })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TruncateFilter<S> {
|
||||||
|
source: S,
|
||||||
|
remaining: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: fmt::Display> fmt::Display for TruncateFilter<S> {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(TruncateWriter::new(f, self.remaining), "{}", self.source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: FastWritable> FastWritable for TruncateFilter<S> {
|
||||||
|
#[inline]
|
||||||
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
|
self.source
|
||||||
|
.write_into(&mut TruncateWriter::new(dest, self.remaining))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TruncateWriter<W> {
|
||||||
|
dest: Option<W>,
|
||||||
|
remaining: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> TruncateWriter<W> {
|
||||||
|
fn new(dest: W, remaining: usize) -> Self {
|
||||||
|
TruncateWriter {
|
||||||
|
dest: Some(dest),
|
||||||
|
remaining,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: fmt::Write> fmt::Write for TruncateWriter<W> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
let Some(dest) = &mut self.dest else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let mut rem = self.remaining;
|
||||||
|
if rem >= s.len() {
|
||||||
|
dest.write_str(s)?;
|
||||||
|
self.remaining -= s.len();
|
||||||
|
} else {
|
||||||
|
if rem > 0 {
|
||||||
|
while !s.is_char_boundary(rem) {
|
||||||
|
rem += 1;
|
||||||
|
}
|
||||||
|
if rem == s.len() {
|
||||||
|
// Don't write "..." if the char bound extends to the end of string.
|
||||||
|
self.remaining = 0;
|
||||||
|
return dest.write_str(s);
|
||||||
|
}
|
||||||
|
dest.write_str(&s[..rem])?;
|
||||||
|
}
|
||||||
|
dest.write_str("...")?;
|
||||||
|
self.dest = None;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||||
|
match self.dest.is_some() {
|
||||||
|
true => self.write_str(c.encode_utf8(&mut [0; 4])),
|
||||||
|
false => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
|
||||||
|
match self.dest.is_some() {
|
||||||
|
true => fmt::write(self, args),
|
||||||
|
false => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Joins iterable into a string separated by provided argument
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{ example|join(", ") }}</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// example: &'a [&'a str],
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: &["foo", "bar", "bazz"] }.to_string(),
|
||||||
|
/// "<div>foo, bar, bazz</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn join<I, S>(input: I, separator: S) -> Result<JoinFilter<I, S>, Infallible>
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: fmt::Display,
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
Ok(JoinFilter(Cell::new(Some((input, separator)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result of the filter [`join()`].
|
||||||
|
///
|
||||||
|
/// ## Note
|
||||||
|
///
|
||||||
|
/// This struct implements [`fmt::Display`], but only produces a string once.
|
||||||
|
/// Any subsequent call to `.to_string()` will result in an empty string, because the iterator is
|
||||||
|
/// already consumed.
|
||||||
|
// The filter contains a [`Cell`], so we can modify iterator inside a method that takes `self` by
|
||||||
|
// reference: [`fmt::Display::fmt()`] normally has the contract that it will produce the same result
|
||||||
|
// in multiple invocations for the same object. We break this contract, because have to consume the
|
||||||
|
// iterator, unless we want to enforce `I: Clone`, nor do we want to "memorize" the result of the
|
||||||
|
// joined data.
|
||||||
|
pub struct JoinFilter<I, S>(Cell<Option<(I, S)>>);
|
||||||
|
|
||||||
|
impl<I, S> fmt::Display for JoinFilter<I, S>
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: fmt::Display,
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Some((iter, separator)) = self.0.take() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
for (idx, token) in iter.into_iter().enumerate() {
|
||||||
|
match idx {
|
||||||
|
0 => f.write_fmt(format_args!("{token}"))?,
|
||||||
|
_ => f.write_fmt(format_args!("{separator}{token}"))?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Centers the value in a field of a given width
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>-{{ example|center(5) }}-</div>
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// example: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { example: "a" }.to_string(),
|
||||||
|
/// "<div>- a -</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn center<T: fmt::Display>(src: T, width: usize) -> Result<Center<T>, Infallible> {
|
||||||
|
Ok(Center { src, width })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Center<T> {
|
||||||
|
src: T,
|
||||||
|
width: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Display> fmt::Display for Center<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.width < MAX_LEN {
|
||||||
|
write!(f, "{: ^1$}", self.src, self.width)
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", self.src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For a value of `±1` by default an empty string `""` is returned, otherwise `"s"`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ## With default arguments
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// I have {{dogs}} dog{{dogs|pluralize}} and {{cats}} cat{{cats|pluralize}}.
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Pets {
|
||||||
|
/// dogs: i8,
|
||||||
|
/// cats: i8,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Pets { dogs: 0, cats: 0 }.to_string(),
|
||||||
|
/// "I have 0 dogs and 0 cats."
|
||||||
|
/// );
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Pets { dogs: 1, cats: 1 }.to_string(),
|
||||||
|
/// "I have 1 dog and 1 cat."
|
||||||
|
/// );
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Pets { dogs: -1, cats: 99 }.to_string(),
|
||||||
|
/// "I have -1 dog and 99 cats."
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Overriding the singular case
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// I have {{dogs}} dog{{ dogs|pluralize("go") }}.
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Dog {
|
||||||
|
/// dogs: i8,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Dog { dogs: 0 }.to_string(),
|
||||||
|
/// "I have 0 dogs."
|
||||||
|
/// );
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Dog { dogs: 1 }.to_string(),
|
||||||
|
/// "I have 1 doggo."
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Overriding singular and plural cases
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// I have {{mice}} {{ mice|pluralize("mouse", "mice") }}.
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Mice {
|
||||||
|
/// mice: i8,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Mice { mice: 42 }.to_string(),
|
||||||
|
/// "I have 42 mice."
|
||||||
|
/// );
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Mice { mice: 1 }.to_string(),
|
||||||
|
/// "I have 1 mouse."
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Arguments get escaped
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// You are number {{ number|pluralize("<b>ONE</b>", number) }}!
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Number {
|
||||||
|
/// number: usize
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Number { number: 1 }.to_string(),
|
||||||
|
/// "You are number <b>ONE</b>!",
|
||||||
|
/// );
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Number { number: 9000 }.to_string(),
|
||||||
|
/// "You are number 9000!",
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn pluralize<C, S, P>(count: C, singular: S, plural: P) -> Result<Pluralize<S, P>, C::Error>
|
||||||
|
where
|
||||||
|
C: PluralizeCount,
|
||||||
|
{
|
||||||
|
match count.is_singular()? {
|
||||||
|
true => Ok(Pluralize::Singular(singular)),
|
||||||
|
false => Ok(Pluralize::Plural(plural)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An integer that can have the value `+1` and maybe `-1`.
|
||||||
|
pub trait PluralizeCount {
|
||||||
|
/// A possible error that can occur while checking the value.
|
||||||
|
type Error: Into<Error>;
|
||||||
|
|
||||||
|
/// Returns `true` if and only if the value is `±1`.
|
||||||
|
fn is_singular(&self) -> Result<bool, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
crate::impl_for_ref! {
|
||||||
|
impl PluralizeCount for T {
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_singular(&self) -> Result<bool, Self::Error> {
|
||||||
|
<T>::is_singular(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PluralizeCount for Pin<T>
|
||||||
|
where
|
||||||
|
T: Deref,
|
||||||
|
<T as Deref>::Target: PluralizeCount,
|
||||||
|
{
|
||||||
|
type Error = <<T as Deref>::Target as PluralizeCount>::Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_singular(&self) -> Result<bool, Self::Error> {
|
||||||
|
self.as_ref().get_ref().is_singular()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// implement `PluralizeCount` for unsigned integer types
|
||||||
|
macro_rules! impl_pluralize_for_unsigned_int {
|
||||||
|
($($ty:ty)*) => { $(
|
||||||
|
impl PluralizeCount for $ty {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_singular(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(*self == 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)* };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_pluralize_for_unsigned_int!(u8 u16 u32 u64 u128 usize);
|
||||||
|
|
||||||
|
/// implement `PluralizeCount` for signed integer types
|
||||||
|
macro_rules! impl_pluralize_for_signed_int {
|
||||||
|
($($ty:ty)*) => { $(
|
||||||
|
impl PluralizeCount for $ty {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_singular(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(*self == 1 || *self == -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)* };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_pluralize_for_signed_int!(i8 i16 i32 i64 i128 isize);
|
||||||
|
|
||||||
|
/// implement `PluralizeCount` for non-zero integer types
|
||||||
|
macro_rules! impl_pluralize_for_non_zero {
|
||||||
|
($($ty:ident)*) => { $(
|
||||||
|
impl PluralizeCount for core::num::$ty {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_singular(&self) -> Result<bool, Self::Error> {
|
||||||
|
self.get().is_singular()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)* };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_pluralize_for_non_zero! {
|
||||||
|
NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize
|
||||||
|
NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum Pluralize<S, P> {
|
||||||
|
Singular(S),
|
||||||
|
Plural(P),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: fmt::Display, P: fmt::Display> fmt::Display for Pluralize<S, P> {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Pluralize::Singular(value) => write!(f, "{value}"),
|
||||||
|
Pluralize::Plural(value) => write!(f, "{value}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: FastWritable, P: FastWritable> FastWritable for Pluralize<S, P> {
|
||||||
|
#[inline]
|
||||||
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
|
match self {
|
||||||
|
Pluralize::Singular(value) => value.write_into(dest),
|
||||||
|
Pluralize::Plural(value) => value.write_into(dest),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "alloc"))]
|
||||||
|
mod tests {
|
||||||
|
use alloc::string::{String, ToString};
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[test]
|
||||||
|
fn test_join() {
|
||||||
|
assert_eq!(
|
||||||
|
join((&["hello", "world"]).iter(), ", ")
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
"hello, world"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
join((&["hello"]).iter(), ", ").unwrap().to_string(),
|
||||||
|
"hello"
|
||||||
|
);
|
||||||
|
|
||||||
|
let empty: &[&str] = &[];
|
||||||
|
assert_eq!(join(empty.iter(), ", ").unwrap().to_string(), "");
|
||||||
|
|
||||||
|
let input: Vec<String> = alloc::vec!["foo".into(), "bar".into(), "bazz".into()];
|
||||||
|
assert_eq!(join(input.iter(), ":").unwrap().to_string(), "foo:bar:bazz");
|
||||||
|
|
||||||
|
let input: &[String] = &["foo".into(), "bar".into()];
|
||||||
|
assert_eq!(join(input.iter(), ":").unwrap().to_string(), "foo:bar");
|
||||||
|
|
||||||
|
let real: String = "blah".into();
|
||||||
|
let input: Vec<&str> = alloc::vec![&real];
|
||||||
|
assert_eq!(join(input.iter(), ";").unwrap().to_string(), "blah");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
join((&&&&&["foo", "bar"]).iter(), ", ")
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
"foo, bar"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_center() {
|
||||||
|
assert_eq!(center("f", 3).unwrap().to_string(), " f ".to_string());
|
||||||
|
assert_eq!(center("f", 4).unwrap().to_string(), " f ".to_string());
|
||||||
|
assert_eq!(center("foo", 1).unwrap().to_string(), "foo".to_string());
|
||||||
|
assert_eq!(
|
||||||
|
center("foo bar", 8).unwrap().to_string(),
|
||||||
|
"foo bar ".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
center("foo", 111_669_149_696).unwrap().to_string(),
|
||||||
|
"foo".to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,23 @@
|
|||||||
use std::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
use std::fmt::{self, Formatter, Write};
|
use core::fmt::{self, Formatter, Write};
|
||||||
use std::ops::Deref;
|
use core::ops::Deref;
|
||||||
use std::pin::Pin;
|
use core::pin::Pin;
|
||||||
use std::{borrow, str};
|
use core::str;
|
||||||
|
|
||||||
|
use crate::Values;
|
||||||
|
|
||||||
/// Marks a string (or other `Display` type) as safe
|
/// Marks a string (or other `Display` type) as safe
|
||||||
///
|
///
|
||||||
/// Use this if you want to allow markup in an expression, or if you know
|
/// Use this if you want to allow markup in an expression, or if you know
|
||||||
/// that the expression's contents don't need to be escaped.
|
/// that the expression's contents don't need to be escaped.
|
||||||
///
|
///
|
||||||
/// Rinja will automatically insert the first (`Escaper`) argument,
|
/// Askama will automatically insert the first (`Escaper`) argument,
|
||||||
/// so this filter only takes a single argument of any type that implements
|
/// so this filter only takes a single argument of any type that implements
|
||||||
/// `Display`.
|
/// `Display`.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "code-in-doc")] {
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
/// # use rinja::Template;
|
/// # use askama::Template;
|
||||||
/// /// ```jinja
|
/// /// ```jinja
|
||||||
/// /// <div>{{ example|safe }}</div>
|
/// /// <div>{{ example|safe }}</div>
|
||||||
/// /// ```
|
/// /// ```
|
||||||
@@ -39,7 +41,7 @@ pub fn safe<T, E>(text: T, escaper: E) -> Result<Safe<T>, Infallible> {
|
|||||||
|
|
||||||
/// Escapes strings according to the escape mode.
|
/// Escapes strings according to the escape mode.
|
||||||
///
|
///
|
||||||
/// Rinja will automatically insert the first (`Escaper`) argument,
|
/// Askama will automatically insert the first (`Escaper`) argument,
|
||||||
/// so this filter only takes a single argument of any type that implements
|
/// so this filter only takes a single argument of any type that implements
|
||||||
/// `Display`.
|
/// `Display`.
|
||||||
///
|
///
|
||||||
@@ -48,7 +50,7 @@ pub fn safe<T, E>(text: T, escaper: E) -> Result<Safe<T>, Infallible> {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "code-in-doc")] {
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
/// # use rinja::Template;
|
/// # use askama::Template;
|
||||||
/// /// ```jinja
|
/// /// ```jinja
|
||||||
/// /// <div>{{ example|escape }}</div>
|
/// /// <div>{{ example|escape }}</div>
|
||||||
/// /// ```
|
/// /// ```
|
||||||
@@ -80,7 +82,7 @@ impl<T: fmt::Display, E: Escaper> fmt::Display for EscapeDisplay<T, E> {
|
|||||||
|
|
||||||
impl<T: FastWritable, E: Escaper> FastWritable for EscapeDisplay<T, E> {
|
impl<T: FastWritable, E: Escaper> FastWritable for EscapeDisplay<T, E> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
self.0.write_into(&mut EscapeWriter(dest, self.1))
|
self.0.write_into(&mut EscapeWriter(dest, self.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +105,7 @@ impl<W: Write, E: Escaper> Write for EscapeWriter<W, E> {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "code-in-doc")] {
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
/// # use rinja::Template;
|
/// # use askama::Template;
|
||||||
/// /// ```jinja
|
/// /// ```jinja
|
||||||
/// /// <div>{{ example|e }}</div>
|
/// /// <div>{{ example|e }}</div>
|
||||||
/// /// ```
|
/// /// ```
|
||||||
@@ -136,13 +138,13 @@ pub struct Html;
|
|||||||
|
|
||||||
impl Escaper for Html {
|
impl Escaper for Html {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_escaped_str<W: Write>(&self, fmt: W, string: &str) -> fmt::Result {
|
fn write_escaped_str<W: Write>(&self, dest: W, string: &str) -> fmt::Result {
|
||||||
crate::html::write_escaped_str(fmt, string)
|
crate::html::write_escaped_str(dest, string)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_escaped_char<W: Write>(&self, fmt: W, c: char) -> fmt::Result {
|
fn write_escaped_char<W: Write>(&self, dest: W, c: char) -> fmt::Result {
|
||||||
crate::html::write_escaped_char(fmt, c)
|
crate::html::write_escaped_char(dest, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,13 +154,13 @@ pub struct Text;
|
|||||||
|
|
||||||
impl Escaper for Text {
|
impl Escaper for Text {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_escaped_str<W: Write>(&self, mut fmt: W, string: &str) -> fmt::Result {
|
fn write_escaped_str<W: Write>(&self, mut dest: W, string: &str) -> fmt::Result {
|
||||||
fmt.write_str(string)
|
dest.write_str(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_escaped_char<W: Write>(&self, mut fmt: W, c: char) -> fmt::Result {
|
fn write_escaped_char<W: Write>(&self, mut dest: W, c: char) -> fmt::Result {
|
||||||
fmt.write_char(c)
|
dest.write_char(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,28 +169,28 @@ impl Escaper for Text {
|
|||||||
/// E.g. in an [`Html`] context, any and all generated text can be used in HTML/XML text nodes and
|
/// E.g. in an [`Html`] context, any and all generated text can be used in HTML/XML text nodes and
|
||||||
/// attributes, without for for maliciously injected data.
|
/// attributes, without for for maliciously injected data.
|
||||||
pub trait Escaper: Copy {
|
pub trait Escaper: Copy {
|
||||||
/// Escaped the input string `string` into `fmt`
|
/// Escaped the input string `string` into `dest`
|
||||||
fn write_escaped_str<W: Write>(&self, fmt: W, string: &str) -> fmt::Result;
|
fn write_escaped_str<W: Write>(&self, dest: W, string: &str) -> fmt::Result;
|
||||||
|
|
||||||
/// Escaped the input char `c` into `fmt`
|
/// Escaped the input char `c` into `dest`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_escaped_char<W: Write>(&self, fmt: W, c: char) -> fmt::Result {
|
fn write_escaped_char<W: Write>(&self, dest: W, c: char) -> fmt::Result {
|
||||||
self.write_escaped_str(fmt, c.encode_utf8(&mut [0; 4]))
|
self.write_escaped_str(dest, c.encode_utf8(&mut [0; 4]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used internally by rinja to select the appropriate escaper
|
/// Used internally by askama to select the appropriate escaper
|
||||||
pub trait AutoEscape {
|
pub trait AutoEscape {
|
||||||
/// The wrapped or converted result type
|
/// The wrapped or converted result type
|
||||||
type Escaped: fmt::Display;
|
type Escaped: fmt::Display;
|
||||||
/// Early error testing for the input value, usually [`Infallible`]
|
/// Early error testing for the input value, usually [`Infallible`]
|
||||||
type Error: Into<crate::Error>;
|
type Error: Into<crate::Error>;
|
||||||
|
|
||||||
/// Used internally by rinja to select the appropriate escaper
|
/// Used internally by askama to select the appropriate escaper
|
||||||
fn rinja_auto_escape(&self) -> Result<Self::Escaped, Self::Error>;
|
fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used internally by rinja to select the appropriate escaper
|
/// Used internally by askama to select the appropriate escaper
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AutoEscaper<'a, T: ?Sized, E> {
|
pub struct AutoEscaper<'a, T: ?Sized, E> {
|
||||||
text: &'a T,
|
text: &'a T,
|
||||||
@@ -196,7 +198,7 @@ pub struct AutoEscaper<'a, T: ?Sized, E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ?Sized, E> AutoEscaper<'a, T, E> {
|
impl<'a, T: ?Sized, E> AutoEscaper<'a, T, E> {
|
||||||
/// Used internally by rinja to select the appropriate escaper
|
/// Used internally by askama to select the appropriate escaper
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(text: &'a T, escaper: E) -> Self {
|
pub fn new(text: &'a T, escaper: E) -> Self {
|
||||||
Self { text, escaper }
|
Self { text, escaper }
|
||||||
@@ -209,14 +211,14 @@ impl<'a, T: fmt::Display + ?Sized, E: Escaper> AutoEscape for &&AutoEscaper<'a,
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rinja_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
|
fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
|
||||||
Ok(EscapeDisplay(self.text, self.escaper))
|
Ok(EscapeDisplay(self.text, self.escaper))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Types that implement this marker trait don't need to be HTML escaped
|
/// Types that implement this marker trait don't need to be HTML escaped
|
||||||
///
|
///
|
||||||
/// Please note that this trait is only meant as speed-up helper. In some odd circumcises rinja
|
/// Please note that this trait is only meant as speed-up helper. In some odd circumcises askama
|
||||||
/// might still decide to HTML escape the input, so if this must not happen, then you need to use
|
/// might still decide to HTML escape the input, so if this must not happen, then you need to use
|
||||||
/// the [`|safe`](super::safe) filter to prevent the auto escaping.
|
/// the [`|safe`](super::safe) filter to prevent the auto escaping.
|
||||||
///
|
///
|
||||||
@@ -230,7 +232,7 @@ impl<'a, T: HtmlSafe + ?Sized> AutoEscape for &AutoEscaper<'a, T, Html> {
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rinja_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
|
fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
|
||||||
Ok(self.text)
|
Ok(self.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,7 +250,7 @@ impl<'a, T: HtmlSafe + ?Sized> AutoEscape for &AutoEscaper<'a, T, Html> {
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// mod filters {
|
/// mod filters {
|
||||||
/// use rinja::{filters::MaybeSafe, Result};
|
/// use askama::{filters::MaybeSafe, Result};
|
||||||
///
|
///
|
||||||
/// // Do not actually use this filter! It's an intentionally bad example.
|
/// // Do not actually use this filter! It's an intentionally bad example.
|
||||||
/// pub fn backdoor<T: std::fmt::Display>(s: T, enable: &bool) -> Result<MaybeSafe<T>> {
|
/// pub fn backdoor<T: std::fmt::Display>(s: T, enable: &bool) -> Result<MaybeSafe<T>> {
|
||||||
@@ -259,7 +261,7 @@ impl<'a, T: HtmlSafe + ?Sized> AutoEscape for &AutoEscaper<'a, T, Html> {
|
|||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[derive(rinja::Template)]
|
/// #[derive(askama::Template)]
|
||||||
/// #[template(
|
/// #[template(
|
||||||
/// source = "<div class='{{ klass|backdoor(enable_backdoor) }}'></div>",
|
/// source = "<div class='{{ klass|backdoor(enable_backdoor) }}'></div>",
|
||||||
/// ext = "html"
|
/// ext = "html"
|
||||||
@@ -301,7 +303,7 @@ const _: () = {
|
|||||||
// This is the fallback. The filter is not the last element of the filter chain.
|
// This is the fallback. The filter is not the last element of the filter chain.
|
||||||
impl<T: FastWritable> FastWritable for MaybeSafe<T> {
|
impl<T: FastWritable> FastWritable for MaybeSafe<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
let inner = match self {
|
let inner = match self {
|
||||||
MaybeSafe::Safe(inner) => inner,
|
MaybeSafe::Safe(inner) => inner,
|
||||||
MaybeSafe::NeedsEscaping(inner) => inner,
|
MaybeSafe::NeedsEscaping(inner) => inner,
|
||||||
@@ -318,7 +320,7 @@ const _: () = {
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rinja_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
|
fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
|
||||||
match self.text {
|
match self.text {
|
||||||
MaybeSafe::Safe(t) => Ok(Wrapped::Safe(t)),
|
MaybeSafe::Safe(t) => Ok(Wrapped::Safe(t)),
|
||||||
MaybeSafe::NeedsEscaping(t) => Ok(Wrapped::NeedsEscaping(t, self.escaper)),
|
MaybeSafe::NeedsEscaping(t) => Ok(Wrapped::NeedsEscaping(t, self.escaper)),
|
||||||
@@ -336,7 +338,7 @@ const _: () = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: FastWritable + ?Sized, E: Escaper> FastWritable for Wrapped<'_, T, E> {
|
impl<T: FastWritable + ?Sized, E: Escaper> FastWritable for Wrapped<'_, T, E> {
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
match *self {
|
match *self {
|
||||||
Wrapped::Safe(t) => t.write_into(dest),
|
Wrapped::Safe(t) => t.write_into(dest),
|
||||||
Wrapped::NeedsEscaping(t, e) => EscapeDisplay(t, e).write_into(dest),
|
Wrapped::NeedsEscaping(t, e) => EscapeDisplay(t, e).write_into(dest),
|
||||||
@@ -366,7 +368,7 @@ const _: () = {
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// mod filters {
|
/// mod filters {
|
||||||
/// use rinja::{filters::Safe, Result};
|
/// use askama::{filters::Safe, Result};
|
||||||
///
|
///
|
||||||
/// // Do not actually use this filter! It's an intentionally bad example.
|
/// // Do not actually use this filter! It's an intentionally bad example.
|
||||||
/// pub fn strip_except_apos(s: impl ToString) -> Result<Safe<String>> {
|
/// pub fn strip_except_apos(s: impl ToString) -> Result<Safe<String>> {
|
||||||
@@ -379,7 +381,7 @@ const _: () = {
|
|||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[derive(rinja::Template)]
|
/// #[derive(askama::Template)]
|
||||||
/// #[template(
|
/// #[template(
|
||||||
/// source = "<div class='{{ klass|strip_except_apos }}'></div>",
|
/// source = "<div class='{{ klass|strip_except_apos }}'></div>",
|
||||||
/// ext = "html"
|
/// ext = "html"
|
||||||
@@ -407,7 +409,7 @@ const _: () = {
|
|||||||
// This is the fallback. The filter is not the last element of the filter chain.
|
// This is the fallback. The filter is not the last element of the filter chain.
|
||||||
impl<T: FastWritable> FastWritable for Safe<T> {
|
impl<T: FastWritable> FastWritable for Safe<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
self.0.write_into(dest)
|
self.0.write_into(dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,7 +421,7 @@ const _: () = {
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rinja_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
|
fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
|
||||||
Ok(&self.text.0)
|
Ok(&self.text.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -460,54 +462,55 @@ mark_html_safe! {
|
|||||||
f32, f64,
|
f32, f64,
|
||||||
i8, i16, i32, i64, i128, isize,
|
i8, i16, i32, i64, i128, isize,
|
||||||
u8, u16, u32, u64, u128, usize,
|
u8, u16, u32, u64, u128, usize,
|
||||||
std::num::NonZeroI8, std::num::NonZeroI16, std::num::NonZeroI32,
|
core::num::NonZeroI8, core::num::NonZeroI16, core::num::NonZeroI32,
|
||||||
std::num::NonZeroI64, std::num::NonZeroI128, std::num::NonZeroIsize,
|
core::num::NonZeroI64, core::num::NonZeroI128, core::num::NonZeroIsize,
|
||||||
std::num::NonZeroU8, std::num::NonZeroU16, std::num::NonZeroU32,
|
core::num::NonZeroU8, core::num::NonZeroU16, core::num::NonZeroU32,
|
||||||
std::num::NonZeroU64, std::num::NonZeroU128, std::num::NonZeroUsize,
|
core::num::NonZeroU64, core::num::NonZeroU128, core::num::NonZeroUsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for &T {}
|
impl<T: HtmlSafe> HtmlSafe for core::num::Wrapping<T> {}
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for Box<T> {}
|
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for std::cell::Ref<'_, T> {}
|
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for std::cell::RefMut<'_, T> {}
|
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for std::rc::Rc<T> {}
|
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for std::pin::Pin<&T> {}
|
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for std::sync::Arc<T> {}
|
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for std::sync::MutexGuard<'_, T> {}
|
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for std::sync::RwLockReadGuard<'_, T> {}
|
|
||||||
impl<T: HtmlSafe + ?Sized> HtmlSafe for std::sync::RwLockWriteGuard<'_, T> {}
|
|
||||||
impl<T: HtmlSafe> HtmlSafe for std::num::Wrapping<T> {}
|
|
||||||
impl<T: fmt::Display> HtmlSafe for HtmlSafeOutput<T> {}
|
impl<T: fmt::Display> HtmlSafe for HtmlSafeOutput<T> {}
|
||||||
|
|
||||||
impl<T> HtmlSafe for borrow::Cow<'_, T>
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<T> HtmlSafe for alloc::borrow::Cow<'_, T>
|
||||||
where
|
where
|
||||||
T: HtmlSafe + borrow::ToOwned + ?Sized,
|
T: HtmlSafe + alloc::borrow::ToOwned + ?Sized,
|
||||||
T::Owned: HtmlSafe,
|
T::Owned: HtmlSafe,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used internally by rinja to select the appropriate [`write!()`] mechanism
|
crate::impl_for_ref! {
|
||||||
pub struct Writable<'a, S: ?Sized>(pub &'a S);
|
impl HtmlSafe for T {}
|
||||||
|
|
||||||
/// Used internally by rinja to select the appropriate [`write!()`] mechanism
|
|
||||||
pub trait WriteWritable {
|
|
||||||
/// Used internally by rinja to select the appropriate [`write!()`] mechanism
|
|
||||||
fn rinja_write<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used internally by rinja to speed up writing some types.
|
impl<T: HtmlSafe> HtmlSafe for Pin<T> {}
|
||||||
|
|
||||||
|
/// Used internally by askama to select the appropriate [`write!()`] mechanism
|
||||||
|
pub struct Writable<'a, S: ?Sized>(pub &'a S);
|
||||||
|
|
||||||
|
/// Used internally by askama to select the appropriate [`write!()`] mechanism
|
||||||
|
pub trait WriteWritable {
|
||||||
|
/// Used internally by askama to select the appropriate [`write!()`] mechanism
|
||||||
|
fn askama_write<W: fmt::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
dest: &mut W,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> crate::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used internally by askama to speed up writing some types.
|
||||||
///
|
///
|
||||||
/// Types implementing this trait can be written without needing to employ an [`fmt::Formatter`].
|
/// Types implementing this trait can be written without needing to employ an [`fmt::Formatter`].
|
||||||
pub trait FastWritable {
|
pub trait FastWritable {
|
||||||
/// Used internally by rinja to speed up writing some types.
|
/// Used internally by askama to speed up writing some types.
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result;
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _: () = {
|
const _: () = {
|
||||||
crate::impl_for_ref! {
|
crate::impl_for_ref! {
|
||||||
impl FastWritable for T {
|
impl FastWritable for T {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
<T>::write_into(self, dest)
|
<T>::write_into(self, dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,14 +522,15 @@ const _: () = {
|
|||||||
<T as Deref>::Target: FastWritable,
|
<T as Deref>::Target: FastWritable,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
self.as_ref().get_ref().write_into(dest)
|
self.as_ref().get_ref().write_into(dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: FastWritable + ToOwned> FastWritable for borrow::Cow<'_, T> {
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<T: FastWritable + alloc::borrow::ToOwned> FastWritable for alloc::borrow::Cow<'_, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
T::write_into(self.as_ref(), dest)
|
T::write_into(self.as_ref(), dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -536,8 +540,8 @@ const _: () = {
|
|||||||
($($ty:ty)*) => { $(
|
($($ty:ty)*) => { $(
|
||||||
impl FastWritable for $ty {
|
impl FastWritable for $ty {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
dest.write_str(itoa::Buffer::new().format(*self))
|
itoa::Buffer::new().format(*self).write_into(dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)* };
|
)* };
|
||||||
@@ -553,8 +557,8 @@ const _: () = {
|
|||||||
($($id:ident)*) => { $(
|
($($id:ident)*) => { $(
|
||||||
impl FastWritable for core::num::$id {
|
impl FastWritable for core::num::$id {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
dest.write_str(itoa::Buffer::new().format(self.get()))
|
self.get().write_into(dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)* };
|
)* };
|
||||||
@@ -567,61 +571,84 @@ const _: () = {
|
|||||||
|
|
||||||
impl FastWritable for str {
|
impl FastWritable for str {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
dest.write_str(self)
|
Ok(dest.write_str(self)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FastWritable for String {
|
#[cfg(feature = "alloc")]
|
||||||
|
impl FastWritable for alloc::string::String {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
dest.write_str(self)
|
self.as_str().write_into(dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FastWritable for bool {
|
impl FastWritable for bool {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
dest.write_str(match self {
|
Ok(dest.write_str(match self {
|
||||||
true => "true",
|
true => "true",
|
||||||
false => "false",
|
false => "false",
|
||||||
})
|
})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FastWritable for char {
|
impl FastWritable for char {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
dest.write_char(*self)
|
Ok(dest.write_char(*self)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FastWritable for fmt::Arguments<'_> {
|
impl FastWritable for fmt::Arguments<'_> {
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
match self.as_str() {
|
Ok(match self.as_str() {
|
||||||
Some(s) => dest.write_str(s),
|
Some(s) => dest.write_str(s),
|
||||||
None => dest.write_fmt(*self),
|
None => dest.write_fmt(*self),
|
||||||
}
|
}?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: FastWritable + ?Sized> WriteWritable for &Writable<'a, S> {
|
impl<S: crate::Template + ?Sized> WriteWritable for &Writable<'_, S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rinja_write<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn askama_write<W: fmt::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
dest: &mut W,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
self.0.render_into_with_values(dest, values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: FastWritable + ?Sized> WriteWritable for &&Writable<'_, S> {
|
||||||
|
#[inline]
|
||||||
|
fn askama_write<W: fmt::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
dest: &mut W,
|
||||||
|
_: &dyn Values,
|
||||||
|
) -> crate::Result<()> {
|
||||||
self.0.write_into(dest)
|
self.0.write_into(dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: fmt::Display + ?Sized> WriteWritable for &&Writable<'a, S> {
|
impl<S: fmt::Display + ?Sized> WriteWritable for &&&Writable<'_, S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rinja_write<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> fmt::Result {
|
fn askama_write<W: fmt::Write + ?Sized>(
|
||||||
write!(dest, "{}", self.0)
|
&self,
|
||||||
|
dest: &mut W,
|
||||||
|
_: &dyn Values,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
Ok(write!(dest, "{}", self.0)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
fn test_escape() {
|
fn test_escape() {
|
||||||
|
use alloc::string::ToString;
|
||||||
|
|
||||||
assert_eq!(escape("", Html).unwrap().to_string(), "");
|
assert_eq!(escape("", Html).unwrap().to_string(), "");
|
||||||
assert_eq!(escape("<&>", Html).unwrap().to_string(), "<&>");
|
assert_eq!(escape("<&>", Html).unwrap().to_string(), "<&>");
|
||||||
assert_eq!(escape("bla&", Html).unwrap().to_string(), "bla&");
|
assert_eq!(escape("bla&", Html).unwrap().to_string(), "bla&");
|
||||||
@@ -636,7 +663,10 @@ fn test_escape() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
fn test_html_safe_marker() {
|
fn test_html_safe_marker() {
|
||||||
|
use alloc::string::ToString;
|
||||||
|
|
||||||
struct Script1;
|
struct Script1;
|
||||||
struct Script2;
|
struct Script2;
|
||||||
|
|
||||||
@@ -656,14 +686,14 @@ fn test_html_safe_marker() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Script1, Html))
|
(&&AutoEscaper::new(&Script1, Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Script2, Html))
|
(&&AutoEscaper::new(&Script2, Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
@@ -671,14 +701,14 @@ fn test_html_safe_marker() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Script1, Text))
|
(&&AutoEscaper::new(&Script1, Text))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Script2, Text))
|
(&&AutoEscaper::new(&Script2, Text))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
@@ -686,14 +716,14 @@ fn test_html_safe_marker() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Safe(Script1), Html))
|
(&&AutoEscaper::new(&Safe(Script1), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Safe(Script2), Html))
|
(&&AutoEscaper::new(&Safe(Script2), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
@@ -701,14 +731,14 @@ fn test_html_safe_marker() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Unsafe(Script1), Html))
|
(&&AutoEscaper::new(&Unsafe(Script1), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Unsafe(Script2), Html))
|
(&&AutoEscaper::new(&Unsafe(Script2), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
@@ -716,28 +746,28 @@ fn test_html_safe_marker() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&MaybeSafe::Safe(Script1), Html))
|
(&&AutoEscaper::new(&MaybeSafe::Safe(Script1), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&MaybeSafe::Safe(Script2), Html))
|
(&&AutoEscaper::new(&MaybeSafe::Safe(Script2), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&MaybeSafe::NeedsEscaping(Script1), Html))
|
(&&AutoEscaper::new(&MaybeSafe::NeedsEscaping(Script1), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&MaybeSafe::NeedsEscaping(Script2), Html))
|
(&&AutoEscaper::new(&MaybeSafe::NeedsEscaping(Script2), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
@@ -745,14 +775,14 @@ fn test_html_safe_marker() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Safe(std::pin::Pin::new(&Script1)), Html))
|
(&&AutoEscaper::new(&Safe(std::pin::Pin::new(&Script1)), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&&AutoEscaper::new(&Safe(std::pin::Pin::new(&Script2)), Html))
|
(&&AutoEscaper::new(&Safe(std::pin::Pin::new(&Script2)), Html))
|
||||||
.rinja_auto_escape()
|
.askama_auto_escape()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
"<script>",
|
"<script>",
|
||||||
120
third_party/rust/askama/src/filters/humansize.rs
vendored
Normal file
120
third_party/rust/askama/src/filters/humansize.rs
vendored
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
use core::convert::Infallible;
|
||||||
|
use core::fmt;
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
|
use super::FastWritable;
|
||||||
|
use crate::ascii_str::{AsciiChar, AsciiStr};
|
||||||
|
|
||||||
|
/// Returns adequate string representation (in KB, ..) of number of bytes
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(
|
||||||
|
/// source = "Filesize: {{ size_in_bytes|filesizeformat }}.",
|
||||||
|
/// ext = "html"
|
||||||
|
/// )]
|
||||||
|
/// struct Example {
|
||||||
|
/// size_in_bytes: u64,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let tmpl = Example { size_in_bytes: 1_234_567 };
|
||||||
|
/// assert_eq!(tmpl.to_string(), "Filesize: 1.23 MB.");
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn filesizeformat(b: f32) -> Result<FilesizeFormatFilter, Infallible> {
|
||||||
|
Ok(FilesizeFormatFilter(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct FilesizeFormatFilter(f32);
|
||||||
|
|
||||||
|
impl fmt::Display for FilesizeFormatFilter {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
Ok(self.write_into(f)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FastWritable for FilesizeFormatFilter {
|
||||||
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
|
if self.0 < 1e3 {
|
||||||
|
(self.0 as u32).write_into(dest)?;
|
||||||
|
Ok(dest.write_str(" B")?)
|
||||||
|
} else if let Some((prefix, factor)) = SI_PREFIXES
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.find_map(|(prefix_factor, max)| (self.0 < max).then_some(prefix_factor))
|
||||||
|
{
|
||||||
|
// u32 is big enough to hold the number 999_999
|
||||||
|
let scaled = (self.0 * factor) as u32;
|
||||||
|
(scaled / 100).write_into(dest)?;
|
||||||
|
format_frac(&mut MaybeUninit::uninit(), prefix, scaled).write_into(dest)
|
||||||
|
} else {
|
||||||
|
too_big(self.0, dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Formats `buffer` to contain the decimal point, decimal places and unit
|
||||||
|
fn format_frac(buffer: &mut MaybeUninit<[AsciiChar; 8]>, prefix: AsciiChar, scaled: u32) -> &str {
|
||||||
|
// LLVM generates better byte code for register sized buffers
|
||||||
|
let buffer = buffer.write(AsciiStr::new_sized("..0 kB"));
|
||||||
|
buffer[4] = prefix;
|
||||||
|
|
||||||
|
let frac = scaled % 100;
|
||||||
|
let buffer = if frac == 0 {
|
||||||
|
&buffer[3..6]
|
||||||
|
} else {
|
||||||
|
let digits = AsciiChar::two_digits(frac);
|
||||||
|
if digits[1] == AsciiChar::new(b'0') {
|
||||||
|
// the decimal separator '.' is already contained in buffer[1]
|
||||||
|
buffer[2] = digits[0];
|
||||||
|
&buffer[1..6]
|
||||||
|
} else {
|
||||||
|
// the decimal separator '.' is already contained in buffer[0]
|
||||||
|
[buffer[1], buffer[2]] = digits;
|
||||||
|
&buffer[0..6]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AsciiStr::from_slice(buffer).as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
fn too_big<W: fmt::Write + ?Sized>(value: f32, dest: &mut W) -> crate::Result<()> {
|
||||||
|
// the value exceeds 999 QB, so we omit the decimal places
|
||||||
|
Ok(write!(dest, "{:.0} QB", value / 1e30)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `((si_prefix, factor), limit)`, the factor is offset by 10**2 to account for 2 decimal places
|
||||||
|
const SI_PREFIXES: &[((AsciiChar, f32), f32)] = &[
|
||||||
|
((AsciiChar::new(b'k'), 1e-1), 1e6),
|
||||||
|
((AsciiChar::new(b'M'), 1e-4), 1e9),
|
||||||
|
((AsciiChar::new(b'G'), 1e-7), 1e12),
|
||||||
|
((AsciiChar::new(b'T'), 1e-10), 1e15),
|
||||||
|
((AsciiChar::new(b'P'), 1e-13), 1e18),
|
||||||
|
((AsciiChar::new(b'E'), 1e-16), 1e21),
|
||||||
|
((AsciiChar::new(b'Z'), 1e-19), 1e24),
|
||||||
|
((AsciiChar::new(b'Y'), 1e-22), 1e27),
|
||||||
|
((AsciiChar::new(b'R'), 1e-25), 1e30),
|
||||||
|
((AsciiChar::new(b'Q'), 1e-28), 1e33),
|
||||||
|
];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn test_filesizeformat() {
|
||||||
|
use alloc::string::ToString;
|
||||||
|
|
||||||
|
assert_eq!(filesizeformat(0.).unwrap().to_string(), "0 B");
|
||||||
|
assert_eq!(filesizeformat(999.).unwrap().to_string(), "999 B");
|
||||||
|
assert_eq!(filesizeformat(1000.).unwrap().to_string(), "1 kB");
|
||||||
|
assert_eq!(filesizeformat(1023.).unwrap().to_string(), "1.02 kB");
|
||||||
|
assert_eq!(filesizeformat(1024.).unwrap().to_string(), "1.02 kB");
|
||||||
|
assert_eq!(filesizeformat(1100.).unwrap().to_string(), "1.1 kB");
|
||||||
|
assert_eq!(filesizeformat(9_499_014.).unwrap().to_string(), "9.49 MB");
|
||||||
|
assert_eq!(
|
||||||
|
filesizeformat(954_548_589.2).unwrap().to_string(),
|
||||||
|
"954.54 MB"
|
||||||
|
);
|
||||||
|
}
|
||||||
418
third_party/rust/askama/src/filters/json.rs
vendored
Normal file
418
third_party/rust/askama/src/filters/json.rs
vendored
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
use std::convert::Infallible;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::{fmt, io, str};
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_json::ser::{CompactFormatter, PrettyFormatter, Serializer};
|
||||||
|
|
||||||
|
use super::FastWritable;
|
||||||
|
use crate::ascii_str::{AsciiChar, AsciiStr};
|
||||||
|
|
||||||
|
/// Serialize to JSON (requires `json` feature)
|
||||||
|
///
|
||||||
|
/// The generated string does not contain ampersands `&`, chevrons `< >`, or apostrophes `'`.
|
||||||
|
/// To use it in a `<script>` you can combine it with the safe filter:
|
||||||
|
///
|
||||||
|
/// ``` html
|
||||||
|
/// <script>
|
||||||
|
/// var data = {{data|json|safe}};
|
||||||
|
/// </script>
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// To use it in HTML attributes, you can either use it in quotation marks `"{{data|json}}"` as is,
|
||||||
|
/// or in apostrophes with the (optional) safe filter `'{{data|json|safe}}'`.
|
||||||
|
/// In HTML texts the output of e.g. `<pre>{{data|json|safe}}</pre>` is safe, too.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div><li data-extra='{{data|json|safe}}'>Example</li></div>
|
||||||
|
/// /// ```
|
||||||
|
///
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// data: Vec<&'a str>,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { data: vec!["foo", "bar"] }.to_string(),
|
||||||
|
/// "<div><li data-extra='[\"foo\",\"bar\"]'>Example</li></div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn json(value: impl Serialize) -> Result<impl fmt::Display, Infallible> {
|
||||||
|
Ok(ToJson { value })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize to formatted/prettified JSON (requires `json` feature)
|
||||||
|
///
|
||||||
|
/// This filter works the same as [`json()`], but it formats the data for human readability.
|
||||||
|
/// It has an additional "indent" argument, which can either be an integer how many spaces to use
|
||||||
|
/// for indentation (capped to 16 characters), or a string (e.g. `"\u{A0}\u{A0}"` for two
|
||||||
|
/// non-breaking spaces).
|
||||||
|
///
|
||||||
|
/// ### Note
|
||||||
|
///
|
||||||
|
/// In askama's template language, this filter is called `|json`, too. The right function is
|
||||||
|
/// automatically selected depending on whether an `indent` argument was provided or not.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// <div>{{data|json(4)|safe}}</div>
|
||||||
|
/// /// ```
|
||||||
|
///
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// data: Vec<&'a str>,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { data: vec!["foo", "bar"] }.to_string(),
|
||||||
|
/// "<div>[
|
||||||
|
/// \"foo\",
|
||||||
|
/// \"bar\"
|
||||||
|
/// ]</div>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn json_pretty(
|
||||||
|
value: impl Serialize,
|
||||||
|
indent: impl AsIndent,
|
||||||
|
) -> Result<impl fmt::Display, Infallible> {
|
||||||
|
Ok(ToJsonPretty { value, indent })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct ToJson<S> {
|
||||||
|
value: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct ToJsonPretty<S, I> {
|
||||||
|
value: S,
|
||||||
|
indent: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A prefix usable for indenting [prettified JSON data](json_pretty)
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use askama::filters::AsIndent;
|
||||||
|
/// assert_eq!(4.as_indent(), " ");
|
||||||
|
/// assert_eq!(" -> ".as_indent(), " -> ");
|
||||||
|
/// ```
|
||||||
|
pub trait AsIndent {
|
||||||
|
/// Borrow `self` as prefix to use.
|
||||||
|
fn as_indent(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsIndent for str {
|
||||||
|
#[inline]
|
||||||
|
fn as_indent(&self) -> &str {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl AsIndent for alloc::string::String {
|
||||||
|
#[inline]
|
||||||
|
fn as_indent(&self) -> &str {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsIndent for usize {
|
||||||
|
#[inline]
|
||||||
|
fn as_indent(&self) -> &str {
|
||||||
|
spaces(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsIndent for std::num::Wrapping<usize> {
|
||||||
|
#[inline]
|
||||||
|
fn as_indent(&self) -> &str {
|
||||||
|
spaces(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsIndent for std::num::NonZeroUsize {
|
||||||
|
#[inline]
|
||||||
|
fn as_indent(&self) -> &str {
|
||||||
|
spaces(self.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spaces(width: usize) -> &'static str {
|
||||||
|
const MAX_SPACES: usize = 16;
|
||||||
|
const SPACES: &str = match str::from_utf8(&[b' '; MAX_SPACES]) {
|
||||||
|
Ok(spaces) => spaces,
|
||||||
|
Err(_) => panic!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
&SPACES[..width.min(SPACES.len())]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<T: AsIndent + alloc::borrow::ToOwned + ?Sized> AsIndent for alloc::borrow::Cow<'_, T> {
|
||||||
|
#[inline]
|
||||||
|
fn as_indent(&self) -> &str {
|
||||||
|
T::as_indent(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::impl_for_ref! {
|
||||||
|
impl AsIndent for T {
|
||||||
|
#[inline]
|
||||||
|
fn as_indent(&self) -> &str {
|
||||||
|
<T>::as_indent(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AsIndent for Pin<T>
|
||||||
|
where
|
||||||
|
T: Deref,
|
||||||
|
<T as Deref>::Target: AsIndent,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn as_indent(&self) -> &str {
|
||||||
|
self.as_ref().get_ref().as_indent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Serialize> FastWritable for ToJson<S> {
|
||||||
|
fn write_into<W: fmt::Write + ?Sized>(&self, f: &mut W) -> crate::Result<()> {
|
||||||
|
serialize(f, &self.value, CompactFormatter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Serialize> fmt::Display for ToJson<S> {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
Ok(self.write_into(f)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Serialize, I: AsIndent> FastWritable for ToJsonPretty<S, I> {
|
||||||
|
fn write_into<W: fmt::Write + ?Sized>(&self, f: &mut W) -> crate::Result<()> {
|
||||||
|
serialize(
|
||||||
|
f,
|
||||||
|
&self.value,
|
||||||
|
PrettyFormatter::with_indent(self.indent.as_indent().as_bytes()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Serialize, I: AsIndent> fmt::Display for ToJsonPretty<S, I> {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
Ok(self.write_into(f)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn serialize<S, W, F>(dest: &mut W, value: &S, formatter: F) -> Result<(), crate::Error>
|
||||||
|
where
|
||||||
|
S: Serialize + ?Sized,
|
||||||
|
W: fmt::Write + ?Sized,
|
||||||
|
F: serde_json::ser::Formatter,
|
||||||
|
{
|
||||||
|
/// The struct must only ever be used with the output of `serde_json`.
|
||||||
|
/// `serde_json` only produces UTF-8 strings in its `io::Write::write()` calls,
|
||||||
|
/// and `<JsonWriter as io::Write>` depends on this invariant.
|
||||||
|
struct JsonWriter<'a, W: fmt::Write + ?Sized>(&'a mut W);
|
||||||
|
|
||||||
|
impl<W: fmt::Write + ?Sized> io::Write for JsonWriter<'_, W> {
|
||||||
|
/// Invariant: must be passed valid UTF-8 slices
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
|
||||||
|
self.write_all(bytes)?;
|
||||||
|
Ok(bytes.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invariant: must be passed valid UTF-8 slices
|
||||||
|
fn write_all(&mut self, bytes: &[u8]) -> io::Result<()> {
|
||||||
|
// SAFETY: `serde_json` only writes valid strings
|
||||||
|
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||||
|
write_escaped_str(&mut *self.0, string)
|
||||||
|
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invariant: no character that needs escaping is multi-byte character when encoded in UTF-8;
|
||||||
|
/// that is true for characters in ASCII range.
|
||||||
|
#[inline]
|
||||||
|
fn write_escaped_str(dest: &mut (impl fmt::Write + ?Sized), src: &str) -> fmt::Result {
|
||||||
|
// This implementation reads one byte after another.
|
||||||
|
// It's not very fast, but should work well enough until portable SIMD gets stabilized.
|
||||||
|
|
||||||
|
let mut escaped_buf = ESCAPED_BUF_INIT;
|
||||||
|
let mut last = 0;
|
||||||
|
|
||||||
|
for (index, byte) in src.bytes().enumerate() {
|
||||||
|
if let Some(escaped) = get_escaped(byte) {
|
||||||
|
[escaped_buf[4], escaped_buf[5]] = escaped;
|
||||||
|
write_str_if_nonempty(dest, &src[last..index])?;
|
||||||
|
dest.write_str(AsciiStr::from_slice(&escaped_buf[..ESCAPED_BUF_LEN]))?;
|
||||||
|
last = index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write_str_if_nonempty(dest, &src[last..])
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut serializer = Serializer::with_formatter(JsonWriter(dest), formatter);
|
||||||
|
Ok(value.serialize(&mut serializer)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the decimal representation of the codepoint if the character needs HTML escaping.
|
||||||
|
#[inline]
|
||||||
|
fn get_escaped(byte: u8) -> Option<[AsciiChar; 2]> {
|
||||||
|
const _: () = assert!(CHAR_RANGE < 32);
|
||||||
|
|
||||||
|
if let MIN_CHAR..=MAX_CHAR = byte {
|
||||||
|
if (1u32 << (byte - MIN_CHAR)) & BITS != 0 {
|
||||||
|
return Some(TABLE.0[(byte - MIN_CHAR) as usize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_str_if_nonempty(output: &mut (impl fmt::Write + ?Sized), input: &str) -> fmt::Result {
|
||||||
|
if !input.is_empty() {
|
||||||
|
output.write_str(input)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List of characters that need HTML escaping, not necessarily in ordinal order.
|
||||||
|
const CHARS: &[u8] = br#"&'<>"#;
|
||||||
|
|
||||||
|
/// The character with the lowest codepoint that needs HTML escaping.
|
||||||
|
const MIN_CHAR: u8 = {
|
||||||
|
let mut v = u8::MAX;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < CHARS.len() {
|
||||||
|
if v > CHARS[i] {
|
||||||
|
v = CHARS[i];
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
v
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The character with the highest codepoint that needs HTML escaping.
|
||||||
|
const MAX_CHAR: u8 = {
|
||||||
|
let mut v = u8::MIN;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < CHARS.len() {
|
||||||
|
if v < CHARS[i] {
|
||||||
|
v = CHARS[i];
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
v
|
||||||
|
};
|
||||||
|
|
||||||
|
const BITS: u32 = {
|
||||||
|
let mut bits = 0;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < CHARS.len() {
|
||||||
|
bits |= 1 << (CHARS[i] - MIN_CHAR);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
bits
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Number of codepoints between the lowest and highest character that needs escaping, incl.
|
||||||
|
const CHAR_RANGE: usize = (MAX_CHAR - MIN_CHAR + 1) as usize;
|
||||||
|
|
||||||
|
#[repr(align(64))]
|
||||||
|
struct Table([[AsciiChar; 2]; CHAR_RANGE]);
|
||||||
|
|
||||||
|
/// For characters that need HTML escaping, the codepoint is formatted as decimal digits,
|
||||||
|
/// otherwise `b"\0\0"`. Starting at [`MIN_CHAR`].
|
||||||
|
const TABLE: &Table = &{
|
||||||
|
let mut table = Table([UNESCAPED; CHAR_RANGE]);
|
||||||
|
let mut i = 0;
|
||||||
|
while i < CHARS.len() {
|
||||||
|
let c = CHARS[i];
|
||||||
|
table.0[c as u32 as usize - MIN_CHAR as usize] = AsciiChar::two_hex_digits(c as u32);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
table
|
||||||
|
};
|
||||||
|
|
||||||
|
const UNESCAPED: [AsciiChar; 2] = AsciiStr::new_sized("");
|
||||||
|
|
||||||
|
const ESCAPED_BUF_INIT_UNPADDED: &str = "\\u00__";
|
||||||
|
// RATIONALE: llvm generates better code if the buffer is register sized
|
||||||
|
const ESCAPED_BUF_INIT: [AsciiChar; 8] = AsciiStr::new_sized(ESCAPED_BUF_INIT_UNPADDED);
|
||||||
|
const ESCAPED_BUF_LEN: usize = ESCAPED_BUF_INIT_UNPADDED.len();
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "alloc"))]
|
||||||
|
mod tests {
|
||||||
|
use alloc::string::ToString;
|
||||||
|
use alloc::vec;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ugly() {
|
||||||
|
assert_eq!(json(true).unwrap().to_string(), "true");
|
||||||
|
assert_eq!(json("foo").unwrap().to_string(), r#""foo""#);
|
||||||
|
assert_eq!(json(true).unwrap().to_string(), "true");
|
||||||
|
assert_eq!(json("foo").unwrap().to_string(), r#""foo""#);
|
||||||
|
assert_eq!(
|
||||||
|
json("<script>").unwrap().to_string(),
|
||||||
|
r#""\u003cscript\u003e""#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
json(vec!["foo", "bar"]).unwrap().to_string(),
|
||||||
|
r#"["foo","bar"]"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pretty() {
|
||||||
|
assert_eq!(json_pretty(true, "").unwrap().to_string(), "true");
|
||||||
|
assert_eq!(
|
||||||
|
json_pretty("<script>", "").unwrap().to_string(),
|
||||||
|
r#""\u003cscript\u003e""#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
json_pretty(vec!["foo", "bar"], "").unwrap().to_string(),
|
||||||
|
r#"[
|
||||||
|
"foo",
|
||||||
|
"bar"
|
||||||
|
]"#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
json_pretty(vec!["foo", "bar"], 2).unwrap().to_string(),
|
||||||
|
r#"[
|
||||||
|
"foo",
|
||||||
|
"bar"
|
||||||
|
]"#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
json_pretty(vec!["foo", "bar"], "————").unwrap().to_string(),
|
||||||
|
r#"[
|
||||||
|
————"foo",
|
||||||
|
————"bar"
|
||||||
|
]"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,29 +7,34 @@
|
|||||||
//!
|
//!
|
||||||
//! All **result types of any filter function** in this module is **subject to change** at any
|
//! All **result types of any filter function** in this module is **subject to change** at any
|
||||||
//! point, and is **not indicated by as semver breaking** version bump.
|
//! point, and is **not indicated by as semver breaking** version bump.
|
||||||
//! The traits [`AutoEscape`] and [`WriteWritable`] are used by [`rinja_derive`]'s generated code
|
//! The traits [`AutoEscape`] and [`WriteWritable`] are used by [`askama_derive`]'s generated code
|
||||||
//! to work with all compatible types.
|
//! to work with all compatible types.
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
mod alloc;
|
||||||
mod builtin;
|
mod builtin;
|
||||||
mod escape;
|
mod escape;
|
||||||
#[cfg(feature = "humansize")]
|
|
||||||
mod humansize;
|
mod humansize;
|
||||||
#[cfg(feature = "serde_json")]
|
#[cfg(feature = "serde_json")]
|
||||||
mod json;
|
mod json;
|
||||||
#[cfg(feature = "urlencode")]
|
#[cfg(feature = "urlencode")]
|
||||||
mod urlencode;
|
mod urlencode;
|
||||||
|
|
||||||
pub use self::builtin::{
|
#[cfg(feature = "alloc")]
|
||||||
PluralizeCount, capitalize, center, fmt, format, indent, join, linebreaks, linebreaksbr, lower,
|
pub use self::alloc::{
|
||||||
lowercase, paragraphbreaks, pluralize, title, trim, truncate, upper, uppercase, wordcount,
|
capitalize, fmt, format, indent, linebreaks, linebreaksbr, lower, lowercase, paragraphbreaks,
|
||||||
|
title, trim, upper, uppercase, wordcount,
|
||||||
};
|
};
|
||||||
|
pub use self::builtin::{PluralizeCount, center, join, pluralize, truncate};
|
||||||
pub use self::escape::{
|
pub use self::escape::{
|
||||||
AutoEscape, AutoEscaper, Escaper, FastWritable, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe,
|
AutoEscape, AutoEscaper, Escaper, FastWritable, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe,
|
||||||
Safe, Text, Unsafe, Writable, WriteWritable, e, escape, safe,
|
Safe, Text, Unsafe, Writable, WriteWritable, e, escape, safe,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "humansize")]
|
|
||||||
pub use self::humansize::filesizeformat;
|
pub use self::humansize::filesizeformat;
|
||||||
#[cfg(feature = "serde_json")]
|
#[cfg(feature = "serde_json")]
|
||||||
pub use self::json::{AsIndent, json, json_pretty};
|
pub use self::json::{AsIndent, json, json_pretty};
|
||||||
#[cfg(feature = "urlencode")]
|
#[cfg(feature = "urlencode")]
|
||||||
pub use self::urlencode::{urlencode, urlencode_strict};
|
pub use self::urlencode::{urlencode, urlencode_strict};
|
||||||
|
|
||||||
|
// MAX_LEN is maximum allowed length for filters.
|
||||||
|
const MAX_LEN: usize = 10_000;
|
||||||
@@ -40,7 +40,7 @@ const URLENCODE_SET: &AsciiSet = &URLENCODE_STRICT_SET.remove(b'/');
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "code-in-doc")] {
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
/// # use rinja::Template;
|
/// # use askama::Template;
|
||||||
/// /// ```jinja
|
/// /// ```jinja
|
||||||
/// /// <div>{{ example|urlencode }}</div>
|
/// /// <div>{{ example|urlencode }}</div>
|
||||||
/// /// ```
|
/// /// ```
|
||||||
@@ -78,7 +78,7 @@ pub fn urlencode<T>(s: T) -> Result<HtmlSafeOutput<UrlencodeFilter<T>>, Infallib
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "code-in-doc")] {
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
/// # use rinja::Template;
|
/// # use askama::Template;
|
||||||
/// /// ```jinja
|
/// /// ```jinja
|
||||||
/// /// <a href='{{ example|urlencode_strict }}'>Example</a>
|
/// /// <a href='{{ example|urlencode_strict }}'>Example</a>
|
||||||
/// /// ```
|
/// /// ```
|
||||||
@@ -110,7 +110,7 @@ impl<T: fmt::Display> fmt::Display for UrlencodeFilter<T> {
|
|||||||
|
|
||||||
impl<T: FastWritable> FastWritable for UrlencodeFilter<T> {
|
impl<T: FastWritable> FastWritable for UrlencodeFilter<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, f: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, f: &mut W) -> crate::Result<()> {
|
||||||
self.0.write_into(&mut UrlencodeWriter(f, self.1))
|
self.0.write_into(&mut UrlencodeWriter(f, self.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,14 +118,19 @@ impl<T: FastWritable> FastWritable for UrlencodeFilter<T> {
|
|||||||
struct UrlencodeWriter<W>(W, &'static AsciiSet);
|
struct UrlencodeWriter<W>(W, &'static AsciiSet);
|
||||||
|
|
||||||
impl<W: fmt::Write> fmt::Write for UrlencodeWriter<W> {
|
impl<W: fmt::Write> fmt::Write for UrlencodeWriter<W> {
|
||||||
#[inline]
|
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
write!(self.0, "{}", utf8_percent_encode(s, self.1))
|
for s in utf8_percent_encode(s, self.1) {
|
||||||
|
self.0.write_str(s)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
fn test_urlencoding() {
|
fn test_urlencoding() {
|
||||||
|
use alloc::string::ToString;
|
||||||
|
|
||||||
// Unreserved (https://tools.ietf.org/html/rfc3986.html#section-2.3)
|
// Unreserved (https://tools.ietf.org/html/rfc3986.html#section-2.3)
|
||||||
// alpha / digit
|
// alpha / digit
|
||||||
assert_eq!(urlencode("AZaz09").unwrap().to_string(), "AZaz09");
|
assert_eq!(urlencode("AZaz09").unwrap().to_string(), "AZaz09");
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
use std::cell::Cell;
|
|
||||||
use std::fmt;
|
|
||||||
use std::iter::{Enumerate, Peekable};
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
// The re-exports are used in the generated code for macro hygiene. Even if the paths `::core` or
|
// The re-exports are used in the generated code for macro hygiene. Even if the paths `::core` or
|
||||||
// `::std` are shadowed, the generated code will still be able to access the crates.
|
// `::std` are shadowed, the generated code will still be able to access the crates.
|
||||||
pub use {core, std};
|
#[cfg(feature = "alloc")]
|
||||||
|
pub extern crate alloc;
|
||||||
|
pub extern crate core;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub extern crate std;
|
||||||
|
|
||||||
|
use core::cell::Cell;
|
||||||
|
use core::fmt;
|
||||||
|
use core::iter::{Enumerate, Peekable};
|
||||||
|
use core::ops::Deref;
|
||||||
|
use core::pin::Pin;
|
||||||
|
|
||||||
|
pub use crate::error::{ErrorMarker, ResultConverter};
|
||||||
use crate::filters::FastWritable;
|
use crate::filters::FastWritable;
|
||||||
|
pub use crate::values::get_value;
|
||||||
|
|
||||||
pub struct TemplateLoop<I>
|
pub struct TemplateLoop<I>
|
||||||
where
|
where
|
||||||
@@ -38,11 +44,14 @@ where
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<(<I as Iterator>::Item, LoopItem)> {
|
fn next(&mut self) -> Option<(<I as Iterator>::Item, LoopItem)> {
|
||||||
self.iter.next().map(|(index, item)| {
|
self.iter.next().map(|(index, item)| {
|
||||||
(item, LoopItem {
|
(
|
||||||
|
item,
|
||||||
|
LoopItem {
|
||||||
index,
|
index,
|
||||||
first: index == 0,
|
first: index == 0,
|
||||||
last: self.iter.peek().is_none(),
|
last: self.iter.peek().is_none(),
|
||||||
})
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,8 +107,9 @@ pub fn get_primitive_value<T: PrimitiveType>(value: T) -> T::Value {
|
|||||||
value.get()
|
value.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type that is, references, or wraps a [primitive][std::primitive] type
|
||||||
pub trait PrimitiveType {
|
pub trait PrimitiveType {
|
||||||
type Value;
|
type Value: Copy + Send + Sync + 'static;
|
||||||
|
|
||||||
fn get(&self) -> Self::Value;
|
fn get(&self) -> Self::Value;
|
||||||
}
|
}
|
||||||
@@ -152,17 +162,18 @@ where
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use std::cell::Cell;
|
/// # use std::cell::Cell;
|
||||||
|
/// # use std::num::{NonZeroI16, Saturating};
|
||||||
/// # use std::rc::Rc;
|
/// # use std::rc::Rc;
|
||||||
/// # use std::pin::Pin;
|
/// # use std::pin::Pin;
|
||||||
/// # use rinja::Template;
|
/// # use askama::Template;
|
||||||
/// #[derive(Template)]
|
/// #[derive(Template)]
|
||||||
/// #[template(ext = "txt", source = "{{ value as u16 }}")]
|
/// #[template(ext = "txt", source = "{{ value as u16 }}")]
|
||||||
/// struct Test<'a> {
|
/// struct Test<'a> {
|
||||||
/// value: &'a Pin<Rc<Cell<i16>>>
|
/// value: &'a Pin<Rc<Cell<Saturating<NonZeroI16>>>>
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// Test { value: &Rc::pin(Cell::new(-1)) }.to_string(),
|
/// Test { value: &Rc::pin(Cell::new(Saturating(NonZeroI16::new(-1).unwrap()))) }.to_string(),
|
||||||
/// "65535",
|
/// "65535",
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
@@ -175,6 +186,52 @@ impl<T: PrimitiveType + Copy> PrimitiveType for Cell<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: PrimitiveType> PrimitiveType for core::num::Wrapping<T> {
|
||||||
|
type Value = T::Value;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get(&self) -> Self::Value {
|
||||||
|
self.0.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PrimitiveType> PrimitiveType for core::num::Saturating<T> {
|
||||||
|
type Value = T::Value;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get(&self) -> Self::Value {
|
||||||
|
self.0.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! primitize_nz {
|
||||||
|
($($nz:ty => $bare:ident,)+) => { $(
|
||||||
|
impl PrimitiveType for $nz {
|
||||||
|
type Value = $bare;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get(&self) -> Self::Value {
|
||||||
|
<$nz>::get(*self).get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+ };
|
||||||
|
}
|
||||||
|
|
||||||
|
primitize_nz! {
|
||||||
|
core::num::NonZeroI8 => i8,
|
||||||
|
core::num::NonZeroI16 => i16,
|
||||||
|
core::num::NonZeroI32 => i32,
|
||||||
|
core::num::NonZeroI64 => i64,
|
||||||
|
core::num::NonZeroI128 => i128,
|
||||||
|
core::num::NonZeroIsize => isize,
|
||||||
|
core::num::NonZeroU8 => u8,
|
||||||
|
core::num::NonZeroU16 => u16,
|
||||||
|
core::num::NonZeroU32 => u32,
|
||||||
|
core::num::NonZeroU64 => u64,
|
||||||
|
core::num::NonZeroU128 => u128,
|
||||||
|
core::num::NonZeroUsize => usize,
|
||||||
|
}
|
||||||
|
|
||||||
/// An empty element, so nothing will be written.
|
/// An empty element, so nothing will be written.
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Empty;
|
pub struct Empty;
|
||||||
@@ -188,7 +245,7 @@ impl fmt::Display for Empty {
|
|||||||
|
|
||||||
impl FastWritable for Empty {
|
impl FastWritable for Empty {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_into<W: fmt::Write + ?Sized>(&self, _: &mut W) -> fmt::Result {
|
fn write_into<W: fmt::Write + ?Sized>(&self, _: &mut W) -> crate::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,3 +254,29 @@ impl FastWritable for Empty {
|
|||||||
pub fn as_bool<T: PrimitiveType<Value = bool>>(value: T) -> bool {
|
pub fn as_bool<T: PrimitiveType<Value = bool>>(value: T) -> bool {
|
||||||
value.get()
|
value.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Concat<L, R>(pub L, pub R);
|
||||||
|
|
||||||
|
impl<L: fmt::Display, R: fmt::Display> fmt::Display for Concat<L, R> {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)?;
|
||||||
|
self.1.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: FastWritable, R: FastWritable> FastWritable for Concat<L, R> {
|
||||||
|
#[inline]
|
||||||
|
fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||||
|
self.0.write_into(dest)?;
|
||||||
|
self.1.write_into(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EnumVariantTemplate {
|
||||||
|
fn render_into_with_values<W: fmt::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
writer: &mut W,
|
||||||
|
values: &dyn crate::Values,
|
||||||
|
) -> crate::Result<()>;
|
||||||
|
}
|
||||||
@@ -1,4 +1,11 @@
|
|||||||
use std::{fmt, str};
|
// The file is shared across many crates, not all have this feature.
|
||||||
|
// If they don't then the tests won't be compiled in, but that's OK, because they are executed at
|
||||||
|
// least in the crate `askama`. There's no need to run the test multiple times.
|
||||||
|
#![allow(unexpected_cfgs)]
|
||||||
|
|
||||||
|
use core::{fmt, str};
|
||||||
|
|
||||||
|
use crate::ascii_str::{AsciiChar, AsciiStr};
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn write_escaped_str(mut dest: impl fmt::Write, src: &str) -> fmt::Result {
|
pub(crate) fn write_escaped_str(mut dest: impl fmt::Write, src: &str) -> fmt::Result {
|
||||||
@@ -12,8 +19,7 @@ pub(crate) fn write_escaped_str(mut dest: impl fmt::Write, src: &str) -> fmt::Re
|
|||||||
if let Some(escaped) = get_escaped(byte) {
|
if let Some(escaped) = get_escaped(byte) {
|
||||||
[escaped_buf[2], escaped_buf[3]] = escaped;
|
[escaped_buf[2], escaped_buf[3]] = escaped;
|
||||||
write_str_if_nonempty(&mut dest, &src[last..index])?;
|
write_str_if_nonempty(&mut dest, &src[last..index])?;
|
||||||
// SAFETY: the content of `escaped_buf` is pure ASCII
|
dest.write_str(AsciiStr::from_slice(&escaped_buf[..ESCAPED_BUF_LEN]))?;
|
||||||
dest.write_str(unsafe { str::from_utf8_unchecked(&escaped_buf[..ESCAPED_BUF_LEN]) })?;
|
|
||||||
last = index + 1;
|
last = index + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,8 +33,7 @@ pub(crate) fn write_escaped_char(mut dest: impl fmt::Write, c: char) -> fmt::Res
|
|||||||
} else if let Some(escaped) = get_escaped(c as u8) {
|
} else if let Some(escaped) = get_escaped(c as u8) {
|
||||||
let mut escaped_buf = ESCAPED_BUF_INIT;
|
let mut escaped_buf = ESCAPED_BUF_INIT;
|
||||||
[escaped_buf[2], escaped_buf[3]] = escaped;
|
[escaped_buf[2], escaped_buf[3]] = escaped;
|
||||||
// SAFETY: the content of `escaped_buf` is pure ASCII
|
dest.write_str(AsciiStr::from_slice(&escaped_buf[..ESCAPED_BUF_LEN]))
|
||||||
dest.write_str(unsafe { str::from_utf8_unchecked(&escaped_buf[..ESCAPED_BUF_LEN]) })
|
|
||||||
} else {
|
} else {
|
||||||
// RATIONALE: `write_char(c)` gets optimized if it is known that `c.is_ascii()`
|
// RATIONALE: `write_char(c)` gets optimized if it is known that `c.is_ascii()`
|
||||||
dest.write_char(c)
|
dest.write_char(c)
|
||||||
@@ -36,14 +41,13 @@ pub(crate) fn write_escaped_char(mut dest: impl fmt::Write, c: char) -> fmt::Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the decimal representation of the codepoint if the character needs HTML escaping.
|
/// Returns the decimal representation of the codepoint if the character needs HTML escaping.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn get_escaped(byte: u8) -> Option<[u8; 2]> {
|
fn get_escaped(byte: u8) -> Option<[AsciiChar; 2]> {
|
||||||
match byte {
|
if let MIN_CHAR..=MAX_CHAR = byte {
|
||||||
MIN_CHAR..=MAX_CHAR => match TABLE.lookup[(byte - MIN_CHAR) as usize] {
|
let entry = TABLE.0[(byte - MIN_CHAR) as usize];
|
||||||
0 => None,
|
(entry != UNESCAPED).then_some(entry)
|
||||||
escaped => Some(escaped.to_ne_bytes()),
|
} else {
|
||||||
},
|
None
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,36 +92,35 @@ const MAX_CHAR: u8 = {
|
|||||||
/// Number of codepoints between the lowest and highest character that needs escaping, incl.
|
/// Number of codepoints between the lowest and highest character that needs escaping, incl.
|
||||||
const CHAR_RANGE: usize = (MAX_CHAR - MIN_CHAR + 1) as usize;
|
const CHAR_RANGE: usize = (MAX_CHAR - MIN_CHAR + 1) as usize;
|
||||||
|
|
||||||
struct Table {
|
#[repr(align(64))]
|
||||||
_align: [usize; 0],
|
struct Table([[AsciiChar; 2]; CHAR_RANGE]);
|
||||||
lookup: [u16; CHAR_RANGE],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For characters that need HTML escaping, the codepoint is formatted as decimal digits,
|
/// For characters that need HTML escaping, the codepoint is formatted as decimal digits,
|
||||||
/// otherwise `b"\0\0"`. Starting at [`MIN_CHAR`].
|
/// otherwise `b"\0\0"`. Starting at [`MIN_CHAR`].
|
||||||
const TABLE: Table = {
|
const TABLE: &Table = &{
|
||||||
let mut table = Table {
|
let mut table = Table([UNESCAPED; CHAR_RANGE]);
|
||||||
_align: [],
|
|
||||||
lookup: [0; CHAR_RANGE],
|
|
||||||
};
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < CHARS.len() {
|
while i < CHARS.len() {
|
||||||
let c = CHARS[i];
|
let c = CHARS[i];
|
||||||
let h = c / 10 + b'0';
|
table.0[c as u32 as usize - MIN_CHAR as usize] = AsciiChar::two_digits(c as u32);
|
||||||
let l = c % 10 + b'0';
|
|
||||||
table.lookup[(c - MIN_CHAR) as usize] = u16::from_ne_bytes([h, l]);
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
table
|
table
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const UNESCAPED: [AsciiChar; 2] = AsciiStr::new_sized("");
|
||||||
|
|
||||||
|
const ESCAPED_BUF_INIT_UNPADDED: &str = "&#__;";
|
||||||
// RATIONALE: llvm generates better code if the buffer is register sized
|
// RATIONALE: llvm generates better code if the buffer is register sized
|
||||||
const ESCAPED_BUF_INIT: [u8; 8] = *b"&#__;\0\0\0";
|
const ESCAPED_BUF_INIT: [AsciiChar; 8] = AsciiStr::new_sized(ESCAPED_BUF_INIT_UNPADDED);
|
||||||
const ESCAPED_BUF_LEN: usize = b"&#__;".len();
|
const ESCAPED_BUF_LEN: usize = ESCAPED_BUF_INIT_UNPADDED.len();
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
fn test_simple_html_string_escaping() {
|
fn test_simple_html_string_escaping() {
|
||||||
let mut buf = String::new();
|
extern crate alloc;
|
||||||
|
|
||||||
|
let mut buf = alloc::string::String::new();
|
||||||
write_escaped_str(&mut buf, "<script>").unwrap();
|
write_escaped_str(&mut buf, "<script>").unwrap();
|
||||||
assert_eq!(buf, "<script>");
|
assert_eq!(buf, "<script>");
|
||||||
|
|
||||||
430
third_party/rust/askama/src/lib.rs
vendored
Normal file
430
third_party/rust/askama/src/lib.rs
vendored
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
//! [](https://crates.io/crates/askama)
|
||||||
|
//! [](https://github.com/askama-rs/askama/actions/workflows/rust.yml)
|
||||||
|
//! [](https://askama.readthedocs.io/)
|
||||||
|
//! [](https://docs.rs/askama/)
|
||||||
|
//!
|
||||||
|
//! Askama implements a type-safe compiler for Jinja-like templates.
|
||||||
|
//! It lets you write templates in a Jinja-like syntax,
|
||||||
|
//! which are linked to a `struct` or an `enum` defining the template context.
|
||||||
|
//! This is done using a custom derive implementation (implemented
|
||||||
|
//! in [`askama_derive`](https://crates.io/crates/askama_derive)).
|
||||||
|
//!
|
||||||
|
//! For feature highlights and a quick start, please review the
|
||||||
|
//! [README](https://github.com/askama-rs/askama/blob/master/README.md).
|
||||||
|
//!
|
||||||
|
//! You can find the documentation about our syntax, features, configuration in our book:
|
||||||
|
//! [askama.readthedocs.io](https://askama.readthedocs.io/).
|
||||||
|
//!
|
||||||
|
//! # Creating Askama templates
|
||||||
|
//!
|
||||||
|
//! The main feature of Askama is the [`Template`] derive macro
|
||||||
|
//! which reads your template code, so your `struct` or `enum` can implement
|
||||||
|
//! the [`Template`] trait and [`Display`][std::fmt::Display], type-safe and fast:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # use askama::Template;
|
||||||
|
//! #[derive(Template)]
|
||||||
|
//! #[template(
|
||||||
|
//! ext = "html",
|
||||||
|
//! source = "<p>© {{ year }} {{ enterprise|upper }}</p>"
|
||||||
|
//! )]
|
||||||
|
//! struct Footer<'a> {
|
||||||
|
//! year: u16,
|
||||||
|
//! enterprise: &'a str,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Footer { year: 2025, enterprise: "<em>Askama</em> developers" }.to_string(),
|
||||||
|
//! "<p>© 2025 <EM>ASKAMA</EM> DEVELOPERS</p>",
|
||||||
|
//! );
|
||||||
|
//! // In here you see can Askama's auto-escaping. You, the developer,
|
||||||
|
//! // can easily disable the auto-escaping with the `|safe` filter,
|
||||||
|
//! // but a malicious user cannot insert e.g. HTML scripts this way.
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! An Askama template is a `struct` or `enum` definition which provides the template
|
||||||
|
//! context combined with a UTF-8 encoded text file (or inline source).
|
||||||
|
//! Askama can be used to generate any kind of text-based format.
|
||||||
|
//! The template file's extension may be used to provide content type hints.
|
||||||
|
//!
|
||||||
|
//! A template consists of **text contents**, which are passed through as-is,
|
||||||
|
//! **expressions**, which get replaced with content while being rendered, and
|
||||||
|
//! **tags**, which control the template's logic.
|
||||||
|
//! The template syntax is very similar to [Jinja](http://jinja.pocoo.org/),
|
||||||
|
//! as well as Jinja-derivatives like [Twig](https://twig.symfony.com/) or
|
||||||
|
//! [Tera](https://github.com/Keats/tera).
|
||||||
|
|
||||||
|
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||||
|
#![deny(elided_lifetimes_in_paths)]
|
||||||
|
#![deny(unreachable_pub)]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
extern crate alloc;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
extern crate std;
|
||||||
|
|
||||||
|
mod ascii_str;
|
||||||
|
mod error;
|
||||||
|
pub mod filters;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod helpers;
|
||||||
|
mod html;
|
||||||
|
mod values;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use alloc::string::String;
|
||||||
|
use core::fmt;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
#[cfg(feature = "derive")]
|
||||||
|
pub use askama_derive::Template;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate as shared;
|
||||||
|
pub use crate::error::{Error, Result};
|
||||||
|
pub use crate::helpers::PrimitiveType;
|
||||||
|
pub use crate::values::{NO_VALUES, Value, Values, get_value};
|
||||||
|
|
||||||
|
/// Main `Template` trait; implementations are generally derived
|
||||||
|
///
|
||||||
|
/// If you need an object-safe template, use [`DynTemplate`].
|
||||||
|
///
|
||||||
|
/// ## Rendering performance
|
||||||
|
///
|
||||||
|
/// When rendering a askama template, you should prefer the methods
|
||||||
|
///
|
||||||
|
/// * [`.render()`][Template::render] (to render the content into a new string),
|
||||||
|
/// * [`.render_into()`][Template::render_into] (to render the content into an [`fmt::Write`]
|
||||||
|
/// object, e.g. [`String`]) or
|
||||||
|
/// * [`.write_into()`][Template::write_into] (to render the content into an [`io::Write`] object,
|
||||||
|
/// e.g. [`Vec<u8>`][alloc::vec::Vec])
|
||||||
|
///
|
||||||
|
/// over [`.to_string()`][std::string::ToString::to_string] or [`format!()`][alloc::format].
|
||||||
|
/// While `.to_string()` and `format!()` give you the same result, they generally perform much worse
|
||||||
|
/// than askama's own methods, because [`fmt::Write`] uses [dynamic methods calls] instead of
|
||||||
|
/// monomorphised code. On average, expect `.to_string()` to be 100% to 200% slower than
|
||||||
|
/// `.render()`.
|
||||||
|
///
|
||||||
|
/// [dynamic methods calls]: <https://doc.rust-lang.org/stable/std/keyword.dyn.html>
|
||||||
|
pub trait Template: fmt::Display + filters::FastWritable {
|
||||||
|
/// Helper method which allocates a new `String` and renders into it.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn render(&self) -> Result<String> {
|
||||||
|
self.render_with_values(NO_VALUES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method which allocates a new `String` and renders into it with provided [`Values`].
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn render_with_values(&self, values: &dyn Values) -> Result<String> {
|
||||||
|
let mut buf = String::new();
|
||||||
|
let _ = buf.try_reserve(Self::SIZE_HINT);
|
||||||
|
self.render_into_with_values(&mut buf, values)?;
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the template to the given `writer` fmt buffer.
|
||||||
|
#[inline]
|
||||||
|
fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
|
||||||
|
self.render_into_with_values(writer, NO_VALUES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the template to the given `writer` fmt buffer with provided [`Values`].
|
||||||
|
fn render_into_with_values<W: fmt::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
writer: &mut W,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
|
/// Renders the template to the given `writer` io buffer.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
|
self.write_into_with_values(writer, NO_VALUES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the template to the given `writer` io buffer with provided [`Values`].
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn write_into_with_values<W: io::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
writer: &mut W,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
struct Wrapped<W: io::Write> {
|
||||||
|
writer: W,
|
||||||
|
err: Option<io::Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: io::Write> fmt::Write for Wrapped<W> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
if let Err(err) = self.writer.write_all(s.as_bytes()) {
|
||||||
|
self.err = Some(err);
|
||||||
|
Err(fmt::Error)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut wrapped = Wrapped { writer, err: None };
|
||||||
|
if self.render_into_with_values(&mut wrapped, values).is_ok() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let err = wrapped.err.take();
|
||||||
|
Err(err.unwrap_or_else(|| io::Error::new(io::ErrorKind::Other, fmt::Error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides a rough estimate of the expanded length of the rendered template. Larger
|
||||||
|
/// values result in higher memory usage but fewer reallocations. Smaller values result in the
|
||||||
|
/// opposite. This value only affects [`render`]. It does not take effect when calling
|
||||||
|
/// [`render_into`], [`write_into`], the [`fmt::Display`] implementation, or the blanket
|
||||||
|
/// [`ToString::to_string`] implementation.
|
||||||
|
///
|
||||||
|
/// [`render`]: Template::render
|
||||||
|
/// [`render_into`]: Template::render_into
|
||||||
|
/// [`write_into`]: Template::write_into
|
||||||
|
/// [`ToString::to_string`]: alloc::string::ToString::to_string
|
||||||
|
const SIZE_HINT: usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Template + ?Sized> Template for &T {
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn render(&self) -> Result<String> {
|
||||||
|
<T as Template>::render(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn render_with_values(&self, values: &dyn Values) -> Result<String> {
|
||||||
|
<T as Template>::render_with_values(self, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
|
||||||
|
<T as Template>::render_into(self, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn render_into_with_values<W: fmt::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
writer: &mut W,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> Result<()> {
|
||||||
|
<T as Template>::render_into_with_values(self, writer, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
|
<T as Template>::write_into(self, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn write_into_with_values<W: io::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
writer: &mut W,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
<T as Template>::write_into_with_values(self, writer, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIZE_HINT: usize = T::SIZE_HINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`dyn`-compatible] wrapper trait around [`Template`] implementers
|
||||||
|
///
|
||||||
|
/// This trades reduced performance (mostly due to writing into `dyn Write`) for dyn-compatibility.
|
||||||
|
///
|
||||||
|
/// [`dyn`-compatible]: https://doc.rust-lang.org/stable/reference/items/traits.html#dyn-compatibility
|
||||||
|
pub trait DynTemplate {
|
||||||
|
/// Helper method which allocates a new `String` and renders into it.
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn dyn_render(&self) -> Result<String>;
|
||||||
|
|
||||||
|
/// Helper method which allocates a new `String` and renders into it with provided [`Values`].
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn dyn_render_with_values(&self, values: &dyn Values) -> Result<String>;
|
||||||
|
|
||||||
|
/// Renders the template to the given `writer` fmt buffer.
|
||||||
|
fn dyn_render_into(&self, writer: &mut dyn fmt::Write) -> Result<()>;
|
||||||
|
|
||||||
|
/// Renders the template to the given `writer` fmt buffer with provided [`Values`].
|
||||||
|
fn dyn_render_into_with_values(
|
||||||
|
&self,
|
||||||
|
writer: &mut dyn fmt::Write,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
|
/// Renders the template to the given `writer` io buffer.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn dyn_write_into(&self, writer: &mut dyn io::Write) -> io::Result<()>;
|
||||||
|
|
||||||
|
/// Renders the template to the given `writer` io buffer with provided [`Values`].
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn dyn_write_into_with_values(
|
||||||
|
&self,
|
||||||
|
writer: &mut dyn io::Write,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> io::Result<()>;
|
||||||
|
|
||||||
|
/// Provides a conservative estimate of the expanded length of the rendered template.
|
||||||
|
fn size_hint(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Template> DynTemplate for T {
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn dyn_render(&self) -> Result<String> {
|
||||||
|
<Self as Template>::render(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn dyn_render_with_values(&self, values: &dyn Values) -> Result<String> {
|
||||||
|
<Self as Template>::render_with_values(self, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn dyn_render_into(&self, writer: &mut dyn fmt::Write) -> Result<()> {
|
||||||
|
<Self as Template>::render_into(self, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dyn_render_into_with_values(
|
||||||
|
&self,
|
||||||
|
writer: &mut dyn fmt::Write,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> Result<()> {
|
||||||
|
<Self as Template>::render_into_with_values(self, writer, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn dyn_write_into(&self, writer: &mut dyn io::Write) -> io::Result<()> {
|
||||||
|
<Self as Template>::write_into(self, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn dyn_write_into_with_values(
|
||||||
|
&self,
|
||||||
|
writer: &mut dyn io::Write,
|
||||||
|
values: &dyn Values,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
<Self as Template>::write_into_with_values(self, writer, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn size_hint(&self) -> usize {
|
||||||
|
<Self as Template>::SIZE_HINT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for dyn DynTemplate {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.dyn_render_into(f).map_err(|_| fmt::Error {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement the trait `$Trait` for a list of reference (wrapper) types to `$T: $Trait + ?Sized`
|
||||||
|
macro_rules! impl_for_ref {
|
||||||
|
(impl $Trait:ident for $T:ident $body:tt) => {
|
||||||
|
const _: () = {
|
||||||
|
crate::impl_for_ref! {
|
||||||
|
impl<$T> $Trait for [
|
||||||
|
&T
|
||||||
|
&mut T
|
||||||
|
core::cell::Ref<'_, T>
|
||||||
|
core::cell::RefMut<'_, T>
|
||||||
|
] $body
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
const _: () = {
|
||||||
|
crate::impl_for_ref! {
|
||||||
|
impl<$T> $Trait for [
|
||||||
|
alloc::boxed::Box<T>
|
||||||
|
alloc::rc::Rc<T>
|
||||||
|
alloc::sync::Arc<T>
|
||||||
|
] $body
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
const _: () = {
|
||||||
|
crate::impl_for_ref! {
|
||||||
|
impl<$T> $Trait for [
|
||||||
|
std::sync::MutexGuard<'_, T>
|
||||||
|
std::sync::RwLockReadGuard<'_, T>
|
||||||
|
std::sync::RwLockWriteGuard<'_, T>
|
||||||
|
] $body
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
(impl<$T:ident> $Trait:ident for [$($ty:ty)*] $body:tt) => {
|
||||||
|
$(impl<$T: $Trait + ?Sized> $Trait for $ty $body)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use impl_for_ref;
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "alloc"))]
|
||||||
|
mod tests {
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::{DynTemplate, Template};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dyn_template() {
|
||||||
|
use alloc::string::ToString;
|
||||||
|
|
||||||
|
struct Test;
|
||||||
|
|
||||||
|
impl Template for Test {
|
||||||
|
fn render_into_with_values<W: fmt::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
writer: &mut W,
|
||||||
|
_values: &dyn Values,
|
||||||
|
) -> Result<()> {
|
||||||
|
Ok(writer.write_str("test")?)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIZE_HINT: usize = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Test {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.render_into(f).map_err(|_| fmt::Error {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl filters::FastWritable for Test {
|
||||||
|
#[inline]
|
||||||
|
fn write_into<W: fmt::Write + ?Sized>(&self, f: &mut W) -> crate::Result<()> {
|
||||||
|
self.render_into(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(t: &dyn DynTemplate) -> String {
|
||||||
|
t.dyn_render().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
let test = &Test as &dyn DynTemplate;
|
||||||
|
|
||||||
|
assert_eq!(render(test), "test");
|
||||||
|
|
||||||
|
assert_eq!(test.to_string(), "test");
|
||||||
|
|
||||||
|
assert_eq!(alloc::format!("{test}"), "test");
|
||||||
|
|
||||||
|
let mut vec = alloc::vec![];
|
||||||
|
test.dyn_write_into(&mut vec).unwrap();
|
||||||
|
assert_eq!(vec, alloc::vec![b't', b'e', b's', b't']);
|
||||||
|
}
|
||||||
|
}
|
||||||
306
third_party/rust/askama/src/values.rs
vendored
Normal file
306
third_party/rust/askama/src/values.rs
vendored
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
use core::any::Any;
|
||||||
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
/// No runtime values provided.
|
||||||
|
pub const NO_VALUES: &dyn Values = &();
|
||||||
|
|
||||||
|
/// Try to find `key` in `values` and then to convert it to `T`.
|
||||||
|
pub fn get_value<T: Any>(values: &dyn Values, key: impl AsRef<str>) -> Result<&T, Error> {
|
||||||
|
let Some(src) = values.get_value(key.as_ref()) else {
|
||||||
|
return Err(Error::ValueMissing);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(value) = src.downcast_ref::<T>() {
|
||||||
|
return Ok(value);
|
||||||
|
} else if let Some(value) = src.downcast_ref::<&T>() {
|
||||||
|
return Ok(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
if let Some(value) = src.downcast_ref::<alloc::boxed::Box<T>>() {
|
||||||
|
return Ok(value);
|
||||||
|
} else if let Some(value) = src.downcast_ref::<alloc::rc::Rc<T>>() {
|
||||||
|
return Ok(value);
|
||||||
|
} else if let Some(value) = src.downcast_ref::<alloc::sync::Arc<T>>() {
|
||||||
|
return Ok(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::ValueType)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A runtime value store for [`Template::render_with_values()`][crate::Template::render_with_values].
|
||||||
|
pub trait Values {
|
||||||
|
/// Try to find `key` in this store.
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::impl_for_ref! {
|
||||||
|
impl Values for T {
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
T::get_value(self, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Values for () {
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, _: &str) -> Option<&'a dyn Any> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> Values for (K, V)
|
||||||
|
where
|
||||||
|
K: Borrow<str>,
|
||||||
|
V: Value,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
if self.0.borrow() == key {
|
||||||
|
self.1.ref_any()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Values> Values for Option<T> {
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
self.as_ref()?.get_value(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V, const N: usize> Values for [(K, V); N]
|
||||||
|
where
|
||||||
|
K: Borrow<str>,
|
||||||
|
V: Value,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
find_value_linear(self.iter(), key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> Values for [(K, V)]
|
||||||
|
where
|
||||||
|
K: Borrow<str>,
|
||||||
|
V: Value,
|
||||||
|
{
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
find_value_linear(self.iter(), key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<K, V> Values for alloc::vec::Vec<(K, V)>
|
||||||
|
where
|
||||||
|
K: Borrow<str>,
|
||||||
|
V: Value,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
find_value_linear(self.iter(), key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<K, V> Values for alloc::collections::VecDeque<(K, V)>
|
||||||
|
where
|
||||||
|
K: Borrow<str>,
|
||||||
|
V: Value,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
find_value_linear(self.iter(), key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<K, V> Values for alloc::collections::LinkedList<(K, V)>
|
||||||
|
where
|
||||||
|
K: Borrow<str>,
|
||||||
|
V: Value,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
find_value_linear(self.iter(), key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_value_linear<'a, K, V, I>(it: I, key: &str) -> Option<&'a dyn Any>
|
||||||
|
where
|
||||||
|
K: Borrow<str> + 'a,
|
||||||
|
V: Value + 'a,
|
||||||
|
I: Iterator<Item = &'a (K, V)>,
|
||||||
|
{
|
||||||
|
for (k, v) in it {
|
||||||
|
if k.borrow() == key {
|
||||||
|
return v.ref_any();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<K, V> Values for alloc::collections::BTreeMap<K, V>
|
||||||
|
where
|
||||||
|
K: Borrow<str> + core::cmp::Ord,
|
||||||
|
V: Value,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
self.get(key)?.ref_any()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl<K, V, S> Values for std::collections::HashMap<K, V, S>
|
||||||
|
where
|
||||||
|
K: Borrow<str> + Eq + core::hash::Hash,
|
||||||
|
V: Value,
|
||||||
|
S: core::hash::BuildHasher,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||||
|
self.get(key)?.ref_any()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A value in a [`Values`] collection.
|
||||||
|
///
|
||||||
|
/// This is <code>[dyn](https://doc.rust-lang.org/stable/std/keyword.dyn.html) [Any]</code>,
|
||||||
|
/// <code>[Option]<dyn Any></code>, or a reference to either.
|
||||||
|
pub trait Value {
|
||||||
|
/// Returns a reference to this value unless it is `None`.
|
||||||
|
fn ref_any(&self) -> Option<&dyn Any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::impl_for_ref! {
|
||||||
|
impl Value for T {
|
||||||
|
#[inline]
|
||||||
|
fn ref_any(&self) -> Option<&dyn Any> {
|
||||||
|
T::ref_any(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value for dyn Any {
|
||||||
|
#[inline]
|
||||||
|
fn ref_any(&self) -> Option<&dyn Any> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Value> Value for Option<T> {
|
||||||
|
#[inline]
|
||||||
|
fn ref_any(&self) -> Option<&dyn Any> {
|
||||||
|
T::ref_any(self.as_ref()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use assert_matches::assert_matches;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn assert_a_10_c_blam(values: &dyn Values) {
|
||||||
|
assert_matches!(get_value::<u32>(values, "a"), Ok(10u32));
|
||||||
|
assert_matches!(get_value::<&str>(values, "c"), Ok(&"blam"));
|
||||||
|
assert_matches!(get_value::<u8>(values, "a"), Err(Error::ValueType));
|
||||||
|
assert_matches!(get_value::<u8>(values, "d"), Err(Error::ValueMissing));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn assert_a_12_c_blam(values: &dyn Values) {
|
||||||
|
assert_matches!(get_value::<u32>(values, "a"), Ok(12u32));
|
||||||
|
assert_matches!(get_value::<&str>(values, "c"), Ok(&"blam"));
|
||||||
|
assert_matches!(get_value::<u8>(values, "a"), Err(Error::ValueType));
|
||||||
|
assert_matches!(get_value::<u8>(values, "d"), Err(Error::ValueMissing));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[test]
|
||||||
|
fn values_on_hashmap() {
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::string::String;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
let mut values: HashMap<String, Box<dyn Any>> = HashMap::new();
|
||||||
|
values.insert("a".into(), Box::new(12u32));
|
||||||
|
values.insert("c".into(), Box::new("blam"));
|
||||||
|
assert_a_12_c_blam(&values);
|
||||||
|
|
||||||
|
let mut values: HashMap<&str, Box<dyn Any>> = HashMap::new();
|
||||||
|
values.insert("a", Box::new(10u32));
|
||||||
|
assert_matches!(get_value::<u32>(&values, "a"), Ok(&10u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[test]
|
||||||
|
fn values_on_btreemap() {
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::collections::BTreeMap;
|
||||||
|
use alloc::string::String;
|
||||||
|
|
||||||
|
let mut values: BTreeMap<String, Box<dyn Any>> = BTreeMap::new();
|
||||||
|
values.insert("a".into(), Box::new(12u32));
|
||||||
|
values.insert("c".into(), Box::new("blam"));
|
||||||
|
assert_a_12_c_blam(&values);
|
||||||
|
|
||||||
|
let mut values: BTreeMap<&str, Box<dyn Any>> = BTreeMap::new();
|
||||||
|
values.insert("a", Box::new(10u32));
|
||||||
|
assert_matches!(get_value::<u32>(&values, "a"), Ok(&10u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn values_on_slice() {
|
||||||
|
let slice: &[(&str, &dyn Any)] = &[("a", &12u32), ("c", &"blam")];
|
||||||
|
assert_a_12_c_blam(&slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[test]
|
||||||
|
fn values_vec() {
|
||||||
|
let vec: alloc::vec::Vec<(&str, &dyn Any)> = alloc::vec![("a", &12u32), ("c", &"blam")];
|
||||||
|
assert_a_12_c_blam(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[test]
|
||||||
|
fn values_deque() {
|
||||||
|
let mut deque = alloc::collections::VecDeque::<(&str, &dyn Any)>::new();
|
||||||
|
deque.push_back(("a", &12u32));
|
||||||
|
deque.push_back(("c", &"blam"));
|
||||||
|
assert_a_12_c_blam(&deque);
|
||||||
|
deque.pop_front();
|
||||||
|
deque.push_back(("a", &10u32));
|
||||||
|
assert_a_10_c_blam(&deque);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[test]
|
||||||
|
fn values_list() {
|
||||||
|
let mut list = alloc::collections::LinkedList::<(&str, &dyn Any)>::new();
|
||||||
|
list.push_back(("a", &12u32));
|
||||||
|
list.push_back(("c", &"blam"));
|
||||||
|
assert_a_12_c_blam(&list);
|
||||||
|
list.pop_front();
|
||||||
|
list.push_back(("a", &10u32));
|
||||||
|
assert_a_10_c_blam(&list);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn values_on_tuple() {
|
||||||
|
let tuple: (&str, &dyn Any) = ("a", &10u32);
|
||||||
|
|
||||||
|
assert_matches!(get_value::<u32>(&tuple, "a"), Ok(10u32));
|
||||||
|
assert_matches!(get_value::<i32>(&tuple, "a"), Err(Error::ValueType));
|
||||||
|
assert_matches!(get_value::<i32>(&tuple, "b"), Err(Error::ValueMissing));
|
||||||
|
}
|
||||||
|
}
|
||||||
23
third_party/rust/askama/tomlfmt.toml
vendored
Normal file
23
third_party/rust/askama/tomlfmt.toml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Keep in the same order as <https://doc.rust-lang.org/cargo/reference/manifest.html>
|
||||||
|
table_order = [
|
||||||
|
"package",
|
||||||
|
# targets
|
||||||
|
"lib",
|
||||||
|
"bin",
|
||||||
|
"example",
|
||||||
|
"test",
|
||||||
|
"bench",
|
||||||
|
# dependencies
|
||||||
|
"dependencies",
|
||||||
|
"dev-dependencies",
|
||||||
|
"build-dependencies",
|
||||||
|
"target",
|
||||||
|
# misc
|
||||||
|
"badges",
|
||||||
|
"features",
|
||||||
|
"lints",
|
||||||
|
"patch",
|
||||||
|
"replace",
|
||||||
|
"profile",
|
||||||
|
"workspace",
|
||||||
|
]
|
||||||
1
third_party/rust/askama_derive/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/askama_derive/.cargo-checksum.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"files":{"Cargo.lock":"8008614e39c3bc17f343022acece92f83df083c063fadd7285b487edb6144cd0","Cargo.toml":"82346a11579069448d1e1d80d0ace534961ec3614a5b1cf08ef26e9120a3373d","LICENSE-APACHE":"87cb0d734c723c083e51c825930ff42bce28596b52dee15567f6b28f19c195e3","LICENSE-MIT":"df20e0180764bf5bd76f74d47bc9e8c0069a666401629c390003a1d5eba99c92","README.md":"7ed9662073cd064bc6e5e57cd41658d318e9ae32285c5d6682a7c8ba83baef6c","_typos.toml":"72588ef2385568368e7ec251c8451f75a16343d5a06b4c2b312beba2cdf5b308","clippy.toml":"d141089a5edd91c83ae211257638df2ef1ca53f66b30faa98b22ea1447faeeff","deny.toml":"b7637b42164b1bbba883c422187292cf08ad624691145b5124eaf10edf38c164","empty_test_config.toml":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/config.rs":"56d724c4c21bd09c93ce8971226a5273eb86696eb1592e127149edcb43a2e4a1","src/generator.rs":"b3f3326072e436cc0ba7e4130e4298d8f3e5d8840121396b153aab4a13b1d403","src/generator/expr.rs":"1fcbbb2b7db536940d80d64863168f27f25b3b6c585c8f6522e5f38be3f800f9","src/generator/node.rs":"5c1dbee8e6ad4dacfa0a6a0c796489d2dd76f4625363beba866194fea65e3677","src/heritage.rs":"d726ea8a567be3503f7f6efa80f21fe009cc590dff63f0d4ad76cc8dd3c5bd43","src/html.rs":"91ee580663d8ad08460373d9e5c8ed196ab0ce61eb113dc8d43ad19ca6671422","src/input.rs":"0e2c0d4b574198511068069e7a11f6f2ab99a76281f45c7490f39cca631b9590","src/integration.rs":"bcdc1b7b1729487a8e4c756890e6d356622b725f1b9a41bff76eca87e71eaa0b","src/lib.rs":"0b9496bd40dc2bdca7ad825061e297ee9524ee18a30f7e065c4c6b583caca72a","src/tests.rs":"262039b9f6cbbf7a838ec55afbb6d6c0ffdf135090a58e157c66b63c8ead0c3b","templates/a.html":"b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c","templates/b.html":"7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730","templates/include1.html":"5a4304e5548d5cf618779383dcc27eba93d826feff1c41a348101c734c034ea2","templates/include2.html":"cd319fee61d9d0e8a00a40d55e91c338fd9e156e69773f03ba5c31db02892eb2","templates/include3.html":"1121cfccd5913f0a63fec40a6ffd44ea64f9dc135c66634ba001d10bcf4302a2","templates/sub/b.html":"7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730","templates/sub/c.html":"bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c","templates/sub/sub1/d.html":"86b0c5a1e2b73b08fd54c727f4458649ed9fe3ad1b6e8ac9460c070113509a1e","tomlfmt.toml":"a33c21547346656aead5f46f2f07e1965f4cf0c6f2a16deb8ec06019619267aa"},"package":"d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac"}
|
||||||
268
third_party/rust/askama_derive/Cargo.lock
generated
vendored
Normal file
268
third_party/rust/askama_derive/Cargo.lock
generated
vendored
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama_derive"
|
||||||
|
version = "0.13.1"
|
||||||
|
dependencies = [
|
||||||
|
"askama_parser",
|
||||||
|
"basic-toml",
|
||||||
|
"console",
|
||||||
|
"memchr",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"pulldown-cmark",
|
||||||
|
"quote",
|
||||||
|
"rustc-hash",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"similar",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama_parser"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "basic-toml"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.15.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"unicode-width",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.171"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulldown-cmark"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"memchr",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "similar"
|
||||||
|
version = "2.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
@@ -11,19 +11,20 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.71"
|
rust-version = "1.81"
|
||||||
name = "rinja_derive"
|
name = "askama_derive"
|
||||||
version = "0.3.5"
|
version = "0.13.1"
|
||||||
build = false
|
build = false
|
||||||
|
autolib = false
|
||||||
autobins = false
|
autobins = false
|
||||||
autoexamples = false
|
autoexamples = false
|
||||||
autotests = false
|
autotests = false
|
||||||
autobenches = false
|
autobenches = false
|
||||||
description = "Procedural macro package for Rinja"
|
description = "Procedural macro package for Askama"
|
||||||
homepage = "https://github.com/rinja-rs/rinja"
|
homepage = "https://github.com/askama-rs/askama"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/rinja-rs/rinja"
|
repository = "https://github.com/askama-rs/askama"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -32,8 +33,35 @@ rustdoc-args = [
|
|||||||
"--cfg=docsrs",
|
"--cfg=docsrs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = []
|
||||||
|
blocks = ["syn/full"]
|
||||||
|
code-in-doc = ["dep:pulldown-cmark"]
|
||||||
|
config = [
|
||||||
|
"dep:basic-toml",
|
||||||
|
"dep:serde",
|
||||||
|
"dep:serde_derive",
|
||||||
|
"parser/config",
|
||||||
|
]
|
||||||
|
default = [
|
||||||
|
"config",
|
||||||
|
"derive",
|
||||||
|
"std",
|
||||||
|
"urlencode",
|
||||||
|
]
|
||||||
|
derive = []
|
||||||
|
full = [
|
||||||
|
"default",
|
||||||
|
"blocks",
|
||||||
|
"code-in-doc",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
serde_json = []
|
||||||
|
std = ["alloc"]
|
||||||
|
urlencode = []
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "rinja_derive"
|
name = "askama_derive"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
@@ -44,37 +72,42 @@ optional = true
|
|||||||
[dependencies.memchr]
|
[dependencies.memchr]
|
||||||
version = "2"
|
version = "2"
|
||||||
|
|
||||||
[dependencies.mime]
|
|
||||||
version = "0.3"
|
|
||||||
|
|
||||||
[dependencies.mime_guess]
|
|
||||||
version = "2"
|
|
||||||
|
|
||||||
[dependencies.parser]
|
[dependencies.parser]
|
||||||
version = "=0.3.5"
|
version = "=0.13.0"
|
||||||
package = "rinja_parser"
|
package = "askama_parser"
|
||||||
|
|
||||||
[dependencies.proc-macro2]
|
[dependencies.proc-macro2]
|
||||||
version = "1"
|
version = "1"
|
||||||
|
|
||||||
[dependencies.pulldown-cmark]
|
[dependencies.pulldown-cmark]
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
optional = true
|
optional = true
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dependencies.quote]
|
[dependencies.quote]
|
||||||
version = "1"
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dependencies.rustc-hash]
|
[dependencies.rustc-hash]
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
features = ["derive"]
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.serde_derive]
|
||||||
|
version = "1.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.syn]
|
[dependencies.syn]
|
||||||
version = "2.0.3"
|
version = "2.0.41"
|
||||||
|
features = [
|
||||||
|
"clone-impls",
|
||||||
|
"derive",
|
||||||
|
"parsing",
|
||||||
|
"printing",
|
||||||
|
]
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies.console]
|
[dev-dependencies.console]
|
||||||
version = "0.15.8"
|
version = "0.15.8"
|
||||||
@@ -86,24 +119,9 @@ version = "0.2.20"
|
|||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
|
|
||||||
[dev-dependencies.syn]
|
[dev-dependencies.syn]
|
||||||
version = "2.0.3"
|
version = "2.0.41"
|
||||||
features = ["full"]
|
features = ["full"]
|
||||||
|
|
||||||
[features]
|
|
||||||
code-in-doc = ["dep:pulldown-cmark"]
|
|
||||||
config = [
|
|
||||||
"dep:serde",
|
|
||||||
"dep:basic-toml",
|
|
||||||
"parser/config",
|
|
||||||
]
|
|
||||||
humansize = []
|
|
||||||
serde_json = []
|
|
||||||
urlencode = []
|
|
||||||
with-actix-web = []
|
|
||||||
with-axum = []
|
|
||||||
with-rocket = []
|
|
||||||
with-warp = []
|
|
||||||
|
|
||||||
[lints.rust.unexpected_cfgs]
|
[lints.rust.unexpected_cfgs]
|
||||||
level = "allow"
|
level = "allow"
|
||||||
priority = 0
|
priority = 0
|
||||||
9
third_party/rust/askama_derive/README.md
vendored
Normal file
9
third_party/rust/askama_derive/README.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# askama_derive: procedural macros for the Askama templating engine
|
||||||
|
|
||||||
|
[](https://crates.io/crates/askama_derive)
|
||||||
|
[](https://github.com/askama-rs/askama/actions/workflows/rust.yml)
|
||||||
|
[](https://askama.readthedocs.io/)
|
||||||
|
[](https://docs.rs/askama_derive/)
|
||||||
|
|
||||||
|
This crate contains the procedural macros used by the
|
||||||
|
[Askama](https://github.com/askama-rs/askama) templating engine.
|
||||||
34
third_party/rust/askama_derive/_typos.toml
vendored
Normal file
34
third_party/rust/askama_derive/_typos.toml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[default]
|
||||||
|
locale = "en-us"
|
||||||
|
|
||||||
|
[files]
|
||||||
|
extend-exclude = [
|
||||||
|
# generated files
|
||||||
|
"book/ethicalads-theme.css",
|
||||||
|
"fuzzing/fuzz/artifacts/",
|
||||||
|
"fuzzing/fuzz/corpus/",
|
||||||
|
"target/",
|
||||||
|
"askama_parser/tests/*.txt",
|
||||||
|
"testing/templates/fuzzed-*",
|
||||||
|
# we copied the files verbatim including any typos :)
|
||||||
|
"askama_parser/benches",
|
||||||
|
"askama_derive_standalone/benches",
|
||||||
|
# filler texts
|
||||||
|
"*/benches/strings.inc",
|
||||||
|
# too many false positives
|
||||||
|
"testing/tests/gen_ws_tests.py",
|
||||||
|
]
|
||||||
|
|
||||||
|
[default.extend-words]
|
||||||
|
# It's actually called that in the ASCII standard
|
||||||
|
Enquiry = "Enquiry"
|
||||||
|
|
||||||
|
# French words
|
||||||
|
exemple = "exemple"
|
||||||
|
existant = "existant"
|
||||||
|
|
||||||
|
# used in tests
|
||||||
|
Ba = "Ba"
|
||||||
|
fo = "fo"
|
||||||
|
Fo = "Fo"
|
||||||
|
sur = "sur"
|
||||||
1
third_party/rust/askama_derive/clippy.toml
vendored
Normal file
1
third_party/rust/askama_derive/clippy.toml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
msrv = "1.81.0"
|
||||||
9
third_party/rust/askama_derive/deny.toml
vendored
Normal file
9
third_party/rust/askama_derive/deny.toml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[licenses]
|
||||||
|
version = 2
|
||||||
|
allow = ["Apache-2.0", "MIT", "MIT-0", "Unicode-3.0"]
|
||||||
|
private = { ignore = true }
|
||||||
|
|
||||||
|
[[licenses.clarify]]
|
||||||
|
name = "ring"
|
||||||
|
expression = "ISC AND MIT AND OpenSSL"
|
||||||
|
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]
|
||||||
0
third_party/rust/askama_derive/empty_test_config.toml
vendored
Normal file
0
third_party/rust/askama_derive/empty_test_config.toml
vendored
Normal file
@@ -10,7 +10,7 @@ use parser::node::Whitespace;
|
|||||||
use parser::{ParseError, Parsed, Syntax, SyntaxBuilder};
|
use parser::{ParseError, Parsed, Syntax, SyntaxBuilder};
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
#[cfg(feature = "config")]
|
#[cfg(feature = "config")]
|
||||||
use serde::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
use crate::{CompileError, FileInfo, OnceMap};
|
use crate::{CompileError, FileInfo, OnceMap};
|
||||||
|
|
||||||
@@ -20,7 +20,8 @@ pub(crate) struct Config {
|
|||||||
pub(crate) syntaxes: BTreeMap<String, SyntaxAndCache<'static>>,
|
pub(crate) syntaxes: BTreeMap<String, SyntaxAndCache<'static>>,
|
||||||
pub(crate) default_syntax: &'static str,
|
pub(crate) default_syntax: &'static str,
|
||||||
pub(crate) escapers: Vec<(Vec<Cow<'static, str>>, Cow<'static, str>)>,
|
pub(crate) escapers: Vec<(Vec<Cow<'static, str>>, Cow<'static, str>)>,
|
||||||
pub(crate) whitespace: WhitespaceHandling,
|
pub(crate) whitespace: Whitespace,
|
||||||
|
pub(crate) full_config_path: Option<PathBuf>,
|
||||||
// `Config` is self referential and `_key` owns it data, so it must come last
|
// `Config` is self referential and `_key` owns it data, so it must come last
|
||||||
_key: OwnedConfigKey,
|
_key: OwnedConfigKey,
|
||||||
}
|
}
|
||||||
@@ -37,25 +38,24 @@ struct OwnedConfigKey(&'static ConfigKey<'static>);
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
struct ConfigKey<'a> {
|
struct ConfigKey<'a> {
|
||||||
|
root: Cow<'a, Path>,
|
||||||
source: Cow<'a, str>,
|
source: Cow<'a, str>,
|
||||||
config_path: Option<Cow<'a, str>>,
|
config_path: Option<Cow<'a, str>>,
|
||||||
template_whitespace: Option<Cow<'a, str>>,
|
template_whitespace: Option<Whitespace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ToOwned for ConfigKey<'a> {
|
impl ToOwned for ConfigKey<'_> {
|
||||||
type Owned = OwnedConfigKey;
|
type Owned = OwnedConfigKey;
|
||||||
|
|
||||||
fn to_owned(&self) -> Self::Owned {
|
fn to_owned(&self) -> Self::Owned {
|
||||||
let owned_key = ConfigKey {
|
let owned_key = ConfigKey {
|
||||||
|
root: Cow::Owned(self.root.as_ref().to_owned()),
|
||||||
source: Cow::Owned(self.source.as_ref().to_owned()),
|
source: Cow::Owned(self.source.as_ref().to_owned()),
|
||||||
config_path: self
|
config_path: self
|
||||||
.config_path
|
.config_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|s| Cow::Owned(s.as_ref().to_owned())),
|
.map(|s| Cow::Owned(s.as_ref().to_owned())),
|
||||||
template_whitespace: self
|
template_whitespace: self.template_whitespace,
|
||||||
.template_whitespace
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| Cow::Owned(s.as_ref().to_owned())),
|
|
||||||
};
|
};
|
||||||
OwnedConfigKey(Box::leak(Box::new(owned_key)))
|
OwnedConfigKey(Box::leak(Box::new(owned_key)))
|
||||||
}
|
}
|
||||||
@@ -72,19 +72,21 @@ impl Config {
|
|||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
source: &str,
|
source: &str,
|
||||||
config_path: Option<&str>,
|
config_path: Option<&str>,
|
||||||
template_whitespace: Option<&str>,
|
template_whitespace: Option<Whitespace>,
|
||||||
config_span: Option<Span>,
|
config_span: Option<Span>,
|
||||||
|
full_config_path: Option<PathBuf>,
|
||||||
) -> Result<&'static Config, CompileError> {
|
) -> Result<&'static Config, CompileError> {
|
||||||
static CACHE: ManuallyDrop<OnceLock<OnceMap<OwnedConfigKey, &'static Config>>> =
|
static CACHE: ManuallyDrop<OnceLock<OnceMap<OwnedConfigKey, &'static Config>>> =
|
||||||
ManuallyDrop::new(OnceLock::new());
|
ManuallyDrop::new(OnceLock::new());
|
||||||
CACHE.get_or_init(OnceMap::default).get_or_try_insert(
|
CACHE.get_or_init(OnceMap::default).get_or_try_insert(
|
||||||
&ConfigKey {
|
&ConfigKey {
|
||||||
|
root: Cow::Owned(manifest_root()),
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
config_path: config_path.map(Cow::Borrowed),
|
config_path: config_path.map(Cow::Borrowed),
|
||||||
template_whitespace: template_whitespace.map(Cow::Borrowed),
|
template_whitespace,
|
||||||
},
|
},
|
||||||
|key| {
|
|key| {
|
||||||
let config = Config::new_uncached(key.to_owned(), config_span)?;
|
let config = Config::new_uncached(key.to_owned(), config_span, full_config_path)?;
|
||||||
let config = &*Box::leak(Box::new(config));
|
let config = &*Box::leak(Box::new(config));
|
||||||
Ok((config._key, config))
|
Ok((config._key, config))
|
||||||
},
|
},
|
||||||
@@ -97,12 +99,12 @@ impl Config {
|
|||||||
fn new_uncached(
|
fn new_uncached(
|
||||||
key: OwnedConfigKey,
|
key: OwnedConfigKey,
|
||||||
config_span: Option<Span>,
|
config_span: Option<Span>,
|
||||||
|
full_config_path: Option<PathBuf>,
|
||||||
) -> Result<Config, CompileError> {
|
) -> Result<Config, CompileError> {
|
||||||
let s = key.0.source.as_ref();
|
let s = key.0.source.as_ref();
|
||||||
let config_path = key.0.config_path.as_deref();
|
let config_path = key.0.config_path.as_deref();
|
||||||
let template_whitespace = key.0.template_whitespace.as_deref();
|
let root = key.0.root.as_ref();
|
||||||
|
|
||||||
let root = manifest_root();
|
|
||||||
let default_dirs = vec![root.join("templates")];
|
let default_dirs = vec![root.join("templates")];
|
||||||
|
|
||||||
let mut syntaxes = BTreeMap::new();
|
let mut syntaxes = BTreeMap::new();
|
||||||
@@ -114,7 +116,7 @@ impl Config {
|
|||||||
RawConfig::from_toml_str(s)?
|
RawConfig::from_toml_str(s)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let (dirs, default_syntax, mut whitespace) = match raw.general {
|
let (dirs, default_syntax, whitespace) = match raw.general {
|
||||||
Some(General {
|
Some(General {
|
||||||
dirs,
|
dirs,
|
||||||
default_syntax,
|
default_syntax,
|
||||||
@@ -126,26 +128,10 @@ impl Config {
|
|||||||
default_syntax.unwrap_or(DEFAULT_SYNTAX_NAME),
|
default_syntax.unwrap_or(DEFAULT_SYNTAX_NAME),
|
||||||
whitespace,
|
whitespace,
|
||||||
),
|
),
|
||||||
None => (
|
None => (default_dirs, DEFAULT_SYNTAX_NAME, Whitespace::default()),
|
||||||
default_dirs,
|
|
||||||
DEFAULT_SYNTAX_NAME,
|
|
||||||
WhitespaceHandling::default(),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
let file_info = config_path.map(|path| FileInfo::new(Path::new(path), None, None));
|
let file_info = config_path.map(|path| FileInfo::new(Path::new(path), None, None));
|
||||||
if let Some(template_whitespace) = template_whitespace {
|
let whitespace = key.0.template_whitespace.unwrap_or(whitespace);
|
||||||
whitespace = match template_whitespace {
|
|
||||||
"suppress" => WhitespaceHandling::Suppress,
|
|
||||||
"minimize" => WhitespaceHandling::Minimize,
|
|
||||||
"preserve" => WhitespaceHandling::Preserve,
|
|
||||||
s => {
|
|
||||||
return Err(CompileError::new(
|
|
||||||
format!("invalid value for `whitespace`: \"{s}\""),
|
|
||||||
file_info,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(raw_syntaxes) = raw.syntax {
|
if let Some(raw_syntaxes) = raw.syntax {
|
||||||
for raw_s in raw_syntaxes {
|
for raw_s in raw_syntaxes {
|
||||||
@@ -168,7 +154,7 @@ impl Config {
|
|||||||
|
|
||||||
if !syntaxes.contains_key(default_syntax) {
|
if !syntaxes.contains_key(default_syntax) {
|
||||||
return Err(CompileError::new(
|
return Err(CompileError::new(
|
||||||
format!("default syntax \"{default_syntax}\" not found"),
|
format_args!("default syntax \"{default_syntax}\" not found"),
|
||||||
file_info,
|
file_info,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -182,7 +168,7 @@ impl Config {
|
|||||||
for (extensions, name) in DEFAULT_ESCAPERS {
|
for (extensions, name) in DEFAULT_ESCAPERS {
|
||||||
escapers.push((
|
escapers.push((
|
||||||
str_set(extensions),
|
str_set(extensions),
|
||||||
format!("rinja::filters::{name}").into(),
|
format!("askama::filters::{name}").into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +178,7 @@ impl Config {
|
|||||||
default_syntax,
|
default_syntax,
|
||||||
escapers,
|
escapers,
|
||||||
whitespace,
|
whitespace,
|
||||||
|
full_config_path,
|
||||||
_key: key,
|
_key: key,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -202,27 +189,34 @@ impl Config {
|
|||||||
start_at: Option<&Path>,
|
start_at: Option<&Path>,
|
||||||
file_info: Option<FileInfo<'_>>,
|
file_info: Option<FileInfo<'_>>,
|
||||||
) -> Result<Arc<Path>, CompileError> {
|
) -> Result<Arc<Path>, CompileError> {
|
||||||
|
let path = 'find_path: {
|
||||||
if let Some(root) = start_at {
|
if let Some(root) = start_at {
|
||||||
let relative = root.with_file_name(path);
|
let relative = root.with_file_name(path);
|
||||||
if relative.exists() {
|
if relative.exists() {
|
||||||
return Ok(relative.into());
|
break 'find_path relative;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for dir in &self.dirs {
|
for dir in &self.dirs {
|
||||||
let rooted = dir.join(path);
|
let rooted = dir.join(path);
|
||||||
if rooted.exists() {
|
if rooted.exists() {
|
||||||
return Ok(rooted.into());
|
break 'find_path rooted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Err(CompileError::new(
|
||||||
Err(CompileError::new(
|
format_args!(
|
||||||
format!(
|
|
||||||
"template {:?} not found in directories {:?}",
|
"template {:?} not found in directories {:?}",
|
||||||
path, self.dirs
|
path, self.dirs,
|
||||||
),
|
),
|
||||||
file_info,
|
file_info,
|
||||||
))
|
));
|
||||||
|
};
|
||||||
|
match path.canonicalize() {
|
||||||
|
Ok(path) => Ok(path.into()),
|
||||||
|
Err(err) => Err(CompileError::new(
|
||||||
|
format_args!("could not canonicalize path {path:?}: {err}"),
|
||||||
|
file_info,
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,38 +321,13 @@ impl RawConfig<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, PartialEq, Eq, Debug, Hash)]
|
|
||||||
#[cfg_attr(feature = "config", derive(Deserialize))]
|
|
||||||
#[cfg_attr(feature = "config", serde(field_identifier, rename_all = "lowercase"))]
|
|
||||||
pub(crate) enum WhitespaceHandling {
|
|
||||||
/// The default behavior. It will leave the whitespace characters "as is".
|
|
||||||
#[default]
|
|
||||||
Preserve,
|
|
||||||
/// It'll remove all the whitespace characters before and after the jinja block.
|
|
||||||
Suppress,
|
|
||||||
/// It'll remove all the whitespace characters except one before and after the jinja blocks.
|
|
||||||
/// If there is a newline character, the preserved character in the trimmed characters, it will
|
|
||||||
/// the one preserved.
|
|
||||||
Minimize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<WhitespaceHandling> for Whitespace {
|
|
||||||
fn from(ws: WhitespaceHandling) -> Self {
|
|
||||||
match ws {
|
|
||||||
WhitespaceHandling::Suppress => Whitespace::Suppress,
|
|
||||||
WhitespaceHandling::Preserve => Whitespace::Preserve,
|
|
||||||
WhitespaceHandling::Minimize => Whitespace::Minimize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "config", derive(Deserialize))]
|
#[cfg_attr(feature = "config", derive(Deserialize))]
|
||||||
struct General<'a> {
|
struct General<'a> {
|
||||||
#[cfg_attr(feature = "config", serde(borrow))]
|
#[cfg_attr(feature = "config", serde(borrow))]
|
||||||
dirs: Option<Vec<&'a str>>,
|
dirs: Option<Vec<&'a str>>,
|
||||||
default_syntax: Option<&'a str>,
|
default_syntax: Option<&'a str>,
|
||||||
#[cfg_attr(feature = "config", serde(default))]
|
#[cfg_attr(feature = "config", serde(default))]
|
||||||
whitespace: WhitespaceHandling,
|
whitespace: Whitespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "config", derive(Deserialize))]
|
#[cfg_attr(feature = "config", derive(Deserialize))]
|
||||||
@@ -370,7 +339,7 @@ struct RawEscaper<'a> {
|
|||||||
pub(crate) fn read_config_file(
|
pub(crate) fn read_config_file(
|
||||||
config_path: Option<&str>,
|
config_path: Option<&str>,
|
||||||
span: Option<Span>,
|
span: Option<Span>,
|
||||||
) -> Result<String, CompileError> {
|
) -> Result<(String, Option<PathBuf>), CompileError> {
|
||||||
let root = manifest_root();
|
let root = manifest_root();
|
||||||
let filename = match config_path {
|
let filename = match config_path {
|
||||||
Some(config_path) => root.join(config_path),
|
Some(config_path) => root.join(config_path),
|
||||||
@@ -378,19 +347,20 @@ pub(crate) fn read_config_file(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if filename.exists() {
|
if filename.exists() {
|
||||||
fs::read_to_string(&filename).map_err(|err| {
|
let content = fs::read_to_string(&filename).map_err(|err| {
|
||||||
CompileError::no_file_info(
|
CompileError::no_file_info(
|
||||||
format!("unable to read {}: {err}", filename.display()),
|
format_args!("unable to read {}: {err}", filename.display()),
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
})
|
})?;
|
||||||
|
Ok((content, filename.canonicalize().ok()))
|
||||||
} else if config_path.is_some() {
|
} else if config_path.is_some() {
|
||||||
Err(CompileError::no_file_info(
|
Err(CompileError::no_file_info(
|
||||||
format!("`{}` does not exist", filename.display()),
|
format_args!("`{}` does not exist", filename.display()),
|
||||||
span,
|
span,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(String::new())
|
Ok((String::new(), None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,12 +372,12 @@ fn str_set(vals: &[&'static str]) -> Vec<Cow<'static, str>> {
|
|||||||
vals.iter().map(|s| Cow::Borrowed(*s)).collect()
|
vals.iter().map(|s| Cow::Borrowed(*s)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
static CONFIG_FILE_NAME: &str = "rinja.toml";
|
static CONFIG_FILE_NAME: &str = "askama.toml";
|
||||||
static DEFAULT_SYNTAX_NAME: &str = "default";
|
static DEFAULT_SYNTAX_NAME: &str = "default";
|
||||||
static DEFAULT_ESCAPERS: &[(&[&str], &str)] = &[
|
static DEFAULT_ESCAPERS: &[(&[&str], &str)] = &[
|
||||||
(
|
(
|
||||||
&[
|
&[
|
||||||
"html", "htm", "j2", "jinja", "jinja2", "rinja", "svg", "xml",
|
"askama", "html", "htm", "j2", "jinja", "jinja2", "rinja", "svg", "xml",
|
||||||
],
|
],
|
||||||
"Html",
|
"Html",
|
||||||
),
|
),
|
||||||
@@ -416,30 +386,34 @@ static DEFAULT_ESCAPERS: &[(&[&str], &str)] = &[
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::env;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_config() {
|
fn test_default_config() {
|
||||||
let mut root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
let mut root = manifest_root();
|
||||||
root.push("templates");
|
root.push("templates");
|
||||||
let config = Config::new("", None, None, None).unwrap();
|
let config = Config::new("", None, None, None, None).unwrap();
|
||||||
assert_eq!(config.dirs, vec![root]);
|
assert_eq!(config.dirs, vec![root]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "config")]
|
#[cfg(feature = "config")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_config_dirs() {
|
fn test_config_dirs() {
|
||||||
let mut root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
let mut root = manifest_root();
|
||||||
root.push("tpl");
|
root = root.join("tpl");
|
||||||
let config = Config::new("[general]\ndirs = [\"tpl\"]", None, None, None).unwrap();
|
let config = Config::new("[general]\ndirs = [\"tpl\"]", None, None, None, None).unwrap();
|
||||||
assert_eq!(config.dirs, vec![root]);
|
assert_eq!(config.dirs, vec![root]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_eq_rooted(actual: &Path, expected: &str) {
|
fn assert_eq_rooted(actual: &Path, expected: &str) {
|
||||||
let mut root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
let mut root = manifest_root().canonicalize().unwrap();
|
||||||
|
if root.ends_with("askama_derive_standalone") {
|
||||||
|
root.pop();
|
||||||
|
root.push("askama_derive");
|
||||||
|
}
|
||||||
|
|
||||||
root.push("templates");
|
root.push("templates");
|
||||||
let mut inner = PathBuf::new();
|
let mut inner = PathBuf::new();
|
||||||
inner.push(expected);
|
inner.push(expected);
|
||||||
@@ -448,7 +422,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn find_absolute() {
|
fn find_absolute() {
|
||||||
let config = Config::new("", None, None, None).unwrap();
|
let config = Config::new("", None, None, None, None).unwrap();
|
||||||
let root = config.find_template("a.html", None, None).unwrap();
|
let root = config.find_template("a.html", None, None).unwrap();
|
||||||
let path = config
|
let path = config
|
||||||
.find_template("sub/b.html", Some(&root), None)
|
.find_template("sub/b.html", Some(&root), None)
|
||||||
@@ -459,14 +433,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn find_relative_nonexistent() {
|
fn find_relative_nonexistent() {
|
||||||
let config = Config::new("", None, None, None).unwrap();
|
let config = Config::new("", None, None, None, None).unwrap();
|
||||||
let root = config.find_template("a.html", None, None).unwrap();
|
let root = config.find_template("a.html", None, None).unwrap();
|
||||||
config.find_template("c.html", Some(&root), None).unwrap();
|
config.find_template("c.html", Some(&root), None).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn find_relative() {
|
fn find_relative() {
|
||||||
let config = Config::new("", None, None, None).unwrap();
|
let config = Config::new("", None, None, None, None).unwrap();
|
||||||
let root = config.find_template("sub/b.html", None, None).unwrap();
|
let root = config.find_template("sub/b.html", None, None).unwrap();
|
||||||
let path = config.find_template("c.html", Some(&root), None).unwrap();
|
let path = config.find_template("c.html", Some(&root), None).unwrap();
|
||||||
assert_eq_rooted(&path, "sub/c.html");
|
assert_eq_rooted(&path, "sub/c.html");
|
||||||
@@ -474,7 +448,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn find_relative_sub() {
|
fn find_relative_sub() {
|
||||||
let config = Config::new("", None, None, None).unwrap();
|
let config = Config::new("", None, None, None, None).unwrap();
|
||||||
let root = config.find_template("sub/b.html", None, None).unwrap();
|
let root = config.find_template("sub/b.html", None, None).unwrap();
|
||||||
let path = config
|
let path = config
|
||||||
.find_template("sub1/d.html", Some(&root), None)
|
.find_template("sub1/d.html", Some(&root), None)
|
||||||
@@ -499,7 +473,7 @@ mod tests {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let default_syntax = Syntax::default();
|
let default_syntax = Syntax::default();
|
||||||
let config = Config::new(raw_config, None, None, None).unwrap();
|
let config = Config::new(raw_config, None, None, None, None).unwrap();
|
||||||
assert_eq!(config.default_syntax, "foo");
|
assert_eq!(config.default_syntax, "foo");
|
||||||
|
|
||||||
let foo = config.syntaxes.get("foo").unwrap();
|
let foo = config.syntaxes.get("foo").unwrap();
|
||||||
@@ -531,7 +505,7 @@ mod tests {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let default_syntax = Syntax::default();
|
let default_syntax = Syntax::default();
|
||||||
let config = Config::new(raw_config, None, None, None).unwrap();
|
let config = Config::new(raw_config, None, None, None, None).unwrap();
|
||||||
assert_eq!(config.default_syntax, "foo");
|
assert_eq!(config.default_syntax, "foo");
|
||||||
|
|
||||||
let foo = config.syntaxes.get("foo").unwrap();
|
let foo = config.syntaxes.get("foo").unwrap();
|
||||||
@@ -568,7 +542,7 @@ mod tests {
|
|||||||
default_syntax = "emoji"
|
default_syntax = "emoji"
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let config = Config::new(raw_config, None, None, None).unwrap();
|
let config = Config::new(raw_config, None, None, None, None).unwrap();
|
||||||
assert_eq!(config.default_syntax, "emoji");
|
assert_eq!(config.default_syntax, "emoji");
|
||||||
|
|
||||||
let foo = config.syntaxes.get("emoji").unwrap();
|
let foo = config.syntaxes.get("emoji").unwrap();
|
||||||
@@ -596,7 +570,7 @@ mod tests {
|
|||||||
name = "too_short"
|
name = "too_short"
|
||||||
block_start = "<"
|
block_start = "<"
|
||||||
"#;
|
"#;
|
||||||
let config = Config::new(raw_config, None, None, None);
|
let config = Config::new(raw_config, None, None, None, None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expect_err(config).msg,
|
expect_err(config).msg,
|
||||||
r#"delimiters must be at least two characters long. The opening block delimiter ("<") is too short"#,
|
r#"delimiters must be at least two characters long. The opening block delimiter ("<") is too short"#,
|
||||||
@@ -607,7 +581,7 @@ mod tests {
|
|||||||
name = "contains_ws"
|
name = "contains_ws"
|
||||||
block_start = " {{ "
|
block_start = " {{ "
|
||||||
"#;
|
"#;
|
||||||
let config = Config::new(raw_config, None, None, None);
|
let config = Config::new(raw_config, None, None, None, None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expect_err(config).msg,
|
expect_err(config).msg,
|
||||||
r#"delimiters may not contain white spaces. The opening block delimiter (" {{ ") contains white spaces"#,
|
r#"delimiters may not contain white spaces. The opening block delimiter (" {{ ") contains white spaces"#,
|
||||||
@@ -620,7 +594,7 @@ mod tests {
|
|||||||
expr_start = "{{$"
|
expr_start = "{{$"
|
||||||
comment_start = "{{#"
|
comment_start = "{{#"
|
||||||
"#;
|
"#;
|
||||||
let config = Config::new(raw_config, None, None, None);
|
let config = Config::new(raw_config, None, None, None, None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expect_err(config).msg,
|
expect_err(config).msg,
|
||||||
r#"an opening delimiter may not be the prefix of another delimiter. The block delimiter ("{{") clashes with the expression delimiter ("{{$")"#,
|
r#"an opening delimiter may not be the prefix of another delimiter. The block delimiter ("{{") clashes with the expression delimiter ("{{$")"#,
|
||||||
@@ -635,7 +609,7 @@ mod tests {
|
|||||||
syntax = [{ name = "default" }]
|
syntax = [{ name = "default" }]
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let _config = Config::new(raw_config, None, None, None).unwrap();
|
let _config = Config::new(raw_config, None, None, None, None).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "config")]
|
#[cfg(feature = "config")]
|
||||||
@@ -647,7 +621,7 @@ mod tests {
|
|||||||
{ name = "foo", block_start = "%%" } ]
|
{ name = "foo", block_start = "%%" } ]
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let _config = Config::new(raw_config, None, None, None).unwrap();
|
let _config = Config::new(raw_config, None, None, None, None).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "config")]
|
#[cfg(feature = "config")]
|
||||||
@@ -659,7 +633,7 @@ mod tests {
|
|||||||
default_syntax = "foo"
|
default_syntax = "foo"
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let _config = Config::new(raw_config, None, None, None).unwrap();
|
let _config = Config::new(raw_config, None, None, None, None).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "config")]
|
#[cfg(feature = "config")]
|
||||||
@@ -674,21 +648,25 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(config.escapers, vec![
|
assert_eq!(
|
||||||
|
config.escapers,
|
||||||
|
vec![
|
||||||
(str_set(&["js"]), "::my_filters::Js".into()),
|
(str_set(&["js"]), "::my_filters::Js".into()),
|
||||||
(
|
(
|
||||||
str_set(&[
|
str_set(&[
|
||||||
"html", "htm", "j2", "jinja", "jinja2", "rinja", "svg", "xml"
|
"askama", "html", "htm", "j2", "jinja", "jinja2", "rinja", "svg", "xml"
|
||||||
]),
|
]),
|
||||||
"rinja::filters::Html".into()
|
"askama::filters::Html".into()
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
str_set(&["md", "none", "txt", "yml", ""]),
|
str_set(&["md", "none", "txt", "yml", ""]),
|
||||||
"rinja::filters::Text".into()
|
"askama::filters::Text".into()
|
||||||
),
|
),
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "config")]
|
#[cfg(feature = "config")]
|
||||||
@@ -702,12 +680,13 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(config.whitespace, WhitespaceHandling::Suppress);
|
assert_eq!(config.whitespace, Whitespace::Suppress);
|
||||||
|
|
||||||
let config = Config::new(r#""#, None, None, None).unwrap();
|
let config = Config::new(r#""#, None, None, None, None).unwrap();
|
||||||
assert_eq!(config.whitespace, WhitespaceHandling::Preserve);
|
assert_eq!(config.whitespace, Whitespace::Preserve);
|
||||||
|
|
||||||
let config = Config::new(
|
let config = Config::new(
|
||||||
r#"
|
r#"
|
||||||
@@ -717,9 +696,10 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(config.whitespace, WhitespaceHandling::Preserve);
|
assert_eq!(config.whitespace, Whitespace::Preserve);
|
||||||
|
|
||||||
let config = Config::new(
|
let config = Config::new(
|
||||||
r#"
|
r#"
|
||||||
@@ -729,9 +709,10 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(config.whitespace, WhitespaceHandling::Minimize);
|
assert_eq!(config.whitespace, Whitespace::Minimize);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "config")]
|
#[cfg(feature = "config")]
|
||||||
@@ -739,30 +720,21 @@ mod tests {
|
|||||||
fn test_whitespace_in_template() {
|
fn test_whitespace_in_template() {
|
||||||
// Checking that template arguments have precedence over general configuration.
|
// Checking that template arguments have precedence over general configuration.
|
||||||
// So in here, in the template arguments, there is `whitespace = "minimize"` so
|
// So in here, in the template arguments, there is `whitespace = "minimize"` so
|
||||||
// the `WhitespaceHandling` should be `Minimize` as well.
|
// the `Whitespace` should be `Minimize` as well.
|
||||||
let config = Config::new(
|
let config = Config::new(
|
||||||
r#"
|
r#"
|
||||||
[general]
|
[general]
|
||||||
whitespace = "suppress"
|
whitespace = "suppress"
|
||||||
"#,
|
"#,
|
||||||
None,
|
None,
|
||||||
Some("minimize"),
|
Some(Whitespace::Minimize),
|
||||||
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(config.whitespace, WhitespaceHandling::Minimize);
|
assert_eq!(config.whitespace, Whitespace::Minimize);
|
||||||
|
|
||||||
let config = Config::new(r#""#, None, Some("minimize"), None).unwrap();
|
let config = Config::new(r#""#, None, Some(Whitespace::Minimize), None, None).unwrap();
|
||||||
assert_eq!(config.whitespace, WhitespaceHandling::Minimize);
|
assert_eq!(config.whitespace, Whitespace::Minimize);
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_config_whitespace_error() {
|
|
||||||
let config = Config::new(r"", None, Some("trim"), None);
|
|
||||||
if let Err(err) = config {
|
|
||||||
assert_eq!(err.msg, "invalid value for `whitespace`: \"trim\"");
|
|
||||||
} else {
|
|
||||||
panic!("Config::new should have return an error");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
670
third_party/rust/askama_derive/src/generator.rs
vendored
Normal file
670
third_party/rust/askama_derive/src/generator.rs
vendored
Normal file
@@ -0,0 +1,670 @@
|
|||||||
|
mod expr;
|
||||||
|
mod node;
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::collections::hash_map::HashMap;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::str;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use parser::node::{Macro, Whitespace};
|
||||||
|
use parser::{
|
||||||
|
CharLit, Expr, FloatKind, IntKind, MAX_RUST_KEYWORD_LEN, Num, RUST_KEYWORDS, StrLit, WithSpan,
|
||||||
|
};
|
||||||
|
use rustc_hash::FxBuildHasher;
|
||||||
|
|
||||||
|
use crate::ascii_str::{AsciiChar, AsciiStr};
|
||||||
|
use crate::heritage::{Context, Heritage};
|
||||||
|
use crate::html::write_escaped_str;
|
||||||
|
use crate::input::{Source, TemplateInput};
|
||||||
|
use crate::integration::{Buffer, impl_everything, write_header};
|
||||||
|
use crate::{CompileError, FileInfo};
|
||||||
|
|
||||||
|
pub(crate) fn template_to_string(
|
||||||
|
buf: &mut Buffer,
|
||||||
|
input: &TemplateInput<'_>,
|
||||||
|
contexts: &HashMap<&Arc<Path>, Context<'_>, FxBuildHasher>,
|
||||||
|
heritage: Option<&Heritage<'_, '_>>,
|
||||||
|
tmpl_kind: TmplKind<'_>,
|
||||||
|
) -> Result<usize, CompileError> {
|
||||||
|
let generator = Generator::new(
|
||||||
|
input,
|
||||||
|
contexts,
|
||||||
|
heritage,
|
||||||
|
MapChain::default(),
|
||||||
|
input.block.is_some(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
let size_hint = match generator.impl_template(buf, tmpl_kind) {
|
||||||
|
Err(mut err) if err.span.is_none() => {
|
||||||
|
err.span = input.source_span;
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
result => result,
|
||||||
|
}?;
|
||||||
|
|
||||||
|
if tmpl_kind == TmplKind::Struct {
|
||||||
|
impl_everything(input.ast, buf);
|
||||||
|
}
|
||||||
|
Ok(size_hint)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum TmplKind<'a> {
|
||||||
|
/// [`askama::Template`]
|
||||||
|
Struct,
|
||||||
|
/// [`askama::helpers::EnumVariantTemplate`]
|
||||||
|
Variant,
|
||||||
|
/// Used in `blocks` implementation
|
||||||
|
#[allow(unused)]
|
||||||
|
Block(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Generator<'a, 'h> {
|
||||||
|
/// The template input state: original struct AST and attributes
|
||||||
|
input: &'a TemplateInput<'a>,
|
||||||
|
/// All contexts, keyed by the package-relative template path
|
||||||
|
contexts: &'a HashMap<&'a Arc<Path>, Context<'a>, FxBuildHasher>,
|
||||||
|
/// The heritage contains references to blocks and their ancestry
|
||||||
|
heritage: Option<&'h Heritage<'a, 'h>>,
|
||||||
|
/// Variables accessible directly from the current scope (not redirected to context)
|
||||||
|
locals: MapChain<'a>,
|
||||||
|
/// Suffix whitespace from the previous literal. Will be flushed to the
|
||||||
|
/// output buffer unless suppressed by whitespace suppression on the next
|
||||||
|
/// non-literal.
|
||||||
|
next_ws: Option<&'a str>,
|
||||||
|
/// Whitespace suppression from the previous non-literal. Will be used to
|
||||||
|
/// determine whether to flush prefix whitespace from the next literal.
|
||||||
|
skip_ws: Whitespace,
|
||||||
|
/// If currently in a block, this will contain the name of a potential parent block
|
||||||
|
super_block: Option<(&'a str, usize)>,
|
||||||
|
/// Buffer for writable
|
||||||
|
buf_writable: WritableBuffer<'a>,
|
||||||
|
/// Used in blocks to check if we are inside a filter block.
|
||||||
|
is_in_filter_block: usize,
|
||||||
|
/// Set of called macros we are currently in. Used to prevent (indirect) recursions.
|
||||||
|
seen_macros: Vec<(&'a Macro<'a>, Option<FileInfo<'a>>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'h> Generator<'a, 'h> {
|
||||||
|
fn new(
|
||||||
|
input: &'a TemplateInput<'a>,
|
||||||
|
contexts: &'a HashMap<&'a Arc<Path>, Context<'a>, FxBuildHasher>,
|
||||||
|
heritage: Option<&'h Heritage<'a, 'h>>,
|
||||||
|
locals: MapChain<'a>,
|
||||||
|
buf_writable_discard: bool,
|
||||||
|
is_in_filter_block: usize,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
input,
|
||||||
|
contexts,
|
||||||
|
heritage,
|
||||||
|
locals,
|
||||||
|
next_ws: None,
|
||||||
|
skip_ws: Whitespace::Preserve,
|
||||||
|
super_block: None,
|
||||||
|
buf_writable: WritableBuffer {
|
||||||
|
discard: buf_writable_discard,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
is_in_filter_block,
|
||||||
|
seen_macros: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement `Template` for the given context struct.
|
||||||
|
fn impl_template(
|
||||||
|
mut self,
|
||||||
|
buf: &mut Buffer,
|
||||||
|
tmpl_kind: TmplKind<'a>,
|
||||||
|
) -> Result<usize, CompileError> {
|
||||||
|
let ctx = &self.contexts[&self.input.path];
|
||||||
|
|
||||||
|
let target = match tmpl_kind {
|
||||||
|
TmplKind::Struct => "askama::Template",
|
||||||
|
TmplKind::Variant => "askama::helpers::EnumVariantTemplate",
|
||||||
|
TmplKind::Block(trait_name) => trait_name,
|
||||||
|
};
|
||||||
|
write_header(self.input.ast, buf, target);
|
||||||
|
buf.write(
|
||||||
|
"fn render_into_with_values<AskamaW>(\
|
||||||
|
&self,\
|
||||||
|
__askama_writer: &mut AskamaW,\
|
||||||
|
__askama_values: &dyn askama::Values\
|
||||||
|
) -> askama::Result<()>\
|
||||||
|
where \
|
||||||
|
AskamaW: askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized\
|
||||||
|
{\
|
||||||
|
#[allow(unused_imports)]\
|
||||||
|
use askama::{\
|
||||||
|
filters::{AutoEscape as _, WriteWritable as _},\
|
||||||
|
helpers::{ResultConverter as _, core::fmt::Write as _},\
|
||||||
|
};",
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(full_config_path) = &self.input.config.full_config_path {
|
||||||
|
buf.write(format_args!(
|
||||||
|
"const _: &[askama::helpers::core::primitive::u8] =\
|
||||||
|
askama::helpers::core::include_bytes!({:?});",
|
||||||
|
full_config_path.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the compiler understands that the generated code depends on the template files.
|
||||||
|
let mut paths = self
|
||||||
|
.contexts
|
||||||
|
.keys()
|
||||||
|
.map(|path| -> &Path { path })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
paths.sort();
|
||||||
|
for path in paths {
|
||||||
|
// Skip the fake path of templates defined in rust source.
|
||||||
|
let path_is_valid = match self.input.source {
|
||||||
|
Source::Path(_) => true,
|
||||||
|
Source::Source(_) => path != &*self.input.path,
|
||||||
|
};
|
||||||
|
if path_is_valid {
|
||||||
|
buf.write(format_args!(
|
||||||
|
"const _: &[askama::helpers::core::primitive::u8] =\
|
||||||
|
askama::helpers::core::include_bytes!({:#?});",
|
||||||
|
path.canonicalize().as_deref().unwrap_or(path),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let size_hint = self.impl_template_inner(ctx, buf)?;
|
||||||
|
|
||||||
|
buf.write("askama::Result::Ok(()) }");
|
||||||
|
if tmpl_kind == TmplKind::Struct {
|
||||||
|
buf.write(format_args!(
|
||||||
|
"const SIZE_HINT: askama::helpers::core::primitive::usize = {size_hint}usize;",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.write('}');
|
||||||
|
|
||||||
|
#[cfg(feature = "blocks")]
|
||||||
|
for block in self.input.blocks {
|
||||||
|
self.impl_block(buf, block)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(size_hint)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "blocks")]
|
||||||
|
fn impl_block(
|
||||||
|
&self,
|
||||||
|
buf: &mut Buffer,
|
||||||
|
block: &crate::input::Block,
|
||||||
|
) -> Result<(), CompileError> {
|
||||||
|
// RATIONALE: `*self` must be the input type, implementation details should not leak:
|
||||||
|
// - impl Self { fn as_block(self) } ->
|
||||||
|
// - struct __Askama__Self__as__block__Wrapper { this: self } ->
|
||||||
|
// - impl Template for __Askama__Self__as__block__Wrapper { fn render_into_with_values() } ->
|
||||||
|
// - impl __Askama__Self__as__block for Self { render_into_with_values() }
|
||||||
|
|
||||||
|
use quote::quote_spanned;
|
||||||
|
use syn::{GenericParam, Ident, Lifetime, LifetimeParam, Token};
|
||||||
|
|
||||||
|
let span = block.span;
|
||||||
|
buf.write(
|
||||||
|
"\
|
||||||
|
#[allow(missing_docs, non_camel_case_types, non_snake_case, unreachable_pub)]\
|
||||||
|
const _: () = {",
|
||||||
|
);
|
||||||
|
|
||||||
|
let ident = &self.input.ast.ident;
|
||||||
|
|
||||||
|
let doc = format!(
|
||||||
|
"A sub-template that renders only the block `{}` of [`{ident}`].",
|
||||||
|
block.name
|
||||||
|
);
|
||||||
|
let method_name = format!("as_{}", block.name);
|
||||||
|
let trait_name = format!("__Askama__{ident}__as__{}", block.name);
|
||||||
|
let wrapper_name = format!("__Askama__{ident}__as__{}__Wrapper", block.name);
|
||||||
|
let self_lt_name = format!("'__Askama__{ident}__as__{}__self", block.name);
|
||||||
|
|
||||||
|
let method_id = Ident::new(&method_name, span);
|
||||||
|
let trait_id = Ident::new(&trait_name, span);
|
||||||
|
let wrapper_id = Ident::new(&wrapper_name, span);
|
||||||
|
let self_lt = Lifetime::new(&self_lt_name, span);
|
||||||
|
|
||||||
|
// generics of the input with an additional lifetime to capture `self`
|
||||||
|
let mut wrapper_generics = self.input.ast.generics.clone();
|
||||||
|
if wrapper_generics.lt_token.is_none() {
|
||||||
|
wrapper_generics.lt_token = Some(Token);
|
||||||
|
wrapper_generics.gt_token = Some(Token);
|
||||||
|
}
|
||||||
|
wrapper_generics.params.insert(
|
||||||
|
0,
|
||||||
|
GenericParam::Lifetime(LifetimeParam::new(self_lt.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
let (impl_generics, ty_generics, where_clause) = self.input.ast.generics.split_for_impl();
|
||||||
|
let (wrapper_impl_generics, wrapper_ty_generics, wrapper_where_clause) =
|
||||||
|
wrapper_generics.split_for_impl();
|
||||||
|
|
||||||
|
let input = TemplateInput {
|
||||||
|
block: Some((&block.name, span)),
|
||||||
|
blocks: &[],
|
||||||
|
..self.input.clone()
|
||||||
|
};
|
||||||
|
let size_hint = template_to_string(
|
||||||
|
buf,
|
||||||
|
&input,
|
||||||
|
self.contexts,
|
||||||
|
self.heritage,
|
||||||
|
TmplKind::Block(&trait_name),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
buf.write(quote_spanned! {
|
||||||
|
span =>
|
||||||
|
pub trait #trait_id {
|
||||||
|
fn render_into_with_values<AskamaW>(
|
||||||
|
&self,
|
||||||
|
writer: &mut AskamaW,
|
||||||
|
values: &dyn askama::Values,
|
||||||
|
) -> askama::Result<()>
|
||||||
|
where
|
||||||
|
AskamaW:
|
||||||
|
askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #impl_generics #ident #ty_generics #where_clause {
|
||||||
|
#[inline]
|
||||||
|
#[doc = #doc]
|
||||||
|
pub fn #method_id(&self) -> impl askama::Template + '_ {
|
||||||
|
#wrapper_id {
|
||||||
|
this: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[askama::helpers::core::prelude::rust_2021::derive(
|
||||||
|
askama::helpers::core::prelude::rust_2021::Clone,
|
||||||
|
askama::helpers::core::prelude::rust_2021::Copy
|
||||||
|
)]
|
||||||
|
pub struct #wrapper_id #wrapper_generics #wrapper_where_clause {
|
||||||
|
this: &#self_lt #ident #ty_generics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #wrapper_impl_generics askama::Template
|
||||||
|
for #wrapper_id #wrapper_ty_generics #wrapper_where_clause {
|
||||||
|
#[inline]
|
||||||
|
fn render_into_with_values<AskamaW>(
|
||||||
|
&self,
|
||||||
|
writer: &mut AskamaW,
|
||||||
|
values: &dyn askama::Values
|
||||||
|
) -> askama::Result<()>
|
||||||
|
where
|
||||||
|
AskamaW: askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized
|
||||||
|
{
|
||||||
|
<_ as #trait_id>::render_into_with_values(self.this, writer, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIZE_HINT: askama::helpers::core::primitive::usize = #size_hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cannot use `crate::integrations::impl_fast_writable()` w/o cloning the struct
|
||||||
|
impl #wrapper_impl_generics askama::filters::FastWritable
|
||||||
|
for #wrapper_id #wrapper_ty_generics #wrapper_where_clause {
|
||||||
|
#[inline]
|
||||||
|
fn write_into<AskamaW>(&self, dest: &mut AskamaW) -> askama::Result<()>
|
||||||
|
where
|
||||||
|
AskamaW: askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized
|
||||||
|
{
|
||||||
|
<_ as askama::Template>::render_into(self, dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cannot use `crate::integrations::impl_display()` w/o cloning the struct
|
||||||
|
impl #wrapper_impl_generics askama::helpers::core::fmt::Display
|
||||||
|
for #wrapper_id #wrapper_ty_generics #wrapper_where_clause {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(
|
||||||
|
&self,
|
||||||
|
f: &mut askama::helpers::core::fmt::Formatter<'_>
|
||||||
|
) -> askama::helpers::core::fmt::Result {
|
||||||
|
<_ as askama::Template>::render_into(self, f)
|
||||||
|
.map_err(|_| askama::helpers::core::fmt::Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
buf.write("};");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_var_defined(&self, var_name: &str) -> bool {
|
||||||
|
self.locals.get(var_name).is_some() || self.input.fields.iter().any(|f| f == var_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "16")]
|
||||||
|
type TargetIsize = i16;
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
type TargetIsize = i32;
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
type TargetIsize = i64;
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "16")]
|
||||||
|
type TargetUsize = u16;
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
type TargetUsize = u32;
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
type TargetUsize = u64;
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_pointer_width = "16",
|
||||||
|
target_pointer_width = "32",
|
||||||
|
target_pointer_width = "64"
|
||||||
|
)))]
|
||||||
|
const _: () = {
|
||||||
|
panic!("unknown cfg!(target_pointer_width)");
|
||||||
|
};
|
||||||
|
|
||||||
|
/// In here, we inspect in the expression if it is a literal, and if it is, whether it
|
||||||
|
/// can be escaped at compile time.
|
||||||
|
fn compile_time_escape<'a>(expr: &Expr<'a>, escaper: &str) -> Option<Writable<'a>> {
|
||||||
|
// we only optimize for known escapers
|
||||||
|
enum OutputKind {
|
||||||
|
Html,
|
||||||
|
Text,
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only optimize for known escapers
|
||||||
|
let output = match escaper.strip_prefix("askama::filters::")? {
|
||||||
|
"Html" => OutputKind::Html,
|
||||||
|
"Text" => OutputKind::Text,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// for now, we only escape strings, chars, numbers, and bools at compile time
|
||||||
|
let value = match *expr {
|
||||||
|
Expr::StrLit(StrLit {
|
||||||
|
prefix: None,
|
||||||
|
content,
|
||||||
|
}) => {
|
||||||
|
if content.find('\\').is_none() {
|
||||||
|
// if the literal does not contain any backslashes, then it does not need unescaping
|
||||||
|
Cow::Borrowed(content)
|
||||||
|
} else {
|
||||||
|
// the input could be string escaped if it contains any backslashes
|
||||||
|
let input = format!(r#""{content}""#);
|
||||||
|
let input = input.parse().ok()?;
|
||||||
|
let input = syn::parse2::<syn::LitStr>(input).ok()?;
|
||||||
|
Cow::Owned(input.value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::CharLit(CharLit {
|
||||||
|
prefix: None,
|
||||||
|
content,
|
||||||
|
}) => {
|
||||||
|
if content.find('\\').is_none() {
|
||||||
|
// if the literal does not contain any backslashes, then it does not need unescaping
|
||||||
|
Cow::Borrowed(content)
|
||||||
|
} else {
|
||||||
|
// the input could be string escaped if it contains any backslashes
|
||||||
|
let input = format!(r#"'{content}'"#);
|
||||||
|
let input = input.parse().ok()?;
|
||||||
|
let input = syn::parse2::<syn::LitChar>(input).ok()?;
|
||||||
|
Cow::Owned(input.value().to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::NumLit(_, value) => {
|
||||||
|
enum NumKind {
|
||||||
|
Int(Option<IntKind>),
|
||||||
|
Float(Option<FloatKind>),
|
||||||
|
}
|
||||||
|
|
||||||
|
let (orig_value, kind) = match value {
|
||||||
|
Num::Int(value, kind) => (value, NumKind::Int(kind)),
|
||||||
|
Num::Float(value, kind) => (value, NumKind::Float(kind)),
|
||||||
|
};
|
||||||
|
let value = match orig_value.chars().any(|c| c == '_') {
|
||||||
|
true => Cow::Owned(orig_value.chars().filter(|&c| c != '_').collect()),
|
||||||
|
false => Cow::Borrowed(orig_value),
|
||||||
|
};
|
||||||
|
|
||||||
|
fn int<T: ToString, E>(
|
||||||
|
from_str_radix: impl Fn(&str, u32) -> Result<T, E>,
|
||||||
|
value: &str,
|
||||||
|
) -> Option<String> {
|
||||||
|
Some(from_str_radix(value, 10).ok()?.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = match kind {
|
||||||
|
NumKind::Int(Some(IntKind::I8)) => int(i8::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::I16)) => int(i16::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::I32)) => int(i32::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::I64)) => int(i64::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::I128)) => int(i128::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::Isize)) => int(TargetIsize::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::U8)) => int(u8::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::U16)) => int(u16::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::U32)) => int(u32::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::U64)) => int(u64::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::U128)) => int(u128::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(Some(IntKind::Usize)) => int(TargetUsize::from_str_radix, &value)?,
|
||||||
|
NumKind::Int(None) => match value.starts_with('-') {
|
||||||
|
true => int(i128::from_str_radix, &value)?,
|
||||||
|
false => int(u128::from_str_radix, &value)?,
|
||||||
|
},
|
||||||
|
NumKind::Float(Some(FloatKind::F32)) => value.parse::<f32>().ok()?.to_string(),
|
||||||
|
NumKind::Float(Some(FloatKind::F64) | None) => {
|
||||||
|
value.parse::<f64>().ok()?.to_string()
|
||||||
|
}
|
||||||
|
// FIXME: implement once `f16` and `f128` are available
|
||||||
|
NumKind::Float(Some(FloatKind::F16 | FloatKind::F128)) => return None,
|
||||||
|
};
|
||||||
|
match value == orig_value {
|
||||||
|
true => Cow::Borrowed(orig_value),
|
||||||
|
false => Cow::Owned(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::BoolLit(true) => Cow::Borrowed("true"),
|
||||||
|
Expr::BoolLit(false) => Cow::Borrowed("false"),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// escape the un-string-escaped input using the selected escaper
|
||||||
|
Some(Writable::Lit(match output {
|
||||||
|
OutputKind::Text => value,
|
||||||
|
OutputKind::Html => {
|
||||||
|
let mut escaped = String::with_capacity(value.len() + 20);
|
||||||
|
write_escaped_str(&mut escaped, &value).ok()?;
|
||||||
|
match escaped == value {
|
||||||
|
true => value,
|
||||||
|
false => Cow::Owned(escaped),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
struct LocalMeta {
|
||||||
|
refs: Option<String>,
|
||||||
|
initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalMeta {
|
||||||
|
fn initialized() -> Self {
|
||||||
|
Self {
|
||||||
|
refs: None,
|
||||||
|
initialized: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_ref(refs: String) -> Self {
|
||||||
|
Self {
|
||||||
|
refs: Some(refs),
|
||||||
|
initialized: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MapChain<'a> {
|
||||||
|
scopes: Vec<HashMap<Cow<'a, str>, LocalMeta, FxBuildHasher>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MapChain<'a> {
|
||||||
|
fn new_empty() -> Self {
|
||||||
|
Self { scopes: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates the scopes in reverse and returns `Some(LocalMeta)`
|
||||||
|
/// from the first scope where `key` exists.
|
||||||
|
fn get<'b>(&'b self, key: &str) -> Option<&'b LocalMeta> {
|
||||||
|
self.scopes.iter().rev().find_map(|set| set.get(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_current_empty(&self) -> bool {
|
||||||
|
self.scopes.last().unwrap().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, key: Cow<'a, str>, val: LocalMeta) {
|
||||||
|
self.scopes.last_mut().unwrap().insert(key, val);
|
||||||
|
|
||||||
|
// Note that if `insert` returns `Some` then it implies
|
||||||
|
// an identifier is reused. For e.g. `{% macro f(a, a) %}`
|
||||||
|
// and `{% let (a, a) = ... %}` then this results in a
|
||||||
|
// generated template, which when compiled fails with the
|
||||||
|
// compile error "identifier `a` used more than once".
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_with_default(&mut self, key: Cow<'a, str>) {
|
||||||
|
self.insert(key, LocalMeta::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(&self, name: &str) -> Option<String> {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
|
self.get(&Cow::Borrowed(name)).map(|meta| match &meta.refs {
|
||||||
|
Some(expr) => expr.clone(),
|
||||||
|
None => name.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_or_self(&self, name: &str) -> String {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
|
self.resolve(name).unwrap_or_else(|| format!("self.{name}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MapChain<'_> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
scopes: vec![HashMap::default()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if enough assumptions can be made,
|
||||||
|
/// to determine that `self` is copyable.
|
||||||
|
fn is_copyable(expr: &Expr<'_>) -> bool {
|
||||||
|
is_copyable_within_op(expr, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_copyable_within_op(expr: &Expr<'_>, within_op: bool) -> bool {
|
||||||
|
match expr {
|
||||||
|
Expr::BoolLit(_)
|
||||||
|
| Expr::NumLit(_, _)
|
||||||
|
| Expr::StrLit(_)
|
||||||
|
| Expr::CharLit(_)
|
||||||
|
| Expr::BinOp(_, _, _) => true,
|
||||||
|
Expr::Unary(.., expr) => is_copyable_within_op(expr, true),
|
||||||
|
Expr::Range(..) => true,
|
||||||
|
// The result of a call likely doesn't need to be borrowed,
|
||||||
|
// as in that case the call is more likely to return a
|
||||||
|
// reference in the first place then.
|
||||||
|
Expr::Call { .. } | Expr::Path(..) | Expr::Filter(..) | Expr::RustMacro(..) => true,
|
||||||
|
// If the `expr` is within a `Unary` or `BinOp` then
|
||||||
|
// an assumption can be made that the operand is copy.
|
||||||
|
// If not, then the value is moved and adding `.clone()`
|
||||||
|
// will solve that issue. However, if the operand is
|
||||||
|
// implicitly borrowed, then it's likely not even possible
|
||||||
|
// to get the template to compile.
|
||||||
|
_ => within_op && is_attr_self(expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this is an `Attr` where the `obj` is `"self"`.
|
||||||
|
fn is_attr_self(mut expr: &Expr<'_>) -> bool {
|
||||||
|
loop {
|
||||||
|
match expr {
|
||||||
|
Expr::Attr(obj, _) if matches!(***obj, Expr::Var("self")) => return true,
|
||||||
|
Expr::Attr(obj, _) if matches!(***obj, Expr::Attr(..)) => expr = obj,
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FILTER_SOURCE: &str = "__askama_filter_block";
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum DisplayWrap {
|
||||||
|
Wrapped,
|
||||||
|
Unwrapped,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
struct WritableBuffer<'a> {
|
||||||
|
buf: Vec<Writable<'a>>,
|
||||||
|
discard: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> WritableBuffer<'a> {
|
||||||
|
fn push(&mut self, writable: Writable<'a>) {
|
||||||
|
if !self.discard {
|
||||||
|
self.buf.push(writable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for WritableBuffer<'a> {
|
||||||
|
type Target = [Writable<'a>];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.buf[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Writable<'a> {
|
||||||
|
Lit(Cow<'a, str>),
|
||||||
|
Expr(&'a WithSpan<'a, Expr<'a>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Identifiers to be replaced with raw identifiers, so as to avoid
|
||||||
|
/// collisions between template syntax and Rust's syntax. In particular
|
||||||
|
/// [Rust keywords](https://doc.rust-lang.org/reference/keywords.html)
|
||||||
|
/// should be replaced, since they're not reserved words in Askama
|
||||||
|
/// syntax but have a high probability of causing problems in the
|
||||||
|
/// generated code.
|
||||||
|
///
|
||||||
|
/// This list excludes the Rust keywords *self*, *Self*, and *super*
|
||||||
|
/// because they are not allowed to be raw identifiers, and *loop*
|
||||||
|
/// because it's used something like a keyword in the template
|
||||||
|
/// language.
|
||||||
|
fn normalize_identifier(ident: &str) -> &str {
|
||||||
|
// This table works for as long as the replacement string is the original string
|
||||||
|
// prepended with "r#". The strings get right-padded to the same length with b'_'.
|
||||||
|
// While the code does not need it, please keep the list sorted when adding new
|
||||||
|
// keywords.
|
||||||
|
|
||||||
|
if ident.len() > MAX_RUST_KEYWORD_LEN {
|
||||||
|
return ident;
|
||||||
|
}
|
||||||
|
let kws = RUST_KEYWORDS[ident.len()];
|
||||||
|
|
||||||
|
let mut padded_ident = [0; MAX_RUST_KEYWORD_LEN];
|
||||||
|
padded_ident[..ident.len()].copy_from_slice(ident.as_bytes());
|
||||||
|
|
||||||
|
// Since the individual buckets are quite short, a linear search is faster than a binary search.
|
||||||
|
for probe in kws {
|
||||||
|
if padded_ident == *AsciiChar::slice_as_bytes(probe[2..].try_into().unwrap()) {
|
||||||
|
return AsciiStr::from_slice(&probe[..ident.len() + 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ident
|
||||||
|
}
|
||||||
1302
third_party/rust/askama_derive/src/generator/expr.rs
vendored
Normal file
1302
third_party/rust/askama_derive/src/generator/expr.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1558
third_party/rust/askama_derive/src/generator/node.rs
vendored
Normal file
1558
third_party/rust/askama_derive/src/generator/node.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,42 +1,44 @@
|
|||||||
|
use core::fmt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use parser::node::{BlockDef, Macro};
|
use parser::node::{BlockDef, Macro};
|
||||||
use parser::{Node, Parsed, WithSpan};
|
use parser::{Node, Parsed, Span};
|
||||||
use rustc_hash::FxBuildHasher;
|
use rustc_hash::FxBuildHasher;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::{CompileError, FileInfo};
|
use crate::{CompileError, FileInfo};
|
||||||
|
|
||||||
pub(crate) struct Heritage<'a> {
|
pub(crate) struct Heritage<'a, 'h> {
|
||||||
pub(crate) root: &'a Context<'a>,
|
pub(crate) root: &'h Context<'a>,
|
||||||
pub(crate) blocks: BlockAncestry<'a>,
|
pub(crate) blocks: BlockAncestry<'a, 'h>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Heritage<'_> {
|
impl<'a, 'h> Heritage<'a, 'h> {
|
||||||
pub(crate) fn new<'n>(
|
pub(crate) fn new(
|
||||||
mut ctx: &'n Context<'n>,
|
mut root: &'h Context<'a>,
|
||||||
contexts: &'n HashMap<&'n Arc<Path>, Context<'n>, FxBuildHasher>,
|
contexts: &'a HashMap<&'a Arc<Path>, Context<'a>, FxBuildHasher>,
|
||||||
) -> Heritage<'n> {
|
) -> Self {
|
||||||
let mut blocks: BlockAncestry<'n> = ctx
|
let mut blocks: BlockAncestry<'a, 'h> = root
|
||||||
.blocks
|
.blocks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, def)| (*name, vec![(ctx, *def)]))
|
.map(|(name, def)| (*name, vec![(root, *def)]))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
while let Some(path) = &ctx.extends {
|
while let Some(path) = &root.extends {
|
||||||
ctx = &contexts[path];
|
root = &contexts[path];
|
||||||
for (name, def) in &ctx.blocks {
|
for (name, def) in &root.blocks {
|
||||||
blocks.entry(name).or_default().push((ctx, def));
|
blocks.entry(name).or_default().push((root, def));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Heritage { root: ctx, blocks }
|
Self { root, blocks }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlockAncestry<'a> = HashMap<&'a str, Vec<(&'a Context<'a>, &'a BlockDef<'a>)>, FxBuildHasher>;
|
type BlockAncestry<'a, 'h> =
|
||||||
|
HashMap<&'a str, Vec<(&'h Context<'a>, &'a BlockDef<'a>)>, FxBuildHasher>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct Context<'a> {
|
pub(crate) struct Context<'a> {
|
||||||
@@ -49,7 +51,7 @@ pub(crate) struct Context<'a> {
|
|||||||
pub(crate) parsed: &'a Parsed,
|
pub(crate) parsed: &'a Parsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context<'_> {
|
impl<'a> Context<'a> {
|
||||||
pub(crate) fn empty(parsed: &Parsed) -> Context<'_> {
|
pub(crate) fn empty(parsed: &Parsed) -> Context<'_> {
|
||||||
Context {
|
Context {
|
||||||
nodes: &[],
|
nodes: &[],
|
||||||
@@ -62,11 +64,11 @@ impl Context<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new<'n>(
|
pub(crate) fn new(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
path: &'n Path,
|
path: &'a Path,
|
||||||
parsed: &'n Parsed,
|
parsed: &'a Parsed,
|
||||||
) -> Result<Context<'n>, CompileError> {
|
) -> Result<Self, CompileError> {
|
||||||
let mut extends = None;
|
let mut extends = None;
|
||||||
let mut blocks = HashMap::default();
|
let mut blocks = HashMap::default();
|
||||||
let mut macros = HashMap::default();
|
let mut macros = HashMap::default();
|
||||||
@@ -78,29 +80,29 @@ impl Context<'_> {
|
|||||||
for n in nodes {
|
for n in nodes {
|
||||||
match n {
|
match n {
|
||||||
Node::Extends(e) => {
|
Node::Extends(e) => {
|
||||||
ensure_top(top, e, path, parsed, "extends")?;
|
ensure_top(top, e.span(), path, parsed, "extends")?;
|
||||||
if extends.is_some() {
|
if extends.is_some() {
|
||||||
return Err(CompileError::new(
|
return Err(CompileError::new(
|
||||||
"multiple extend blocks found",
|
"multiple extend blocks found",
|
||||||
Some(FileInfo::of(e, path, parsed)),
|
Some(FileInfo::of(e.span(), path, parsed)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
extends = Some(config.find_template(
|
extends = Some(config.find_template(
|
||||||
e.path,
|
e.path,
|
||||||
Some(path),
|
Some(path),
|
||||||
Some(FileInfo::of(e, path, parsed)),
|
Some(FileInfo::of(e.span(), path, parsed)),
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
Node::Macro(m) => {
|
Node::Macro(m) => {
|
||||||
ensure_top(top, m, path, parsed, "macro")?;
|
ensure_top(top, m.span(), path, parsed, "macro")?;
|
||||||
macros.insert(m.name, &**m);
|
macros.insert(m.name, &**m);
|
||||||
}
|
}
|
||||||
Node::Import(import) => {
|
Node::Import(import) => {
|
||||||
ensure_top(top, import, path, parsed, "import")?;
|
ensure_top(top, import.span(), path, parsed, "import")?;
|
||||||
let path = config.find_template(
|
let path = config.find_template(
|
||||||
import.path,
|
import.path,
|
||||||
Some(path),
|
Some(path),
|
||||||
Some(FileInfo::of(import, path, parsed)),
|
Some(FileInfo::of(import.span(), path, parsed)),
|
||||||
)?;
|
)?;
|
||||||
imports.insert(import.scope, path);
|
imports.insert(import.scope, path);
|
||||||
}
|
}
|
||||||
@@ -139,17 +141,18 @@ impl Context<'_> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn generate_error<T>(&self, msg: &str, node: &WithSpan<'_, T>) -> CompileError {
|
pub(crate) fn generate_error(&self, msg: impl fmt::Display, node: Span<'_>) -> CompileError {
|
||||||
CompileError::new(
|
CompileError::new(msg, self.file_info_of(node))
|
||||||
msg,
|
}
|
||||||
self.path.map(|path| FileInfo::of(node, path, self.parsed)),
|
|
||||||
)
|
pub(crate) fn file_info_of(&self, node: Span<'a>) -> Option<FileInfo<'a>> {
|
||||||
|
self.path.map(|path| FileInfo::of(node, path, self.parsed))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_top<T>(
|
fn ensure_top(
|
||||||
top: bool,
|
top: bool,
|
||||||
node: &WithSpan<'_, T>,
|
node: Span<'_>,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
parsed: &Parsed,
|
parsed: &Parsed,
|
||||||
kind: &str,
|
kind: &str,
|
||||||
@@ -1,4 +1,11 @@
|
|||||||
use std::{fmt, str};
|
// The file is shared across many crates, not all have this feature.
|
||||||
|
// If they don't then the tests won't be compiled in, but that's OK, because they are executed at
|
||||||
|
// least in the crate `askama`. There's no need to run the test multiple times.
|
||||||
|
#![allow(unexpected_cfgs)]
|
||||||
|
|
||||||
|
use core::{fmt, str};
|
||||||
|
|
||||||
|
use crate::ascii_str::{AsciiChar, AsciiStr};
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn write_escaped_str(mut dest: impl fmt::Write, src: &str) -> fmt::Result {
|
pub(crate) fn write_escaped_str(mut dest: impl fmt::Write, src: &str) -> fmt::Result {
|
||||||
@@ -12,8 +19,7 @@ pub(crate) fn write_escaped_str(mut dest: impl fmt::Write, src: &str) -> fmt::Re
|
|||||||
if let Some(escaped) = get_escaped(byte) {
|
if let Some(escaped) = get_escaped(byte) {
|
||||||
[escaped_buf[2], escaped_buf[3]] = escaped;
|
[escaped_buf[2], escaped_buf[3]] = escaped;
|
||||||
write_str_if_nonempty(&mut dest, &src[last..index])?;
|
write_str_if_nonempty(&mut dest, &src[last..index])?;
|
||||||
// SAFETY: the content of `escaped_buf` is pure ASCII
|
dest.write_str(AsciiStr::from_slice(&escaped_buf[..ESCAPED_BUF_LEN]))?;
|
||||||
dest.write_str(unsafe { str::from_utf8_unchecked(&escaped_buf[..ESCAPED_BUF_LEN]) })?;
|
|
||||||
last = index + 1;
|
last = index + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,8 +33,7 @@ pub(crate) fn write_escaped_char(mut dest: impl fmt::Write, c: char) -> fmt::Res
|
|||||||
} else if let Some(escaped) = get_escaped(c as u8) {
|
} else if let Some(escaped) = get_escaped(c as u8) {
|
||||||
let mut escaped_buf = ESCAPED_BUF_INIT;
|
let mut escaped_buf = ESCAPED_BUF_INIT;
|
||||||
[escaped_buf[2], escaped_buf[3]] = escaped;
|
[escaped_buf[2], escaped_buf[3]] = escaped;
|
||||||
// SAFETY: the content of `escaped_buf` is pure ASCII
|
dest.write_str(AsciiStr::from_slice(&escaped_buf[..ESCAPED_BUF_LEN]))
|
||||||
dest.write_str(unsafe { str::from_utf8_unchecked(&escaped_buf[..ESCAPED_BUF_LEN]) })
|
|
||||||
} else {
|
} else {
|
||||||
// RATIONALE: `write_char(c)` gets optimized if it is known that `c.is_ascii()`
|
// RATIONALE: `write_char(c)` gets optimized if it is known that `c.is_ascii()`
|
||||||
dest.write_char(c)
|
dest.write_char(c)
|
||||||
@@ -36,14 +41,13 @@ pub(crate) fn write_escaped_char(mut dest: impl fmt::Write, c: char) -> fmt::Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the decimal representation of the codepoint if the character needs HTML escaping.
|
/// Returns the decimal representation of the codepoint if the character needs HTML escaping.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn get_escaped(byte: u8) -> Option<[u8; 2]> {
|
fn get_escaped(byte: u8) -> Option<[AsciiChar; 2]> {
|
||||||
match byte {
|
if let MIN_CHAR..=MAX_CHAR = byte {
|
||||||
MIN_CHAR..=MAX_CHAR => match TABLE.lookup[(byte - MIN_CHAR) as usize] {
|
let entry = TABLE.0[(byte - MIN_CHAR) as usize];
|
||||||
0 => None,
|
(entry != UNESCAPED).then_some(entry)
|
||||||
escaped => Some(escaped.to_ne_bytes()),
|
} else {
|
||||||
},
|
None
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,36 +92,35 @@ const MAX_CHAR: u8 = {
|
|||||||
/// Number of codepoints between the lowest and highest character that needs escaping, incl.
|
/// Number of codepoints between the lowest and highest character that needs escaping, incl.
|
||||||
const CHAR_RANGE: usize = (MAX_CHAR - MIN_CHAR + 1) as usize;
|
const CHAR_RANGE: usize = (MAX_CHAR - MIN_CHAR + 1) as usize;
|
||||||
|
|
||||||
struct Table {
|
#[repr(align(64))]
|
||||||
_align: [usize; 0],
|
struct Table([[AsciiChar; 2]; CHAR_RANGE]);
|
||||||
lookup: [u16; CHAR_RANGE],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For characters that need HTML escaping, the codepoint is formatted as decimal digits,
|
/// For characters that need HTML escaping, the codepoint is formatted as decimal digits,
|
||||||
/// otherwise `b"\0\0"`. Starting at [`MIN_CHAR`].
|
/// otherwise `b"\0\0"`. Starting at [`MIN_CHAR`].
|
||||||
const TABLE: Table = {
|
const TABLE: &Table = &{
|
||||||
let mut table = Table {
|
let mut table = Table([UNESCAPED; CHAR_RANGE]);
|
||||||
_align: [],
|
|
||||||
lookup: [0; CHAR_RANGE],
|
|
||||||
};
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < CHARS.len() {
|
while i < CHARS.len() {
|
||||||
let c = CHARS[i];
|
let c = CHARS[i];
|
||||||
let h = c / 10 + b'0';
|
table.0[c as u32 as usize - MIN_CHAR as usize] = AsciiChar::two_digits(c as u32);
|
||||||
let l = c % 10 + b'0';
|
|
||||||
table.lookup[(c - MIN_CHAR) as usize] = u16::from_ne_bytes([h, l]);
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
table
|
table
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const UNESCAPED: [AsciiChar; 2] = AsciiStr::new_sized("");
|
||||||
|
|
||||||
|
const ESCAPED_BUF_INIT_UNPADDED: &str = "&#__;";
|
||||||
// RATIONALE: llvm generates better code if the buffer is register sized
|
// RATIONALE: llvm generates better code if the buffer is register sized
|
||||||
const ESCAPED_BUF_INIT: [u8; 8] = *b"&#__;\0\0\0";
|
const ESCAPED_BUF_INIT: [AsciiChar; 8] = AsciiStr::new_sized(ESCAPED_BUF_INIT_UNPADDED);
|
||||||
const ESCAPED_BUF_LEN: usize = b"&#__;".len();
|
const ESCAPED_BUF_LEN: usize = ESCAPED_BUF_INIT_UNPADDED.len();
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
fn test_simple_html_string_escaping() {
|
fn test_simple_html_string_escaping() {
|
||||||
let mut buf = String::new();
|
extern crate alloc;
|
||||||
|
|
||||||
|
let mut buf = alloc::string::String::new();
|
||||||
write_escaped_str(&mut buf, "<script>").unwrap();
|
write_escaped_str(&mut buf, "<script>").unwrap();
|
||||||
assert_eq!(buf, "<script>");
|
assert_eq!(buf, "<script>");
|
||||||
|
|
||||||
1066
third_party/rust/askama_derive/src/input.rs
vendored
Normal file
1066
third_party/rust/askama_derive/src/input.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
528
third_party/rust/askama_derive/src/integration.rs
vendored
Normal file
528
third_party/rust/askama_derive/src/integration.rs
vendored
Normal file
@@ -0,0 +1,528 @@
|
|||||||
|
use std::fmt::{Arguments, Display, Write};
|
||||||
|
|
||||||
|
use proc_macro2::{TokenStream, TokenTree};
|
||||||
|
use quote::{ToTokens, quote};
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
use syn::{
|
||||||
|
Data, DeriveInput, Fields, GenericParam, Generics, Ident, Lifetime, LifetimeParam, Token, Type,
|
||||||
|
Variant, parse_quote,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::generator::TmplKind;
|
||||||
|
use crate::input::{PartialTemplateArgs, TemplateArgs};
|
||||||
|
use crate::{CompileError, build_template_item};
|
||||||
|
|
||||||
|
/// Implement every integration for the given item
|
||||||
|
pub(crate) fn impl_everything(ast: &DeriveInput, buf: &mut Buffer) {
|
||||||
|
impl_display(ast, buf);
|
||||||
|
impl_fast_writable(ast, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes header for the `impl` for `TraitFromPathName` or `Template` for the given item
|
||||||
|
pub(crate) fn write_header(ast: &DeriveInput, buf: &mut Buffer, target: impl Display) {
|
||||||
|
let (impl_generics, orig_ty_generics, where_clause) = ast.generics.split_for_impl();
|
||||||
|
|
||||||
|
let ident = &ast.ident;
|
||||||
|
buf.write(format_args!(
|
||||||
|
"impl {} {} for {} {{",
|
||||||
|
quote!(#impl_generics),
|
||||||
|
target,
|
||||||
|
quote!(#ident #orig_ty_generics #where_clause),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement `Display` for the given item.
|
||||||
|
fn impl_display(ast: &DeriveInput, buf: &mut Buffer) {
|
||||||
|
let ident = &ast.ident;
|
||||||
|
buf.write(format_args!(
|
||||||
|
"\
|
||||||
|
/// Implement the [`format!()`][askama::helpers::std::format] trait for [`{}`]\n\
|
||||||
|
///\n\
|
||||||
|
/// Please be aware of the rendering performance notice in the \
|
||||||
|
[`Template`][askama::Template] trait.\n\
|
||||||
|
",
|
||||||
|
quote!(#ident),
|
||||||
|
));
|
||||||
|
write_header(ast, buf, "askama::helpers::core::fmt::Display");
|
||||||
|
buf.write(
|
||||||
|
"\
|
||||||
|
#[inline]\
|
||||||
|
fn fmt(\
|
||||||
|
&self,\
|
||||||
|
f: &mut askama::helpers::core::fmt::Formatter<'_>\
|
||||||
|
) -> askama::helpers::core::fmt::Result {\
|
||||||
|
askama::Template::render_into(self, f)\
|
||||||
|
.map_err(|_| askama::helpers::core::fmt::Error)\
|
||||||
|
}\
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement `FastWritable` for the given item.
|
||||||
|
fn impl_fast_writable(ast: &DeriveInput, buf: &mut Buffer) {
|
||||||
|
write_header(ast, buf, "askama::filters::FastWritable");
|
||||||
|
buf.write(
|
||||||
|
"\
|
||||||
|
#[inline]\
|
||||||
|
fn write_into<AskamaW>(&self, dest: &mut AskamaW) -> askama::Result<()> \
|
||||||
|
where \
|
||||||
|
AskamaW: askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized,\
|
||||||
|
{\
|
||||||
|
askama::Template::render_into(self, dest)\
|
||||||
|
}\
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Buffer {
|
||||||
|
// The buffer to generate the code into
|
||||||
|
buf: String,
|
||||||
|
discard: bool,
|
||||||
|
last_was_write_str: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Buffer {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(&self.buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffer {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
buf: String::new(),
|
||||||
|
discard: false,
|
||||||
|
last_was_write_str: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn into_string(self) -> String {
|
||||||
|
self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_discard(&self) -> bool {
|
||||||
|
self.discard
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_discard(&mut self, discard: bool) {
|
||||||
|
self.discard = discard;
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write(&mut self, src: impl BufferFmt) {
|
||||||
|
if self.discard {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
|
||||||
|
src.append_to(&mut self.buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_separated_path(&mut self, path: &[&str]) {
|
||||||
|
if self.discard {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
|
||||||
|
for (idx, item) in path.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
self.buf.push_str("::");
|
||||||
|
}
|
||||||
|
self.buf.push_str(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_escaped_str(&mut self, s: &str) {
|
||||||
|
if self.discard {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
|
||||||
|
self.buf.push('"');
|
||||||
|
string_escape(&mut self.buf, s);
|
||||||
|
self.buf.push('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_writer(&mut self, s: &str) -> usize {
|
||||||
|
const OPEN: &str = r#"__askama_writer.write_str(""#;
|
||||||
|
const CLOSE: &str = r#"")?;"#;
|
||||||
|
|
||||||
|
if !s.is_empty() && !self.discard {
|
||||||
|
if !self.last_was_write_str {
|
||||||
|
self.last_was_write_str = true;
|
||||||
|
self.buf.push_str(OPEN);
|
||||||
|
} else {
|
||||||
|
// strip trailing `")?;`, leaving an unterminated string
|
||||||
|
self.buf.truncate(self.buf.len() - CLOSE.len());
|
||||||
|
}
|
||||||
|
string_escape(&mut self.buf, s);
|
||||||
|
self.buf.push_str(CLOSE);
|
||||||
|
}
|
||||||
|
s.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clear(&mut self) {
|
||||||
|
self.buf.clear();
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_mark(&mut self) -> usize {
|
||||||
|
self.buf.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn marked_text(&self, mark: usize) -> &str {
|
||||||
|
&self.buf[..mark]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait BufferFmt {
|
||||||
|
fn append_to(&self, buf: &mut String);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BufferFmt + ?Sized> BufferFmt for &T {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
T::append_to(self, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferFmt for char {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
buf.push(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferFmt for str {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
buf.push_str(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferFmt for String {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
buf.push_str(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferFmt for Arguments<'_> {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
buf.write_fmt(*self).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferFmt for TokenStream {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
write!(buf, "{self}").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Similar to `write!(dest, "{src:?}")`, but only escapes the strictly needed characters,
|
||||||
|
/// and without the surrounding `"…"` quotation marks.
|
||||||
|
fn string_escape(dest: &mut String, src: &str) {
|
||||||
|
// SAFETY: we will only push valid str slices
|
||||||
|
let dest = unsafe { dest.as_mut_vec() };
|
||||||
|
let src = src.as_bytes();
|
||||||
|
let mut last = 0;
|
||||||
|
|
||||||
|
// According to <https://doc.rust-lang.org/reference/tokens.html#string-literals>, every
|
||||||
|
// character is valid except `" \ IsolatedCR`. We don't test if the `\r` is isolated or not,
|
||||||
|
// but always escape it.
|
||||||
|
for x in memchr::memchr3_iter(b'\\', b'"', b'\r', src) {
|
||||||
|
dest.extend(&src[last..x]);
|
||||||
|
dest.extend(match src[x] {
|
||||||
|
b'\\' => br"\\",
|
||||||
|
b'\"' => br#"\""#,
|
||||||
|
_ => br"\r",
|
||||||
|
});
|
||||||
|
last = x + 1;
|
||||||
|
}
|
||||||
|
dest.extend(&src[last..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build_template_enum(
|
||||||
|
buf: &mut Buffer,
|
||||||
|
enum_ast: &DeriveInput,
|
||||||
|
mut enum_args: Option<PartialTemplateArgs>,
|
||||||
|
vars_args: Vec<Option<PartialTemplateArgs>>,
|
||||||
|
has_default_impl: bool,
|
||||||
|
) -> Result<usize, CompileError> {
|
||||||
|
let Data::Enum(enum_data) = &enum_ast.data else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
|
impl_everything(enum_ast, buf);
|
||||||
|
|
||||||
|
let enum_id = &enum_ast.ident;
|
||||||
|
let enum_span = enum_id.span();
|
||||||
|
let lifetime = Lifetime::new(&format!("'__Askama_{enum_id}"), enum_span);
|
||||||
|
|
||||||
|
let mut generics = enum_ast.generics.clone();
|
||||||
|
if generics.lt_token.is_none() {
|
||||||
|
generics.lt_token = Some(Token);
|
||||||
|
}
|
||||||
|
if generics.gt_token.is_none() {
|
||||||
|
generics.gt_token = Some(Token);
|
||||||
|
}
|
||||||
|
generics
|
||||||
|
.params
|
||||||
|
.insert(0, GenericParam::Lifetime(LifetimeParam::new(lifetime)));
|
||||||
|
|
||||||
|
let mut biggest_size_hint = 0;
|
||||||
|
let mut render_into_arms = TokenStream::new();
|
||||||
|
let mut size_hint_arms = TokenStream::new();
|
||||||
|
for (var, var_args) in enum_data.variants.iter().zip(vars_args) {
|
||||||
|
let Some(mut var_args) = var_args else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let var_ast = type_for_enum_variant(enum_ast, &generics, var);
|
||||||
|
buf.write(quote!(#var_ast));
|
||||||
|
|
||||||
|
// not inherited: template, meta_docs, block, print
|
||||||
|
if let Some(enum_args) = &mut enum_args {
|
||||||
|
set_default(&mut var_args, enum_args, |v| &mut v.source);
|
||||||
|
set_default(&mut var_args, enum_args, |v| &mut v.escape);
|
||||||
|
set_default(&mut var_args, enum_args, |v| &mut v.ext);
|
||||||
|
set_default(&mut var_args, enum_args, |v| &mut v.syntax);
|
||||||
|
set_default(&mut var_args, enum_args, |v| &mut v.config);
|
||||||
|
set_default(&mut var_args, enum_args, |v| &mut v.whitespace);
|
||||||
|
}
|
||||||
|
let size_hint = biggest_size_hint.max(build_template_item(
|
||||||
|
buf,
|
||||||
|
&var_ast,
|
||||||
|
Some(enum_ast),
|
||||||
|
&TemplateArgs::from_partial(&var_ast, Some(var_args))?,
|
||||||
|
TmplKind::Variant,
|
||||||
|
)?);
|
||||||
|
biggest_size_hint = biggest_size_hint.max(size_hint);
|
||||||
|
|
||||||
|
variant_as_arm(
|
||||||
|
&var_ast,
|
||||||
|
var,
|
||||||
|
size_hint,
|
||||||
|
&mut render_into_arms,
|
||||||
|
&mut size_hint_arms,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if has_default_impl {
|
||||||
|
let size_hint = build_template_item(
|
||||||
|
buf,
|
||||||
|
enum_ast,
|
||||||
|
None,
|
||||||
|
&TemplateArgs::from_partial(enum_ast, enum_args)?,
|
||||||
|
TmplKind::Variant,
|
||||||
|
)?;
|
||||||
|
biggest_size_hint = biggest_size_hint.max(size_hint);
|
||||||
|
|
||||||
|
render_into_arms.extend(quote! {
|
||||||
|
ref __askama_arg => {
|
||||||
|
<_ as askama::helpers::EnumVariantTemplate>::render_into_with_values(
|
||||||
|
__askama_arg,
|
||||||
|
__askama_writer,
|
||||||
|
__askama_values,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
size_hint_arms.extend(quote! {
|
||||||
|
_ => {
|
||||||
|
#size_hint
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
write_header(enum_ast, buf, "askama::Template");
|
||||||
|
buf.write(format_args!(
|
||||||
|
"\
|
||||||
|
fn render_into_with_values<AskamaW>(\
|
||||||
|
&self,\
|
||||||
|
__askama_writer: &mut AskamaW,\
|
||||||
|
__askama_values: &dyn askama::Values,\
|
||||||
|
) -> askama::Result<()>\
|
||||||
|
where \
|
||||||
|
AskamaW: askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized\
|
||||||
|
{{\
|
||||||
|
match *self {{\
|
||||||
|
{render_into_arms}\
|
||||||
|
}}\
|
||||||
|
}}",
|
||||||
|
));
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
buf.write(format_args!(
|
||||||
|
"\
|
||||||
|
fn render_with_values(\
|
||||||
|
&self,\
|
||||||
|
__askama_values: &dyn askama::Values,\
|
||||||
|
) -> askama::Result<askama::helpers::alloc::string::String> {{\
|
||||||
|
let size_hint = match self {{\
|
||||||
|
{size_hint_arms}\
|
||||||
|
}};\
|
||||||
|
let mut buf = askama::helpers::alloc::string::String::new();\
|
||||||
|
let _ = buf.try_reserve(size_hint);\
|
||||||
|
self.render_into_with_values(&mut buf, __askama_values)?;\
|
||||||
|
askama::Result::Ok(buf)\
|
||||||
|
}}",
|
||||||
|
));
|
||||||
|
|
||||||
|
buf.write(format_args!(
|
||||||
|
"\
|
||||||
|
const SIZE_HINT: askama::helpers::core::primitive::usize = {biggest_size_hint}usize;\
|
||||||
|
}}",
|
||||||
|
));
|
||||||
|
Ok(biggest_size_hint)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_default<S, T, A>(dest: &mut S, parent: &mut S, mut access: A)
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
A: FnMut(&mut S) -> &mut Option<T>,
|
||||||
|
{
|
||||||
|
let dest = access(dest);
|
||||||
|
if dest.is_none() {
|
||||||
|
if let Some(parent) = access(parent) {
|
||||||
|
*dest = Some(parent.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a `struct` to contain the data of an enum variant
|
||||||
|
fn type_for_enum_variant(
|
||||||
|
enum_ast: &DeriveInput,
|
||||||
|
enum_generics: &Generics,
|
||||||
|
var: &Variant,
|
||||||
|
) -> DeriveInput {
|
||||||
|
let enum_id = &enum_ast.ident;
|
||||||
|
let (_, ty_generics, _) = enum_ast.generics.split_for_impl();
|
||||||
|
let lt = enum_generics.params.first().unwrap();
|
||||||
|
|
||||||
|
let id = &var.ident;
|
||||||
|
let span = id.span();
|
||||||
|
let id = Ident::new(&format!("__Askama__{enum_id}__{id}"), span);
|
||||||
|
|
||||||
|
let phantom: Type = parse_quote! {
|
||||||
|
askama::helpers::core::marker::PhantomData < &#lt #enum_id #ty_generics >
|
||||||
|
};
|
||||||
|
let fields = match &var.fields {
|
||||||
|
Fields::Named(fields) => {
|
||||||
|
let mut fields = fields.clone();
|
||||||
|
for f in fields.named.iter_mut() {
|
||||||
|
let ty = &f.ty;
|
||||||
|
f.ty = parse_quote!(&#lt #ty);
|
||||||
|
}
|
||||||
|
let id = Ident::new(&format!("__Askama__{enum_id}__phantom"), span);
|
||||||
|
fields.named.push(parse_quote!(#id: #phantom));
|
||||||
|
Fields::Named(fields)
|
||||||
|
}
|
||||||
|
Fields::Unnamed(fields) => {
|
||||||
|
let mut fields = fields.clone();
|
||||||
|
for f in fields.unnamed.iter_mut() {
|
||||||
|
let ty = &f.ty;
|
||||||
|
f.ty = parse_quote!(&#lt #ty);
|
||||||
|
}
|
||||||
|
fields.unnamed.push(parse_quote!(#phantom));
|
||||||
|
Fields::Unnamed(fields)
|
||||||
|
}
|
||||||
|
Fields::Unit => Fields::Unnamed(parse_quote!((#phantom))),
|
||||||
|
};
|
||||||
|
let semicolon = match &var.fields {
|
||||||
|
Fields::Named(_) => None,
|
||||||
|
_ => Some(Token),
|
||||||
|
};
|
||||||
|
|
||||||
|
parse_quote! {
|
||||||
|
#[askama::helpers::core::prelude::rust_2021::derive(
|
||||||
|
askama::helpers::core::prelude::rust_2021::Clone,
|
||||||
|
askama::helpers::core::prelude::rust_2021::Copy,
|
||||||
|
askama::helpers::core::prelude::rust_2021::Debug
|
||||||
|
)]
|
||||||
|
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||||
|
struct #id #enum_generics #fields #semicolon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a `match` arm for an `enum` variant, that calls `<_ as EnumVariantTemplate>::render_into()`
|
||||||
|
/// for that type and data
|
||||||
|
fn variant_as_arm(
|
||||||
|
var_ast: &DeriveInput,
|
||||||
|
var: &Variant,
|
||||||
|
size_hint: usize,
|
||||||
|
render_into_arms: &mut TokenStream,
|
||||||
|
size_hint_arms: &mut TokenStream,
|
||||||
|
) {
|
||||||
|
let var_id = &var_ast.ident;
|
||||||
|
let ident = &var.ident;
|
||||||
|
let span = ident.span();
|
||||||
|
|
||||||
|
let generics = var_ast.generics.clone();
|
||||||
|
let (_, ty_generics, _) = generics.split_for_impl();
|
||||||
|
let ty_generics: TokenStream = ty_generics
|
||||||
|
.as_turbofish()
|
||||||
|
.to_token_stream()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, token)| match idx {
|
||||||
|
// 0 1 2 3 4 => : : < ' __Askama_Foo
|
||||||
|
4 => TokenTree::Ident(Ident::new("_", span)),
|
||||||
|
_ => token,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let Data::Struct(ast_data) = &var_ast.data else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
let mut src = TokenStream::new();
|
||||||
|
let mut this = TokenStream::new();
|
||||||
|
match &var.fields {
|
||||||
|
Fields::Named(fields) => {
|
||||||
|
for (idx, field) in fields.named.iter().enumerate() {
|
||||||
|
let arg = Ident::new(&format!("__askama_arg_{idx}"), field.span());
|
||||||
|
let id = field.ident.as_ref().unwrap();
|
||||||
|
src.extend(quote!(#id: ref #arg,));
|
||||||
|
this.extend(quote!(#id: #arg,));
|
||||||
|
}
|
||||||
|
|
||||||
|
let phantom = match &ast_data.fields {
|
||||||
|
Fields::Named(fields) => fields
|
||||||
|
.named
|
||||||
|
.iter()
|
||||||
|
.next_back()
|
||||||
|
.unwrap()
|
||||||
|
.ident
|
||||||
|
.as_ref()
|
||||||
|
.unwrap(),
|
||||||
|
Fields::Unnamed(_) | Fields::Unit => unreachable!(),
|
||||||
|
};
|
||||||
|
this.extend(quote!(#phantom: askama::helpers::core::marker::PhantomData {},));
|
||||||
|
}
|
||||||
|
|
||||||
|
Fields::Unnamed(fields) => {
|
||||||
|
for (idx, field) in fields.unnamed.iter().enumerate() {
|
||||||
|
let span = field.ident.span();
|
||||||
|
let arg = Ident::new(&format!("__askama_arg_{idx}"), span);
|
||||||
|
let idx = syn::LitInt::new(&format!("{idx}"), span);
|
||||||
|
src.extend(quote!(#idx: ref #arg,));
|
||||||
|
this.extend(quote!(#idx: #arg,));
|
||||||
|
}
|
||||||
|
let idx = syn::LitInt::new(&format!("{}", fields.unnamed.len()), span);
|
||||||
|
this.extend(quote!(#idx: askama::helpers::core::marker::PhantomData {},));
|
||||||
|
}
|
||||||
|
|
||||||
|
Fields::Unit => {
|
||||||
|
this.extend(quote!(0: askama::helpers::core::marker::PhantomData {},));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
render_into_arms.extend(quote! {
|
||||||
|
Self :: #ident { #src } => {
|
||||||
|
<_ as askama::helpers::EnumVariantTemplate>::render_into_with_values(
|
||||||
|
& #var_id #ty_generics { #this },
|
||||||
|
__askama_writer,
|
||||||
|
__askama_values,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
size_hint_arms.extend(quote! {
|
||||||
|
Self :: #ident { .. } => {
|
||||||
|
#size_hint
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ mod generator;
|
|||||||
mod heritage;
|
mod heritage;
|
||||||
mod html;
|
mod html;
|
||||||
mod input;
|
mod input;
|
||||||
|
mod integration;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
@@ -17,22 +18,24 @@ use std::hash::{BuildHasher, Hash};
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use config::{Config, read_config_file};
|
use parser::{Parsed, ascii_str, strip_common};
|
||||||
use generator::{Generator, MapChain};
|
|
||||||
use heritage::{Context, Heritage};
|
|
||||||
use input::{Print, TemplateArgs, TemplateInput};
|
|
||||||
use parser::{Parsed, WithSpan, strip_common};
|
|
||||||
#[cfg(not(feature = "__standalone"))]
|
#[cfg(not(feature = "__standalone"))]
|
||||||
use proc_macro::TokenStream as TokenStream12;
|
use proc_macro::TokenStream as TokenStream12;
|
||||||
#[cfg(feature = "__standalone")]
|
#[cfg(feature = "__standalone")]
|
||||||
use proc_macro2::TokenStream as TokenStream12;
|
use proc_macro2::TokenStream as TokenStream12;
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
|
||||||
use quote::quote_spanned;
|
use quote::{quote, quote_spanned};
|
||||||
use rustc_hash::FxBuildHasher;
|
use rustc_hash::FxBuildHasher;
|
||||||
|
|
||||||
|
use crate::config::{Config, read_config_file};
|
||||||
|
use crate::generator::{TmplKind, template_to_string};
|
||||||
|
use crate::heritage::{Context, Heritage};
|
||||||
|
use crate::input::{AnyTemplateArgs, Print, TemplateArgs, TemplateInput};
|
||||||
|
use crate::integration::{Buffer, build_template_enum};
|
||||||
|
|
||||||
/// The `Template` derive macro and its `template()` attribute.
|
/// The `Template` derive macro and its `template()` attribute.
|
||||||
///
|
///
|
||||||
/// Rinja works by generating one or more trait implementations for any
|
/// Askama works by generating one or more trait implementations for any
|
||||||
/// `struct` type decorated with the `#[derive(Template)]` attribute. The
|
/// `struct` type decorated with the `#[derive(Template)]` attribute. The
|
||||||
/// code generation process takes some options that can be specified through
|
/// code generation process takes some options that can be specified through
|
||||||
/// the `template()` attribute.
|
/// the `template()` attribute.
|
||||||
@@ -77,10 +80,10 @@ use rustc_hash::FxBuildHasher;
|
|||||||
/// of the template `struct`.
|
/// of the template `struct`.
|
||||||
///
|
///
|
||||||
/// Instead of `path = "…"` or `source = "…"`, specify `in_doc = true` in the `#[template]`
|
/// Instead of `path = "…"` or `source = "…"`, specify `in_doc = true` in the `#[template]`
|
||||||
/// attribute, and in the struct's documentation add a `rinja` code block:
|
/// attribute, and in the struct's documentation add a `askama` code block:
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// /// ```rinja
|
/// /// ```askama
|
||||||
/// /// <div>{{ lines|linebreaksbr }}</div>
|
/// /// <div>{{ lines|linebreaksbr }}</div>
|
||||||
/// /// ```
|
/// /// ```
|
||||||
/// #[derive(Template)]
|
/// #[derive(Template)]
|
||||||
@@ -98,6 +101,56 @@ use rustc_hash::FxBuildHasher;
|
|||||||
/// the generated code (`code`) or `all` for both.
|
/// the generated code (`code`) or `all` for both.
|
||||||
/// The requested data will be printed to stdout at compile time.
|
/// The requested data will be printed to stdout at compile time.
|
||||||
///
|
///
|
||||||
|
/// ### block
|
||||||
|
///
|
||||||
|
/// E.g. `block = "block_name"`
|
||||||
|
///
|
||||||
|
/// Renders the block by itself.
|
||||||
|
/// Expressions outside of the block are not required by the struct, and
|
||||||
|
/// inheritance is also supported. This can be useful when you need to
|
||||||
|
/// decompose your template for partial rendering, without needing to
|
||||||
|
/// extract the partial into a separate template or macro.
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(path = "hello.html", block = "hello")]
|
||||||
|
/// struct HelloTemplate<'a> { ... }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### blocks
|
||||||
|
///
|
||||||
|
/// E.g. `blocks = ["title", "content"]`
|
||||||
|
///
|
||||||
|
/// Automatically generates (a number of) sub-templates that act as if they had a
|
||||||
|
/// `block = "..."` attribute. You can access the sub-templates with the method
|
||||||
|
/// <code>my_template.as_<em>block_name</em>()</code>, where *`block_name`* is the
|
||||||
|
/// name of the block:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(
|
||||||
|
/// ext = "txt",
|
||||||
|
/// source = "
|
||||||
|
/// {% block title %} ... {% endblock %}
|
||||||
|
/// {% block content %} ... {% endblock %}
|
||||||
|
/// ",
|
||||||
|
/// blocks = ["title", "content"]
|
||||||
|
/// )]
|
||||||
|
/// struct News<'a> {
|
||||||
|
/// title: &'a str,
|
||||||
|
/// message: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let news = News {
|
||||||
|
/// title: "Announcing Rust 1.84.1",
|
||||||
|
/// message: "The Rust team has published a new point release of Rust, 1.84.1.",
|
||||||
|
/// };
|
||||||
|
/// assert_eq!(
|
||||||
|
/// news.as_title().render().unwrap(),
|
||||||
|
/// "<h1>Announcing Rust 1.84.1</h1>"
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
/// ### escape
|
/// ### escape
|
||||||
///
|
///
|
||||||
/// E.g. `escape = "none"`
|
/// E.g. `escape = "none"`
|
||||||
@@ -110,7 +163,38 @@ use rustc_hash::FxBuildHasher;
|
|||||||
/// E.g. `syntax = "foo"`
|
/// E.g. `syntax = "foo"`
|
||||||
///
|
///
|
||||||
/// Set the syntax name for a parser defined in the configuration file.
|
/// Set the syntax name for a parser defined in the configuration file.
|
||||||
/// The default syntax, `"default"`, is the one provided by Rinja.
|
/// The default syntax, `"default"`, is the one provided by Askama.
|
||||||
|
///
|
||||||
|
/// ### askama
|
||||||
|
///
|
||||||
|
/// E.g. `askama = askama`
|
||||||
|
///
|
||||||
|
/// If you are using askama in a subproject, a library or a [macro][book-macro], it might be
|
||||||
|
/// necessary to specify the [path][book-tree] where to find the module `askama`:
|
||||||
|
///
|
||||||
|
/// [book-macro]: https://doc.rust-lang.org/book/ch19-06-macros.html
|
||||||
|
/// [book-tree]: https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// #[doc(hidden)]
|
||||||
|
/// use askama as __askama;
|
||||||
|
///
|
||||||
|
/// #[macro_export]
|
||||||
|
/// macro_rules! new_greeter {
|
||||||
|
/// ($name:ident) => {
|
||||||
|
/// #[derive(Debug, $crate::askama::Template)]
|
||||||
|
/// #[template(
|
||||||
|
/// ext = "txt",
|
||||||
|
/// source = "Hello, world!",
|
||||||
|
/// askama = $crate::__askama
|
||||||
|
/// )]
|
||||||
|
/// struct $name;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// new_greeter!(HelloWorld);
|
||||||
|
/// assert_eq!(HelloWorld.to_string(), Ok("Hello, world."));
|
||||||
|
/// ```
|
||||||
#[allow(clippy::useless_conversion)] // To be compatible with both `TokenStream`s
|
#[allow(clippy::useless_conversion)] // To be compatible with both `TokenStream`s
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
not(feature = "__standalone"),
|
not(feature = "__standalone"),
|
||||||
@@ -122,53 +206,66 @@ pub fn derive_template(input: TokenStream12) -> TokenStream12 {
|
|||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let msgs = err.into_iter().map(|err| err.to_string());
|
let msgs = err.into_iter().map(|err| err.to_string());
|
||||||
return compile_error(msgs, Span::call_site()).into();
|
let ts = quote! {
|
||||||
|
const _: () = {
|
||||||
|
extern crate core;
|
||||||
|
#(core::compile_error!(#msgs);)*
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return ts.into();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match build_template(&ast) {
|
|
||||||
Ok(source) => source.parse().unwrap(),
|
let mut buf = Buffer::new();
|
||||||
Err(CompileError {
|
let mut args = AnyTemplateArgs::new(&ast);
|
||||||
msg,
|
let crate_name = args
|
||||||
span,
|
.as_mut()
|
||||||
rendered: _rendered,
|
.map(|a| a.take_crate_name())
|
||||||
}) => {
|
.unwrap_or_default();
|
||||||
let mut ts = compile_error(std::iter::once(msg), span.unwrap_or(ast.ident.span()));
|
|
||||||
if let Ok(source) = build_skeleton(&ast) {
|
let result = args.and_then(|args| build_template(&mut buf, &ast, args));
|
||||||
let source: TokenStream = source.parse().unwrap();
|
let ts = if let Err(CompileError { msg, span }) = result {
|
||||||
|
let mut ts = quote_spanned! {
|
||||||
|
span.unwrap_or(ast.ident.span()) =>
|
||||||
|
askama::helpers::core::compile_error!(#msg);
|
||||||
|
};
|
||||||
|
buf.clear();
|
||||||
|
if build_skeleton(&mut buf, &ast).is_ok() {
|
||||||
|
let source: TokenStream = buf.into_string().parse().unwrap();
|
||||||
ts.extend(source);
|
ts.extend(source);
|
||||||
}
|
}
|
||||||
ts.into()
|
ts
|
||||||
}
|
} else {
|
||||||
}
|
buf.into_string().parse().unwrap()
|
||||||
}
|
};
|
||||||
|
|
||||||
fn compile_error(msgs: impl Iterator<Item = String>, span: Span) -> TokenStream {
|
let ts = TokenTree::Group(Group::new(Delimiter::None, ts));
|
||||||
let crate_ = syn::Ident::new(CRATE, Span::call_site());
|
let ts = if let Some(crate_name) = crate_name {
|
||||||
quote_spanned! {
|
quote! {
|
||||||
span =>
|
|
||||||
const _: () = {
|
const _: () = {
|
||||||
extern crate #crate_ as rinja;
|
use #crate_name as askama;
|
||||||
#(rinja::helpers::core::compile_error!(#msgs);)*
|
#ts
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
const _: () = {
|
||||||
|
extern crate askama;
|
||||||
|
#ts
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ts.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_skeleton(ast: &syn::DeriveInput) -> Result<String, CompileError> {
|
fn build_skeleton(buf: &mut Buffer, ast: &syn::DeriveInput) -> Result<usize, CompileError> {
|
||||||
let template_args = TemplateArgs::fallback();
|
let template_args = TemplateArgs::fallback();
|
||||||
let config = Config::new("", None, None, None)?;
|
let config = Config::new("", None, None, None, None)?;
|
||||||
let input = TemplateInput::new(ast, config, &template_args)?;
|
let input = TemplateInput::new(ast, None, config, &template_args)?;
|
||||||
let mut contexts = HashMap::default();
|
let mut contexts = HashMap::default();
|
||||||
let parsed = parser::Parsed::default();
|
let parsed = parser::Parsed::default();
|
||||||
contexts.insert(&input.path, Context::empty(&parsed));
|
contexts.insert(&input.path, Context::empty(&parsed));
|
||||||
Generator::new(
|
template_to_string(buf, &input, &contexts, None, TmplKind::Struct)
|
||||||
&input,
|
|
||||||
&contexts,
|
|
||||||
None,
|
|
||||||
MapChain::default(),
|
|
||||||
input.block.is_some(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.build(&contexts[&input.path])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a `syn::DeriveInput` and generates source code for it
|
/// Takes a `syn::DeriveInput` and generates source code for it
|
||||||
@@ -178,34 +275,55 @@ fn build_skeleton(ast: &syn::DeriveInput) -> Result<String, CompileError> {
|
|||||||
/// parsed, and the parse tree is fed to the code generator. Will print
|
/// parsed, and the parse tree is fed to the code generator. Will print
|
||||||
/// the parse tree and/or generated source according to the `print` key's
|
/// the parse tree and/or generated source according to the `print` key's
|
||||||
/// value as passed to the `template()` attribute.
|
/// value as passed to the `template()` attribute.
|
||||||
pub(crate) fn build_template(ast: &syn::DeriveInput) -> Result<String, CompileError> {
|
pub(crate) fn build_template(
|
||||||
let template_args = TemplateArgs::new(ast)?;
|
buf: &mut Buffer,
|
||||||
let mut result = build_template_inner(ast, &template_args);
|
ast: &syn::DeriveInput,
|
||||||
|
args: AnyTemplateArgs,
|
||||||
|
) -> Result<usize, CompileError> {
|
||||||
|
let err_span;
|
||||||
|
let mut result = match args {
|
||||||
|
AnyTemplateArgs::Struct(item) => {
|
||||||
|
err_span = item.source.1.or(item.template_span);
|
||||||
|
build_template_item(buf, ast, None, &item, TmplKind::Struct)
|
||||||
|
}
|
||||||
|
AnyTemplateArgs::Enum {
|
||||||
|
enum_args,
|
||||||
|
vars_args,
|
||||||
|
has_default_impl,
|
||||||
|
} => {
|
||||||
|
err_span = enum_args
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|v| v.source.as_ref())
|
||||||
|
.map(|s| s.span())
|
||||||
|
.or_else(|| enum_args.as_ref().map(|v| v.template.span()));
|
||||||
|
build_template_enum(buf, ast, enum_args, vars_args, has_default_impl)
|
||||||
|
}
|
||||||
|
};
|
||||||
if let Err(err) = &mut result {
|
if let Err(err) = &mut result {
|
||||||
if err.span.is_none() {
|
if err.span.is_none() {
|
||||||
err.span = template_args
|
err.span = err_span;
|
||||||
.source
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|(_, span)| *span)
|
|
||||||
.or(template_args.template_span);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_template_inner(
|
fn build_template_item(
|
||||||
|
buf: &mut Buffer,
|
||||||
ast: &syn::DeriveInput,
|
ast: &syn::DeriveInput,
|
||||||
|
enum_ast: Option<&syn::DeriveInput>,
|
||||||
template_args: &TemplateArgs,
|
template_args: &TemplateArgs,
|
||||||
) -> Result<String, CompileError> {
|
tmpl_kind: TmplKind<'_>,
|
||||||
|
) -> Result<usize, CompileError> {
|
||||||
let config_path = template_args.config_path();
|
let config_path = template_args.config_path();
|
||||||
let s = read_config_file(config_path, template_args.config_span)?;
|
let (s, full_config_path) = read_config_file(config_path, template_args.config_span)?;
|
||||||
let config = Config::new(
|
let config = Config::new(
|
||||||
&s,
|
&s,
|
||||||
config_path,
|
config_path,
|
||||||
template_args.whitespace.as_deref(),
|
template_args.whitespace,
|
||||||
template_args.config_span,
|
template_args.config_span,
|
||||||
|
full_config_path,
|
||||||
)?;
|
)?;
|
||||||
let input = TemplateInput::new(ast, config, template_args)?;
|
let input = TemplateInput::new(ast, enum_ast, config, template_args)?;
|
||||||
|
|
||||||
let mut templates = HashMap::default();
|
let mut templates = HashMap::default();
|
||||||
input.find_used_templates(&mut templates)?;
|
input.find_used_templates(&mut templates)?;
|
||||||
@@ -217,46 +335,40 @@ fn build_template_inner(
|
|||||||
|
|
||||||
let ctx = &contexts[&input.path];
|
let ctx = &contexts[&input.path];
|
||||||
let heritage = if !ctx.blocks.is_empty() || ctx.extends.is_some() {
|
let heritage = if !ctx.blocks.is_empty() || ctx.extends.is_some() {
|
||||||
let heritage = Heritage::new(ctx, &contexts);
|
Some(Heritage::new(ctx, &contexts))
|
||||||
|
|
||||||
if let Some(block_name) = input.block {
|
|
||||||
if !heritage.blocks.contains_key(&block_name) {
|
|
||||||
return Err(CompileError::no_file_info(
|
|
||||||
format!("cannot find block {block_name}"),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(heritage)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some((block_name, block_span)) = input.block {
|
||||||
|
let has_block = match &heritage {
|
||||||
|
Some(heritage) => heritage.blocks.contains_key(block_name),
|
||||||
|
None => ctx.blocks.contains_key(block_name),
|
||||||
|
};
|
||||||
|
if !has_block {
|
||||||
|
return Err(CompileError::no_file_info(
|
||||||
|
format_args!("cannot find block `{block_name}`"),
|
||||||
|
Some(block_span),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if input.print == Print::Ast || input.print == Print::All {
|
if input.print == Print::Ast || input.print == Print::All {
|
||||||
eprintln!("{:?}", templates[&input.path].nodes());
|
eprintln!("{:?}", templates[&input.path].nodes());
|
||||||
}
|
}
|
||||||
|
|
||||||
let code = Generator::new(
|
let mark = buf.get_mark();
|
||||||
&input,
|
let size_hint = template_to_string(buf, &input, &contexts, heritage.as_ref(), tmpl_kind)?;
|
||||||
&contexts,
|
|
||||||
heritage.as_ref(),
|
|
||||||
MapChain::default(),
|
|
||||||
input.block.is_some(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.build(&contexts[&input.path])?;
|
|
||||||
if input.print == Print::Code || input.print == Print::All {
|
if input.print == Print::Code || input.print == Print::All {
|
||||||
eprintln!("{code}");
|
eprintln!("{}", buf.marked_text(mark));
|
||||||
}
|
}
|
||||||
Ok(code)
|
Ok(size_hint)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct CompileError {
|
struct CompileError {
|
||||||
msg: String,
|
msg: String,
|
||||||
span: Option<Span>,
|
span: Option<Span>,
|
||||||
rendered: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompileError {
|
impl CompileError {
|
||||||
@@ -273,18 +385,13 @@ impl CompileError {
|
|||||||
Some(file_info) => format!("{msg}{file_info}"),
|
Some(file_info) => format!("{msg}{file_info}"),
|
||||||
None => msg.to_string(),
|
None => msg.to_string(),
|
||||||
};
|
};
|
||||||
Self {
|
Self { msg, span }
|
||||||
msg,
|
|
||||||
span,
|
|
||||||
rendered: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn no_file_info<S: fmt::Display>(msg: S, span: Option<Span>) -> Self {
|
fn no_file_info<S: ToString>(msg: S, span: Option<Span>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
msg: msg.to_string(),
|
msg: msg.to_string(),
|
||||||
span,
|
span,
|
||||||
rendered: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -314,11 +421,12 @@ impl<'a> FileInfo<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn of<T>(node: &WithSpan<'a, T>, path: &'a Path, parsed: &'a Parsed) -> Self {
|
fn of(node: parser::Span<'a>, path: &'a Path, parsed: &'a Parsed) -> Self {
|
||||||
|
let source = parsed.source();
|
||||||
Self {
|
Self {
|
||||||
path,
|
path,
|
||||||
source: Some(parsed.source()),
|
source: Some(source),
|
||||||
node_source: Some(node.span()),
|
node_source: node.as_suffix_of(source),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,14 +440,17 @@ impl fmt::Display for FileInfo<'_> {
|
|||||||
"\n --> {file_path}:{row}:{column}\n{source_after}",
|
"\n --> {file_path}:{row}:{column}\n{source_after}",
|
||||||
row = error_info.row,
|
row = error_info.row,
|
||||||
column = error_info.column,
|
column = error_info.column,
|
||||||
source_after = &error_info.source_after,
|
source_after = error_info.source_after,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let file_path = match std::env::current_dir() {
|
write!(
|
||||||
Ok(cwd) => strip_common(&cwd, self.path),
|
f,
|
||||||
Err(_) => self.path.display().to_string(),
|
"\n --> {}",
|
||||||
};
|
match std::env::current_dir() {
|
||||||
write!(f, "\n --> {file_path}")
|
Ok(cwd) => fmt_left!(move "{}", strip_common(&cwd, self.path)),
|
||||||
|
Err(_) => fmt_right!("{}", self.path.display()),
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,11 +462,18 @@ struct ErrorInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_row_and_column(src: &str, input: &str) -> ErrorInfo {
|
fn generate_row_and_column(src: &str, input: &str) -> ErrorInfo {
|
||||||
|
const MAX_LINE_LEN: usize = 80;
|
||||||
|
|
||||||
let offset = src.len() - input.len();
|
let offset = src.len() - input.len();
|
||||||
let (source_before, source_after) = src.split_at(offset);
|
let (source_before, source_after) = src.split_at(offset);
|
||||||
|
|
||||||
let source_after = match source_after.char_indices().enumerate().take(41).last() {
|
let source_after = match source_after
|
||||||
Some((80, (i, _))) => format!("{:?}...", &source_after[..i]),
|
.char_indices()
|
||||||
|
.enumerate()
|
||||||
|
.take(MAX_LINE_LEN + 1)
|
||||||
|
.last()
|
||||||
|
{
|
||||||
|
Some((MAX_LINE_LEN, (i, _))) => format!("{:?}...", &source_after[..i]),
|
||||||
_ => format!("{source_after:?}"),
|
_ => format!("{source_after:?}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -374,7 +492,7 @@ fn generate_error_info(src: &str, input: &str, file_path: &Path) -> (ErrorInfo,
|
|||||||
Ok(cwd) => strip_common(&cwd, file_path),
|
Ok(cwd) => strip_common(&cwd, file_path),
|
||||||
Err(_) => file_path.display().to_string(),
|
Err(_) => file_path.display().to_string(),
|
||||||
};
|
};
|
||||||
let error_info: ErrorInfo = generate_row_and_column(src, input);
|
let error_info = generate_row_and_column(src, input);
|
||||||
(error_info, file_path)
|
(error_info, file_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,9 +547,60 @@ impl<K: Hash + Eq, V> OnceMap<K, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum EitherFormat<L, R>
|
||||||
|
where
|
||||||
|
L: for<'a, 'b> Fn(&'a mut fmt::Formatter<'b>) -> fmt::Result,
|
||||||
|
R: for<'a, 'b> Fn(&'a mut fmt::Formatter<'b>) -> fmt::Result,
|
||||||
|
{
|
||||||
|
Left(L),
|
||||||
|
Right(R),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> fmt::Display for EitherFormat<L, R>
|
||||||
|
where
|
||||||
|
L: for<'a, 'b> Fn(&'a mut fmt::Formatter<'b>) -> fmt::Result,
|
||||||
|
R: for<'a, 'b> Fn(&'a mut fmt::Formatter<'b>) -> fmt::Result,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Left(v) => v(f),
|
||||||
|
Self::Right(v) => v(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! fmt_left {
|
||||||
|
(move $fmt:literal $($tt:tt)*) => {
|
||||||
|
$crate::EitherFormat::Left(move |f: &mut std::fmt::Formatter<'_>| {
|
||||||
|
write!(f, $fmt $($tt)*)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($fmt:literal $($tt:tt)*) => {
|
||||||
|
$crate::EitherFormat::Left(|f: &mut std::fmt::Formatter<'_>| {
|
||||||
|
write!(f, $fmt $($tt)*)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! fmt_right {
|
||||||
|
(move $fmt:literal $($tt:tt)*) => {
|
||||||
|
$crate::EitherFormat::Right(move |f: &mut std::fmt::Formatter<'_>| {
|
||||||
|
write!(f, $fmt $($tt)*)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($fmt:literal $($tt:tt)*) => {
|
||||||
|
$crate::EitherFormat::Right(|f: &mut std::fmt::Formatter<'_>| {
|
||||||
|
write!(f, $fmt $($tt)*)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use {fmt_left, fmt_right};
|
||||||
|
|
||||||
// This is used by the code generator to decide whether a named filter is part of
|
// This is used by the code generator to decide whether a named filter is part of
|
||||||
// Rinja or should refer to a local `filters` module.
|
// Askama or should refer to a local `filters` module.
|
||||||
const BUILT_IN_FILTERS: &[&str] = &[
|
const BUILTIN_FILTERS: &[&str] = &[
|
||||||
"capitalize",
|
"capitalize",
|
||||||
"center",
|
"center",
|
||||||
"indent",
|
"indent",
|
||||||
@@ -441,18 +610,9 @@ const BUILT_IN_FILTERS: &[&str] = &[
|
|||||||
"trim",
|
"trim",
|
||||||
"truncate",
|
"truncate",
|
||||||
"upper",
|
"upper",
|
||||||
"urlencode",
|
"uppercase",
|
||||||
"wordcount",
|
"wordcount",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CRATE: &str = if cfg!(feature = "with-actix-web") {
|
// Built-in filters that need the `alloc` feature.
|
||||||
"rinja_actix"
|
const BUILTIN_FILTERS_NEED_ALLOC: &[&str] = &["center", "truncate"];
|
||||||
} else if cfg!(feature = "with-axum") {
|
|
||||||
"rinja_axum"
|
|
||||||
} else if cfg!(feature = "with-rocket") {
|
|
||||||
"rinja_rocket"
|
|
||||||
} else if cfg!(feature = "with-warp") {
|
|
||||||
"rinja_warp"
|
|
||||||
} else {
|
|
||||||
"rinja"
|
|
||||||
};
|
|
||||||
1167
third_party/rust/askama_derive/src/tests.rs
vendored
Normal file
1167
third_party/rust/askama_derive/src/tests.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
23
third_party/rust/askama_derive/tomlfmt.toml
vendored
Normal file
23
third_party/rust/askama_derive/tomlfmt.toml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Keep in the same order as <https://doc.rust-lang.org/cargo/reference/manifest.html>
|
||||||
|
table_order = [
|
||||||
|
"package",
|
||||||
|
# targets
|
||||||
|
"lib",
|
||||||
|
"bin",
|
||||||
|
"example",
|
||||||
|
"test",
|
||||||
|
"bench",
|
||||||
|
# dependencies
|
||||||
|
"dependencies",
|
||||||
|
"dev-dependencies",
|
||||||
|
"build-dependencies",
|
||||||
|
"target",
|
||||||
|
# misc
|
||||||
|
"badges",
|
||||||
|
"features",
|
||||||
|
"lints",
|
||||||
|
"patch",
|
||||||
|
"replace",
|
||||||
|
"profile",
|
||||||
|
"workspace",
|
||||||
|
]
|
||||||
1
third_party/rust/askama_parser/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/askama_parser/.cargo-checksum.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"files":{"Cargo.lock":"fe55f129ae3e6619311c163585945ffe3aa1375e33661f0660a88b00aef32a0d","Cargo.toml":"c24d9b8953af7ad1763bfe09ae00274d02f1bd3ef9e0ed199583c2708712777d","LICENSE-APACHE":"87cb0d734c723c083e51c825930ff42bce28596b52dee15567f6b28f19c195e3","LICENSE-MIT":"df20e0180764bf5bd76f74d47bc9e8c0069a666401629c390003a1d5eba99c92","README.md":"6db8144626e0048bfeed20163343b9f01130f297c9ec4b34e1087383cf0c7050","_typos.toml":"72588ef2385568368e7ec251c8451f75a16343d5a06b4c2b312beba2cdf5b308","benches/from_str.rs":"115edebf8d2812adf0f6d981063a977de9f447eccfc09578b321bec308f716e9","benches/librustdoc/LICENSE.md":"8e22b9dae01ceac57b59cf5794dd1a179fc411d14c5351ebf6a97be2678d00c2","benches/librustdoc/item_info.html":"50b88a3f7a56c9d4faf56b1c74dda1e8f02b28ede6581c71c5effa805961a8ea","benches/librustdoc/item_union.html":"aee76fa8260873caa1fb694b8972400e76474fbe7a0791da9298b68828f62364","benches/librustdoc/page.html":"1c656f63a52e1e526b9b0042e7819641db7530b94a71943257cd878f11dbd029","benches/librustdoc/print_item.html":"d3f020edd22e168a84b28b28c0d06084679c58f7c666dc6bf85ba3ba9ede1802","benches/librustdoc/short_item_info.html":"e477cc4083d6e090c00c81947450dba8d3116bdbae8ccd0d59e332c07d6d918b","benches/librustdoc/sidebar.html":"be30b18a25b6011784c11898bca43f06f99a69184812bef3ea92af27ae048849","benches/librustdoc/source.html":"4715ad7116104b800565ef332126c290addf23e989c7161ad184593bcfef50cc","benches/librustdoc/type_layout.html":"9d881f22ce88e40661dffd09d428f9cb2e33d878febc228126674a78b5e6c759","benches/librustdoc/type_layout_size.html":"23778308b293055acc84e29b87384e648675d41b9a18d3b76ad5a1cbeff9d502","clippy.toml":"d141089a5edd91c83ae211257638df2ef1ca53f66b30faa98b22ea1447faeeff","deny.toml":"b7637b42164b1bbba883c422187292cf08ad624691145b5124eaf10edf38c164","src/ascii_str.rs":"272ed728e3e024cefbf16b9a004ad806781cff95f613d34c35a4e8479224d16f","src/expr.rs":"c62b88d09aa311edab8025137b7fa79a33377180ccb0294a9a821e5c7074a94b","src/lib.rs":"b00d21a80702e441a48b11c712dbda09ed88e51e2f9f5fc3f1ae95c0d9bdca04","src/memchr_splitter.rs":"98b595e9aa2e8bbd5e387f708845a457bdc4a1e4430afd59fca5e8842c7f80c2","src/node.rs":"333a4fa93d421fbf08d9e6bf0d8ad8f939ca030f8f3a4105a3e2f4d5bc1c4b19","src/target.rs":"3147366b5be225d10bd3f0dc15294b5e8680268e435cd10690a6eb462ce37783","src/tests.rs":"8181c774f69f540eea59d551ed49d4359bde896d741f55ff39e83df569e00f25","tests/comment-depth.txt":"5d771b3a3b9e7ecea773f76426acb24fdb466df30b8554fb8fc48e9ecc70b32e","tests/excessive_filter_block.txt":"e062f409de016958e577e288f1ecb91d54eb50a319ea0a002eb008ad5e68125b","tests/filter-recursion.txt":"0aaf73f6664a5e1c8f38ac333692e1a2b391d0e23debdc2acf9c6d1b4303ce08","tests/fuzzed-deeply-tested-if-let.txt":"8ca5b1b580e1189feb59a1a12bbea220056b2cc655edd84068327b9bf04879b2","tests/fuzzed_excessive_filter_block.inc":"f50f1cc972c36daace6054ea43cf85b16c2065a7c60be881ceb730d9348793cf","tests/fuzzed_span_is_not_substring_of_source.bin":"27b97ec342a66dbb5a29e0e2ecf1f04ccf2a4e53c003f767143ad8a1729c4641","tests/target-recursion.txt":"501fa6855bbdf566f89b52ded1265c6f05896e749cfaf216d9dea07c76c772d2","tests/unary-recursion.txt":"1d1763a8f62e1a397cc274727562d6b2c5ac80d2558e8aa930a51906c604ea00","tomlfmt.toml":"a33c21547346656aead5f46f2f07e1965f4cf0c6f2a16deb8ec06019619267aa"},"package":"cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"}
|
||||||
619
third_party/rust/askama_parser/Cargo.lock
generated
vendored
Normal file
619
third_party/rust/askama_parser/Cargo.lock
generated
vendored
Normal file
@@ -0,0 +1,619 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anes"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama_parser"
|
||||||
|
version = "0.13.0"
|
||||||
|
dependencies = [
|
||||||
|
"criterion",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cast"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
||||||
|
dependencies = [
|
||||||
|
"ciborium-io",
|
||||||
|
"ciborium-ll",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium-io"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium-ll"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
||||||
|
dependencies = [
|
||||||
|
"ciborium-io",
|
||||||
|
"half",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
||||||
|
dependencies = [
|
||||||
|
"anes",
|
||||||
|
"cast",
|
||||||
|
"ciborium",
|
||||||
|
"clap",
|
||||||
|
"criterion-plot",
|
||||||
|
"is-terminal",
|
||||||
|
"itertools",
|
||||||
|
"num-traits",
|
||||||
|
"once_cell",
|
||||||
|
"oorandom",
|
||||||
|
"plotters",
|
||||||
|
"rayon",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"tinytemplate",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion-plot"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||||
|
dependencies = [
|
||||||
|
"cast",
|
||||||
|
"itertools",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "half"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.171"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oorandom"
|
||||||
|
version = "11.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"plotters-backend",
|
||||||
|
"plotters-svg",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-backend"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-svg"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
|
||||||
|
dependencies = [
|
||||||
|
"plotters-backend",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.140"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinytemplate"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
@@ -11,17 +11,18 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.71"
|
rust-version = "1.81"
|
||||||
name = "rinja_parser"
|
name = "askama_parser"
|
||||||
version = "0.3.5"
|
version = "0.13.0"
|
||||||
build = false
|
build = false
|
||||||
|
autolib = false
|
||||||
autobins = false
|
autobins = false
|
||||||
autoexamples = false
|
autoexamples = false
|
||||||
autotests = false
|
autotests = false
|
||||||
autobenches = false
|
autobenches = false
|
||||||
description = "Parser for Rinja templates"
|
description = "Parser for Askama templates"
|
||||||
homepage = "https://github.com/rinja-rs/rinja"
|
homepage = "https://github.com/askama-rs/askama"
|
||||||
documentation = "https://docs.rs/rinja"
|
documentation = "https://docs.rs/askama"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = [
|
keywords = [
|
||||||
"markup",
|
"markup",
|
||||||
@@ -31,7 +32,7 @@ keywords = [
|
|||||||
]
|
]
|
||||||
categories = ["template-engine"]
|
categories = ["template-engine"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/rinja-rs/rinja"
|
repository = "https://github.com/askama-rs/askama"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -40,8 +41,14 @@ rustdoc-args = [
|
|||||||
"--cfg=docsrs",
|
"--cfg=docsrs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
config = [
|
||||||
|
"dep:serde",
|
||||||
|
"dep:serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "rinja_parser"
|
name = "askama_parser"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
@@ -52,18 +59,16 @@ harness = false
|
|||||||
[dependencies.memchr]
|
[dependencies.memchr]
|
||||||
version = "2"
|
version = "2"
|
||||||
|
|
||||||
[dependencies.nom]
|
|
||||||
version = "7"
|
|
||||||
features = ["alloc"]
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
features = ["derive"]
|
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.serde_derive]
|
||||||
|
version = "1.0"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.winnow]
|
||||||
|
version = "0.7.0"
|
||||||
|
|
||||||
[dev-dependencies.criterion]
|
[dev-dependencies.criterion]
|
||||||
version = "0.5"
|
version = "0.5"
|
||||||
|
|
||||||
[features]
|
|
||||||
config = ["dep:serde"]
|
|
||||||
9
third_party/rust/askama_parser/README.md
vendored
Normal file
9
third_party/rust/askama_parser/README.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# askama_parser: template parser for the Askama templating engine
|
||||||
|
|
||||||
|
[](https://crates.io/crates/askama_parser)
|
||||||
|
[](https://github.com/askama-rs/askama/actions/workflows/rust.yml)
|
||||||
|
[](https://askama.readthedocs.io/)
|
||||||
|
[](https://docs.rs/askama_parser/)
|
||||||
|
|
||||||
|
This crate contains the procedural macros used by the
|
||||||
|
[Askama](https://github.com/askama-rs/askama) templating engine.
|
||||||
34
third_party/rust/askama_parser/_typos.toml
vendored
Normal file
34
third_party/rust/askama_parser/_typos.toml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[default]
|
||||||
|
locale = "en-us"
|
||||||
|
|
||||||
|
[files]
|
||||||
|
extend-exclude = [
|
||||||
|
# generated files
|
||||||
|
"book/ethicalads-theme.css",
|
||||||
|
"fuzzing/fuzz/artifacts/",
|
||||||
|
"fuzzing/fuzz/corpus/",
|
||||||
|
"target/",
|
||||||
|
"askama_parser/tests/*.txt",
|
||||||
|
"testing/templates/fuzzed-*",
|
||||||
|
# we copied the files verbatim including any typos :)
|
||||||
|
"askama_parser/benches",
|
||||||
|
"askama_derive_standalone/benches",
|
||||||
|
# filler texts
|
||||||
|
"*/benches/strings.inc",
|
||||||
|
# too many false positives
|
||||||
|
"testing/tests/gen_ws_tests.py",
|
||||||
|
]
|
||||||
|
|
||||||
|
[default.extend-words]
|
||||||
|
# It's actually called that in the ASCII standard
|
||||||
|
Enquiry = "Enquiry"
|
||||||
|
|
||||||
|
# French words
|
||||||
|
exemple = "exemple"
|
||||||
|
existant = "existant"
|
||||||
|
|
||||||
|
# used in tests
|
||||||
|
Ba = "Ba"
|
||||||
|
fo = "fo"
|
||||||
|
Fo = "Fo"
|
||||||
|
sur = "sur"
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
use askama_parser::{Ast, Syntax};
|
||||||
use criterion::{Criterion, Throughput, black_box, criterion_group, criterion_main};
|
use criterion::{Criterion, Throughput, black_box, criterion_group, criterion_main};
|
||||||
use rinja_parser::{Ast, Syntax};
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
criterion_group!(benches, librustdoc);
|
criterion_group!(benches, librustdoc);
|
||||||
1
third_party/rust/askama_parser/clippy.toml
vendored
Normal file
1
third_party/rust/askama_parser/clippy.toml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
msrv = "1.81.0"
|
||||||
9
third_party/rust/askama_parser/deny.toml
vendored
Normal file
9
third_party/rust/askama_parser/deny.toml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[licenses]
|
||||||
|
version = 2
|
||||||
|
allow = ["Apache-2.0", "MIT", "MIT-0", "Unicode-3.0"]
|
||||||
|
private = { ignore = true }
|
||||||
|
|
||||||
|
[[licenses.clarify]]
|
||||||
|
name = "ring"
|
||||||
|
expression = "ISC AND MIT AND OpenSSL"
|
||||||
|
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]
|
||||||
144
third_party/rust/askama_parser/src/ascii_str.rs
vendored
Normal file
144
third_party/rust/askama_parser/src/ascii_str.rs
vendored
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// FIXME: Replace `AsciiChar` with `[core:ascii::Char]` once [#110998] is stable
|
||||||
|
// [#110998]: https://github.com/rust-lang/rust/issues/110998
|
||||||
|
|
||||||
|
#![allow(unreachable_pub)]
|
||||||
|
|
||||||
|
use core::ops::{Deref, Index, IndexMut};
|
||||||
|
|
||||||
|
pub use _ascii_char::AsciiChar;
|
||||||
|
|
||||||
|
/// A string that only contains ASCII characters, same layout as [`str`].
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct AsciiStr([AsciiChar]);
|
||||||
|
|
||||||
|
impl AsciiStr {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new_sized<const N: usize>(src: &str) -> [AsciiChar; N] {
|
||||||
|
if !src.is_ascii() || src.len() > N {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
|
||||||
|
let src = src.as_bytes();
|
||||||
|
let mut result = [AsciiChar::NULL; N];
|
||||||
|
let mut i = 0;
|
||||||
|
while i < src.len() {
|
||||||
|
result[i] = AsciiChar::new(src[i]);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn from_slice(src: &[AsciiChar]) -> &Self {
|
||||||
|
// SAFETY: `Self` is transparent over `[AsciiChar]`.
|
||||||
|
unsafe { core::mem::transmute::<&[AsciiChar], &AsciiStr>(src) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn as_str(&self) -> &str {
|
||||||
|
// SAFETY: `Self` has the same layout as `str`,
|
||||||
|
// and all ASCII characters are valid UTF-8 characters.
|
||||||
|
unsafe { core::mem::transmute::<&AsciiStr, &str>(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must not implement `DerefMut`. Not every `char` is an ASCII character.
|
||||||
|
impl Deref for AsciiStr {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Idx> Index<Idx> for AsciiStr
|
||||||
|
where
|
||||||
|
[AsciiChar]: Index<Idx, Output = [AsciiChar]>,
|
||||||
|
{
|
||||||
|
type Output = [AsciiChar];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: Idx) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Idx> IndexMut<Idx> for AsciiStr
|
||||||
|
where
|
||||||
|
[AsciiChar]: IndexMut<Idx, Output = [AsciiChar]>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn index_mut(&mut self, index: Idx) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for &'static AsciiStr {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
// SAFETY: `Self` has the same layout as `str`.
|
||||||
|
unsafe { core::mem::transmute::<&str, &AsciiStr>("") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsciiChar {
|
||||||
|
pub const NULL: AsciiChar = AsciiChar::new(0);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn slice_as_bytes<const N: usize>(src: &[AsciiChar; N]) -> &[u8; N] {
|
||||||
|
// SAFETY: `[AsciiChar]` has the same layout as `[u8]`.
|
||||||
|
unsafe { core::mem::transmute::<&[AsciiChar; N], &[u8; N]>(src) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn two_digits(d: u32) -> [Self; 2] {
|
||||||
|
const ALPHABET: &[u8; 10] = b"0123456789";
|
||||||
|
|
||||||
|
if d >= ALPHABET.len().pow(2) as u32 {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
[
|
||||||
|
Self::new(ALPHABET[d as usize / ALPHABET.len()]),
|
||||||
|
Self::new(ALPHABET[d as usize % ALPHABET.len()]),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn two_hex_digits(d: u32) -> [Self; 2] {
|
||||||
|
const ALPHABET: &[u8; 16] = b"0123456789abcdef";
|
||||||
|
|
||||||
|
if d >= ALPHABET.len().pow(2) as u32 {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
[
|
||||||
|
Self::new(ALPHABET[d as usize / ALPHABET.len()]),
|
||||||
|
Self::new(ALPHABET[d as usize % ALPHABET.len()]),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod _ascii_char {
|
||||||
|
/// A character that is known to be in ASCII range, same layout as [`u8`].
|
||||||
|
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct AsciiChar(u8);
|
||||||
|
|
||||||
|
impl AsciiChar {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(c: u8) -> Self {
|
||||||
|
if c.is_ascii() { Self(c) } else { panic!() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
779
third_party/rust/askama_parser/src/expr.rs
vendored
Normal file
779
third_party/rust/askama_parser/src/expr.rs
vendored
Normal file
@@ -0,0 +1,779 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use winnow::Parser;
|
||||||
|
use winnow::ascii::digit1;
|
||||||
|
use winnow::combinator::{
|
||||||
|
alt, cut_err, fail, not, opt, peek, preceded, repeat, separated, terminated,
|
||||||
|
};
|
||||||
|
use winnow::error::ParserError as _;
|
||||||
|
use winnow::stream::Stream as _;
|
||||||
|
|
||||||
|
use crate::node::CondTest;
|
||||||
|
use crate::{
|
||||||
|
CharLit, ErrorContext, Level, Num, ParseErr, ParseResult, PathOrIdentifier, Span, StrLit,
|
||||||
|
WithSpan, char_lit, filter, identifier, keyword, num_lit, path_or_identifier, skip_ws0,
|
||||||
|
skip_ws1, str_lit, ws,
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! expr_prec_layer {
|
||||||
|
( $name:ident, $inner:ident, $op:expr ) => {
|
||||||
|
fn $name(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let mut level_guard = level.guard();
|
||||||
|
let start = *i;
|
||||||
|
let mut expr = Self::$inner(i, level)?;
|
||||||
|
let mut i_before = *i;
|
||||||
|
let mut right = opt((ws($op), |i: &mut _| Self::$inner(i, level)));
|
||||||
|
while let Some((op, right)) = right.parse_next(i)? {
|
||||||
|
level_guard.nest(i_before)?;
|
||||||
|
i_before = *i;
|
||||||
|
expr = WithSpan::new(Self::BinOp(op, Box::new(expr), Box::new(right)), start);
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_expr<'a>(
|
||||||
|
expr: &WithSpan<'a, Expr<'a>>,
|
||||||
|
allow_underscore: bool,
|
||||||
|
) -> Result<(), ParseErr<'a>> {
|
||||||
|
match &expr.inner {
|
||||||
|
Expr::Var("_") if !allow_underscore => Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"reserved keyword `_` cannot be used here",
|
||||||
|
expr.span,
|
||||||
|
))),
|
||||||
|
Expr::IsDefined(var) | Expr::IsNotDefined(var) => {
|
||||||
|
if *var == "_" {
|
||||||
|
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"reserved keyword `_` cannot be used here",
|
||||||
|
expr.span,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::BoolLit(_)
|
||||||
|
| Expr::NumLit(_, _)
|
||||||
|
| Expr::StrLit(_)
|
||||||
|
| Expr::CharLit(_)
|
||||||
|
| Expr::Path(_)
|
||||||
|
| Expr::Attr(_, _)
|
||||||
|
| Expr::Filter(_)
|
||||||
|
| Expr::NamedArgument(_, _)
|
||||||
|
| Expr::Var(_)
|
||||||
|
| Expr::RustMacro(_, _)
|
||||||
|
| Expr::Try(_)
|
||||||
|
| Expr::FilterSource
|
||||||
|
| Expr::LetCond(_) => Ok(()),
|
||||||
|
Expr::Array(elems) | Expr::Tuple(elems) | Expr::Concat(elems) => {
|
||||||
|
for elem in elems {
|
||||||
|
check_expr(elem, allow_underscore)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Expr::Index(elem1, elem2) | Expr::BinOp(_, elem1, elem2) => {
|
||||||
|
check_expr(elem1, false)?;
|
||||||
|
check_expr(elem2, false)
|
||||||
|
}
|
||||||
|
Expr::Range(_, elem1, elem2) => {
|
||||||
|
if let Some(elem1) = elem1 {
|
||||||
|
check_expr(elem1, false)?;
|
||||||
|
}
|
||||||
|
if let Some(elem2) = elem2 {
|
||||||
|
check_expr(elem2, false)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Expr::As(elem, _) | Expr::Unary(_, elem) | Expr::Group(elem) => check_expr(elem, false),
|
||||||
|
Expr::Call { path, args, .. } => {
|
||||||
|
check_expr(path, false)?;
|
||||||
|
for arg in args {
|
||||||
|
check_expr(arg, false)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Expr<'a> {
|
||||||
|
BoolLit(bool),
|
||||||
|
NumLit(&'a str, Num<'a>),
|
||||||
|
StrLit(StrLit<'a>),
|
||||||
|
CharLit(CharLit<'a>),
|
||||||
|
Var(&'a str),
|
||||||
|
Path(Vec<&'a str>),
|
||||||
|
Array(Vec<WithSpan<'a, Expr<'a>>>),
|
||||||
|
Attr(Box<WithSpan<'a, Expr<'a>>>, Attr<'a>),
|
||||||
|
Index(Box<WithSpan<'a, Expr<'a>>>, Box<WithSpan<'a, Expr<'a>>>),
|
||||||
|
Filter(Filter<'a>),
|
||||||
|
As(Box<WithSpan<'a, Expr<'a>>>, &'a str),
|
||||||
|
NamedArgument(&'a str, Box<WithSpan<'a, Expr<'a>>>),
|
||||||
|
Unary(&'a str, Box<WithSpan<'a, Expr<'a>>>),
|
||||||
|
BinOp(
|
||||||
|
&'a str,
|
||||||
|
Box<WithSpan<'a, Expr<'a>>>,
|
||||||
|
Box<WithSpan<'a, Expr<'a>>>,
|
||||||
|
),
|
||||||
|
Range(
|
||||||
|
&'a str,
|
||||||
|
Option<Box<WithSpan<'a, Expr<'a>>>>,
|
||||||
|
Option<Box<WithSpan<'a, Expr<'a>>>>,
|
||||||
|
),
|
||||||
|
Group(Box<WithSpan<'a, Expr<'a>>>),
|
||||||
|
Tuple(Vec<WithSpan<'a, Expr<'a>>>),
|
||||||
|
Call {
|
||||||
|
path: Box<WithSpan<'a, Expr<'a>>>,
|
||||||
|
args: Vec<WithSpan<'a, Expr<'a>>>,
|
||||||
|
generics: Vec<WithSpan<'a, TyGenerics<'a>>>,
|
||||||
|
},
|
||||||
|
RustMacro(Vec<&'a str>, &'a str),
|
||||||
|
Try(Box<WithSpan<'a, Expr<'a>>>),
|
||||||
|
/// This variant should never be used directly. It is created when generating filter blocks.
|
||||||
|
FilterSource,
|
||||||
|
IsDefined(&'a str),
|
||||||
|
IsNotDefined(&'a str),
|
||||||
|
Concat(Vec<WithSpan<'a, Expr<'a>>>),
|
||||||
|
/// If you have `&& let Some(y)`, this variant handles it.
|
||||||
|
LetCond(Box<WithSpan<'a, CondTest<'a>>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Expr<'a> {
|
||||||
|
pub(super) fn arguments(
|
||||||
|
i: &mut &'a str,
|
||||||
|
level: Level<'_>,
|
||||||
|
is_template_macro: bool,
|
||||||
|
) -> ParseResult<'a, Vec<WithSpan<'a, Self>>> {
|
||||||
|
let _level_guard = level.nest(i)?;
|
||||||
|
let mut named_arguments = HashSet::new();
|
||||||
|
let start = *i;
|
||||||
|
|
||||||
|
preceded(
|
||||||
|
ws('('),
|
||||||
|
cut_err(terminated(
|
||||||
|
separated(
|
||||||
|
0..,
|
||||||
|
ws(move |i: &mut _| {
|
||||||
|
// Needed to prevent borrowing it twice between this closure and the one
|
||||||
|
// calling `Self::named_arguments`.
|
||||||
|
let named_arguments = &mut named_arguments;
|
||||||
|
let has_named_arguments = !named_arguments.is_empty();
|
||||||
|
|
||||||
|
let expr = alt((
|
||||||
|
move |i: &mut _| {
|
||||||
|
Self::named_argument(
|
||||||
|
i,
|
||||||
|
level,
|
||||||
|
named_arguments,
|
||||||
|
start,
|
||||||
|
is_template_macro,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
move |i: &mut _| Self::parse(i, level, false),
|
||||||
|
))
|
||||||
|
.parse_next(i)?;
|
||||||
|
if has_named_arguments && !matches!(*expr, Self::NamedArgument(_, _)) {
|
||||||
|
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"named arguments must always be passed last",
|
||||||
|
start,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
',',
|
||||||
|
),
|
||||||
|
(opt(ws(',')), ')'),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn named_argument(
|
||||||
|
i: &mut &'a str,
|
||||||
|
level: Level<'_>,
|
||||||
|
named_arguments: &mut HashSet<&'a str>,
|
||||||
|
start: &'a str,
|
||||||
|
is_template_macro: bool,
|
||||||
|
) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
if !is_template_macro {
|
||||||
|
// If this is not a template macro, we don't want to parse named arguments so
|
||||||
|
// we instead return an error which will allow to continue the parsing.
|
||||||
|
return fail.parse_next(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (argument, _, value) = (identifier, ws('='), move |i: &mut _| {
|
||||||
|
Self::parse(i, level, false)
|
||||||
|
})
|
||||||
|
.parse_next(i)?;
|
||||||
|
if named_arguments.insert(argument) {
|
||||||
|
Ok(WithSpan::new(
|
||||||
|
Self::NamedArgument(argument, Box::new(value)),
|
||||||
|
start,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
format!("named argument `{argument}` was passed more than once"),
|
||||||
|
start,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn parse(
|
||||||
|
i: &mut &'a str,
|
||||||
|
level: Level<'_>,
|
||||||
|
allow_underscore: bool,
|
||||||
|
) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let _level_guard = level.nest(i)?;
|
||||||
|
let start = Span::from(*i);
|
||||||
|
let range_right = move |i: &mut _| {
|
||||||
|
(
|
||||||
|
ws(alt(("..=", ".."))),
|
||||||
|
opt(move |i: &mut _| Self::or(i, level)),
|
||||||
|
)
|
||||||
|
.parse_next(i)
|
||||||
|
};
|
||||||
|
let expr = alt((
|
||||||
|
range_right.map(move |(op, right)| {
|
||||||
|
WithSpan::new(Self::Range(op, None, right.map(Box::new)), start)
|
||||||
|
}),
|
||||||
|
(move |i: &mut _| Self::or(i, level), opt(range_right)).map(move |(left, right)| {
|
||||||
|
match right {
|
||||||
|
Some((op, right)) => WithSpan::new(
|
||||||
|
Self::Range(op, Some(Box::new(left)), right.map(Box::new)),
|
||||||
|
start,
|
||||||
|
),
|
||||||
|
None => left,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
.parse_next(i)?;
|
||||||
|
check_expr(&expr, allow_underscore)?;
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_prec_layer!(or, and, "||");
|
||||||
|
expr_prec_layer!(and, compare, "&&");
|
||||||
|
expr_prec_layer!(compare, bor, alt(("==", "!=", ">=", ">", "<=", "<",)));
|
||||||
|
expr_prec_layer!(bor, bxor, "bitor".value("|"));
|
||||||
|
expr_prec_layer!(bxor, band, token_xor);
|
||||||
|
expr_prec_layer!(band, shifts, token_bitand);
|
||||||
|
expr_prec_layer!(shifts, addsub, alt((">>", "<<")));
|
||||||
|
expr_prec_layer!(addsub, concat, alt(("+", "-")));
|
||||||
|
|
||||||
|
fn concat(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
fn concat_expr<'a>(
|
||||||
|
i: &mut &'a str,
|
||||||
|
level: Level<'_>,
|
||||||
|
) -> ParseResult<'a, Option<WithSpan<'a, Expr<'a>>>> {
|
||||||
|
let ws1 = |i: &mut _| opt(skip_ws1).parse_next(i);
|
||||||
|
|
||||||
|
let start = *i;
|
||||||
|
let data = opt((ws1, '~', ws1, |i: &mut _| Expr::muldivmod(i, level))).parse_next(i)?;
|
||||||
|
if let Some((t1, _, t2, expr)) = data {
|
||||||
|
if t1.is_none() || t2.is_none() {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"the concat operator `~` must be surrounded by spaces",
|
||||||
|
start,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(Some(expr))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = *i;
|
||||||
|
let expr = Self::muldivmod(i, level)?;
|
||||||
|
let expr2 = concat_expr(i, level)?;
|
||||||
|
if let Some(expr2) = expr2 {
|
||||||
|
let mut exprs = vec![expr, expr2];
|
||||||
|
while let Some(expr) = concat_expr(i, level)? {
|
||||||
|
exprs.push(expr);
|
||||||
|
}
|
||||||
|
Ok(WithSpan::new(Self::Concat(exprs), start))
|
||||||
|
} else {
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_prec_layer!(muldivmod, is_as, alt(("*", "/", "%")));
|
||||||
|
|
||||||
|
fn is_as(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let start = *i;
|
||||||
|
let lhs = Self::filtered(i, level)?;
|
||||||
|
let before_keyword = *i;
|
||||||
|
let rhs = opt(ws(identifier)).parse_next(i)?;
|
||||||
|
match rhs {
|
||||||
|
Some("is") => {}
|
||||||
|
Some("as") => {
|
||||||
|
let target = opt(identifier).parse_next(i)?;
|
||||||
|
let target = target.unwrap_or_default();
|
||||||
|
if crate::PRIMITIVE_TYPES.contains(&target) {
|
||||||
|
return Ok(WithSpan::new(Self::As(Box::new(lhs), target), start));
|
||||||
|
} else if target.is_empty() {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"`as` operator expects the name of a primitive type on its right-hand side",
|
||||||
|
before_keyword.trim_start(),
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
format!(
|
||||||
|
"`as` operator expects the name of a primitive type on its right-hand \
|
||||||
|
side, found `{target}`"
|
||||||
|
),
|
||||||
|
before_keyword.trim_start(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
*i = before_keyword;
|
||||||
|
return Ok(lhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rhs = opt(terminated(opt(keyword("not")), ws(keyword("defined")))).parse_next(i)?;
|
||||||
|
let ctor = match rhs {
|
||||||
|
None => {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"expected `defined` or `not defined` after `is`",
|
||||||
|
// We use `start` to show the whole `var is` thing instead of the current token.
|
||||||
|
start,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Some(None) => Self::IsDefined,
|
||||||
|
Some(Some(_)) => Self::IsNotDefined,
|
||||||
|
};
|
||||||
|
let var_name = match *lhs {
|
||||||
|
Self::Var(var_name) => var_name,
|
||||||
|
Self::Attr(_, _) => {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"`is defined` operator can only be used on variables, not on their fields",
|
||||||
|
start,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"`is defined` operator can only be used on variables",
|
||||||
|
start,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(WithSpan::new(ctor(var_name), start))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filtered(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let mut level_guard = level.guard();
|
||||||
|
let start = *i;
|
||||||
|
let mut res = Self::prefix(i, level)?;
|
||||||
|
while let Some((name, generics, args)) = opt(|i: &mut _| filter(i, level)).parse_next(i)? {
|
||||||
|
level_guard.nest(i)?;
|
||||||
|
|
||||||
|
let mut arguments = args.unwrap_or_else(|| Vec::with_capacity(1));
|
||||||
|
arguments.insert(0, res);
|
||||||
|
|
||||||
|
res = WithSpan::new(
|
||||||
|
Self::Filter(Filter {
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
generics,
|
||||||
|
}),
|
||||||
|
start,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prefix(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let start = *i;
|
||||||
|
|
||||||
|
// This is a rare place where we create recursion in the parsed AST
|
||||||
|
// without recursing the parser call stack. However, this can lead
|
||||||
|
// to stack overflows in drop glue when the AST is very deep.
|
||||||
|
let mut level_guard = level.guard();
|
||||||
|
let mut ops = vec![];
|
||||||
|
let mut i_before = *i;
|
||||||
|
while let Some(op) = opt(ws(alt(("!", "-", "*", "&")))).parse_next(i)? {
|
||||||
|
level_guard.nest(i_before)?;
|
||||||
|
ops.push(op);
|
||||||
|
i_before = *i;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut expr = Suffix::parse(i, level)?;
|
||||||
|
for op in ops.iter().rev() {
|
||||||
|
expr = WithSpan::new(Self::Unary(op, Box::new(expr)), start);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn single(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
alt((
|
||||||
|
Self::num,
|
||||||
|
Self::str,
|
||||||
|
Self::char,
|
||||||
|
Self::path_var_bool,
|
||||||
|
move |i: &mut _| Self::array(i, level),
|
||||||
|
move |i: &mut _| Self::group(i, level),
|
||||||
|
))
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn group(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let start = *i;
|
||||||
|
let expr = preceded(ws('('), opt(|i: &mut _| Self::parse(i, level, true))).parse_next(i)?;
|
||||||
|
let Some(expr) = expr else {
|
||||||
|
let _ = ')'.parse_next(i)?;
|
||||||
|
return Ok(WithSpan::new(Self::Tuple(vec![]), start));
|
||||||
|
};
|
||||||
|
|
||||||
|
let comma = ws(opt(peek(','))).parse_next(i)?;
|
||||||
|
if comma.is_none() {
|
||||||
|
let _ = ')'.parse_next(i)?;
|
||||||
|
return Ok(WithSpan::new(Self::Group(Box::new(expr)), start));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut exprs = vec![expr];
|
||||||
|
repeat(
|
||||||
|
0..,
|
||||||
|
preceded(',', ws(|i: &mut _| Self::parse(i, level, true))),
|
||||||
|
)
|
||||||
|
.fold(
|
||||||
|
|| (),
|
||||||
|
|(), expr| {
|
||||||
|
exprs.push(expr);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.parse_next(i)?;
|
||||||
|
let _ = (ws(opt(',')), ')').parse_next(i)?;
|
||||||
|
Ok(WithSpan::new(Self::Tuple(exprs), start))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let start = *i;
|
||||||
|
let array = preceded(
|
||||||
|
ws('['),
|
||||||
|
cut_err(terminated(
|
||||||
|
opt(terminated(
|
||||||
|
separated(1.., ws(move |i: &mut _| Self::parse(i, level, true)), ','),
|
||||||
|
ws(opt(',')),
|
||||||
|
)),
|
||||||
|
']',
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse_next(i)?;
|
||||||
|
Ok(WithSpan::new(Self::Array(array.unwrap_or_default()), start))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_var_bool(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let start = *i;
|
||||||
|
path_or_identifier
|
||||||
|
.map(|v| match v {
|
||||||
|
PathOrIdentifier::Path(v) => Self::Path(v),
|
||||||
|
PathOrIdentifier::Identifier("true") => Self::BoolLit(true),
|
||||||
|
PathOrIdentifier::Identifier("false") => Self::BoolLit(false),
|
||||||
|
PathOrIdentifier::Identifier(v) => Self::Var(v),
|
||||||
|
})
|
||||||
|
.parse_next(i)
|
||||||
|
.map(|expr| WithSpan::new(expr, start))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn str(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let start = *i;
|
||||||
|
str_lit
|
||||||
|
.map(|i| WithSpan::new(Self::StrLit(i), start))
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let start = *i;
|
||||||
|
let (num, full) = num_lit.with_taken().parse_next(i)?;
|
||||||
|
Ok(WithSpan::new(Expr::NumLit(full, num), start))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn char(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let start = *i;
|
||||||
|
char_lit
|
||||||
|
.map(|i| WithSpan::new(Self::CharLit(i), start))
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn contains_bool_lit_or_is_defined(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::BoolLit(_) | Self::IsDefined(_) | Self::IsNotDefined(_) => true,
|
||||||
|
Self::Unary(_, expr) | Self::Group(expr) => expr.contains_bool_lit_or_is_defined(),
|
||||||
|
Self::BinOp("&&" | "||", left, right) => {
|
||||||
|
left.contains_bool_lit_or_is_defined() || right.contains_bool_lit_or_is_defined()
|
||||||
|
}
|
||||||
|
Self::NumLit(_, _)
|
||||||
|
| Self::StrLit(_)
|
||||||
|
| Self::CharLit(_)
|
||||||
|
| Self::Var(_)
|
||||||
|
| Self::FilterSource
|
||||||
|
| Self::RustMacro(_, _)
|
||||||
|
| Self::As(_, _)
|
||||||
|
| Self::Call { .. }
|
||||||
|
| Self::Range(_, _, _)
|
||||||
|
| Self::Try(_)
|
||||||
|
| Self::NamedArgument(_, _)
|
||||||
|
| Self::Filter(_)
|
||||||
|
| Self::Attr(_, _)
|
||||||
|
| Self::Index(_, _)
|
||||||
|
| Self::Tuple(_)
|
||||||
|
| Self::Array(_)
|
||||||
|
| Self::BinOp(_, _, _)
|
||||||
|
| Self::Path(_)
|
||||||
|
| Self::Concat(_)
|
||||||
|
| Self::LetCond(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn token_xor<'a>(i: &mut &'a str) -> ParseResult<'a> {
|
||||||
|
let good = alt((keyword("xor").value(true), '^'.value(false))).parse_next(i)?;
|
||||||
|
if good {
|
||||||
|
Ok("^")
|
||||||
|
} else {
|
||||||
|
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"the binary XOR operator is called `xor` in askama",
|
||||||
|
*i,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn token_bitand<'a>(i: &mut &'a str) -> ParseResult<'a> {
|
||||||
|
let good = alt((keyword("bitand").value(true), ('&', not('&')).value(false))).parse_next(i)?;
|
||||||
|
if good {
|
||||||
|
Ok("&")
|
||||||
|
} else {
|
||||||
|
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"the binary AND operator is called `bitand` in askama",
|
||||||
|
*i,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Filter<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub arguments: Vec<WithSpan<'a, Expr<'a>>>,
|
||||||
|
pub generics: Vec<WithSpan<'a, TyGenerics<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Attr<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub generics: Vec<WithSpan<'a, TyGenerics<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Suffix<'a> {
|
||||||
|
Attr(Attr<'a>),
|
||||||
|
Index(WithSpan<'a, Expr<'a>>),
|
||||||
|
Call {
|
||||||
|
args: Vec<WithSpan<'a, Expr<'a>>>,
|
||||||
|
generics: Vec<WithSpan<'a, TyGenerics<'a>>>,
|
||||||
|
},
|
||||||
|
// The value is the arguments of the macro call.
|
||||||
|
MacroCall(&'a str),
|
||||||
|
Try,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Suffix<'a> {
|
||||||
|
fn parse(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Expr<'a>>> {
|
||||||
|
let mut level_guard = level.guard();
|
||||||
|
let mut expr = Expr::single(i, level)?;
|
||||||
|
let mut right = opt(alt((
|
||||||
|
|i: &mut _| Self::attr(i, level),
|
||||||
|
|i: &mut _| Self::index(i, level),
|
||||||
|
|i: &mut _| Self::call(i, level),
|
||||||
|
Self::r#try,
|
||||||
|
Self::r#macro,
|
||||||
|
)));
|
||||||
|
loop {
|
||||||
|
let before_suffix = *i;
|
||||||
|
let suffix = right.parse_next(i)?;
|
||||||
|
let Some(suffix) = suffix else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
level_guard.nest(before_suffix)?;
|
||||||
|
|
||||||
|
match suffix {
|
||||||
|
Self::Attr(attr) => {
|
||||||
|
expr = WithSpan::new(Expr::Attr(expr.into(), attr), before_suffix)
|
||||||
|
}
|
||||||
|
Self::Index(index) => {
|
||||||
|
expr = WithSpan::new(Expr::Index(expr.into(), index.into()), before_suffix);
|
||||||
|
}
|
||||||
|
Self::Call { args, generics } => {
|
||||||
|
expr = WithSpan::new(
|
||||||
|
Expr::Call {
|
||||||
|
path: expr.into(),
|
||||||
|
args,
|
||||||
|
generics,
|
||||||
|
},
|
||||||
|
before_suffix,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::Try => expr = WithSpan::new(Expr::Try(expr.into()), before_suffix),
|
||||||
|
Self::MacroCall(args) => match expr.inner {
|
||||||
|
Expr::Path(path) => {
|
||||||
|
expr = WithSpan::new(Expr::RustMacro(path, args), before_suffix)
|
||||||
|
}
|
||||||
|
Expr::Var(name) => {
|
||||||
|
expr = WithSpan::new(Expr::RustMacro(vec![name], args), before_suffix)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(winnow::error::ErrMode::from_input(&before_suffix).cut());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn r#macro(i: &mut &'a str) -> ParseResult<'a, Self> {
|
||||||
|
fn nested_parenthesis<'a>(input: &mut &'a str) -> ParseResult<'a, ()> {
|
||||||
|
let mut nested = 0;
|
||||||
|
let mut last = 0;
|
||||||
|
let mut in_str = false;
|
||||||
|
let mut escaped = false;
|
||||||
|
|
||||||
|
for (i, c) in input.char_indices() {
|
||||||
|
if !(c == '(' || c == ')') || !in_str {
|
||||||
|
match c {
|
||||||
|
'(' => nested += 1,
|
||||||
|
')' => {
|
||||||
|
if nested == 0 {
|
||||||
|
last = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nested -= 1;
|
||||||
|
}
|
||||||
|
'"' => {
|
||||||
|
if in_str {
|
||||||
|
if !escaped {
|
||||||
|
in_str = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
in_str = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'\\' => {
|
||||||
|
escaped = !escaped;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if escaped && c != '\\' {
|
||||||
|
escaped = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nested == 0 {
|
||||||
|
let _ = input.next_slice(last);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
fail.parse_next(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preceded(
|
||||||
|
(ws('!'), '('),
|
||||||
|
cut_err(terminated(
|
||||||
|
nested_parenthesis.take().map(Self::MacroCall),
|
||||||
|
')',
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attr(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||||
|
preceded(
|
||||||
|
ws(('.', not('.'))),
|
||||||
|
cut_err((
|
||||||
|
alt((digit1, identifier)),
|
||||||
|
opt(|i: &mut _| call_generics(i, level)),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.map(|(name, generics)| {
|
||||||
|
Self::Attr(Attr {
|
||||||
|
name,
|
||||||
|
generics: generics.unwrap_or_default(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||||
|
preceded(
|
||||||
|
ws('['),
|
||||||
|
cut_err(terminated(
|
||||||
|
ws(move |i: &mut _| Expr::parse(i, level, true)),
|
||||||
|
']',
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.map(Self::Index)
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||||
|
(opt(|i: &mut _| call_generics(i, level)), |i: &mut _| {
|
||||||
|
Expr::arguments(i, level, false)
|
||||||
|
})
|
||||||
|
.map(|(generics, args)| Self::Call {
|
||||||
|
args,
|
||||||
|
generics: generics.unwrap_or_default(),
|
||||||
|
})
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn r#try(i: &mut &'a str) -> ParseResult<'a, Self> {
|
||||||
|
preceded(skip_ws0, '?').map(|_| Self::Try).parse_next(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct TyGenerics<'a> {
|
||||||
|
pub refs: usize,
|
||||||
|
pub path: Vec<&'a str>,
|
||||||
|
pub args: Vec<WithSpan<'a, TyGenerics<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> TyGenerics<'i> {
|
||||||
|
fn parse(i: &mut &'i str, level: Level<'_>) -> ParseResult<'i, WithSpan<'i, Self>> {
|
||||||
|
let start = *i;
|
||||||
|
(
|
||||||
|
repeat(0.., ws('&')),
|
||||||
|
separated(1.., ws(identifier), "::"),
|
||||||
|
opt(|i: &mut _| Self::args(i, level)).map(|generics| generics.unwrap_or_default()),
|
||||||
|
)
|
||||||
|
.map(|(refs, path, args)| WithSpan::new(TyGenerics { refs, path, args }, start))
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn args(
|
||||||
|
i: &mut &'i str,
|
||||||
|
level: Level<'_>,
|
||||||
|
) -> ParseResult<'i, Vec<WithSpan<'i, TyGenerics<'i>>>> {
|
||||||
|
ws('<').parse_next(i)?;
|
||||||
|
let _level_guard = level.nest(i)?;
|
||||||
|
cut_err(terminated(
|
||||||
|
terminated(
|
||||||
|
separated(0.., |i: &mut _| TyGenerics::parse(i, level), ws(',')),
|
||||||
|
ws(opt(',')),
|
||||||
|
),
|
||||||
|
'>',
|
||||||
|
))
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn call_generics<'i>(
|
||||||
|
i: &mut &'i str,
|
||||||
|
level: Level<'_>,
|
||||||
|
) -> ParseResult<'i, Vec<WithSpan<'i, TyGenerics<'i>>>> {
|
||||||
|
preceded(ws("::"), cut_err(|i: &mut _| TyGenerics::args(i, level))).parse_next(i)
|
||||||
|
}
|
||||||
1501
third_party/rust/askama_parser/src/lib.rs
vendored
Normal file
1501
third_party/rust/askama_parser/src/lib.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1435
third_party/rust/askama_parser/src/node.rs
vendored
Normal file
1435
third_party/rust/askama_parser/src/node.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
276
third_party/rust/askama_parser/src/target.rs
vendored
Normal file
276
third_party/rust/askama_parser/src/target.rs
vendored
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
use winnow::combinator::{alt, opt, peek, preceded, separated};
|
||||||
|
use winnow::token::one_of;
|
||||||
|
use winnow::{ModalParser, Parser};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
CharLit, ErrorContext, Num, ParseErr, ParseResult, PathOrIdentifier, State, StrLit, WithSpan,
|
||||||
|
bool_lit, char_lit, identifier, is_rust_keyword, keyword, num_lit, path_or_identifier, str_lit,
|
||||||
|
ws,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Target<'a> {
|
||||||
|
Name(&'a str),
|
||||||
|
Tuple(Vec<&'a str>, Vec<Target<'a>>),
|
||||||
|
Array(Vec<&'a str>, Vec<Target<'a>>),
|
||||||
|
Struct(Vec<&'a str>, Vec<(&'a str, Target<'a>)>),
|
||||||
|
NumLit(&'a str, Num<'a>),
|
||||||
|
StrLit(StrLit<'a>),
|
||||||
|
CharLit(CharLit<'a>),
|
||||||
|
BoolLit(&'a str),
|
||||||
|
Path(Vec<&'a str>),
|
||||||
|
OrChain(Vec<Target<'a>>),
|
||||||
|
Placeholder(WithSpan<'a, ()>),
|
||||||
|
/// The `Option` is the variable name (if any) in `var_name @ ..`.
|
||||||
|
Rest(WithSpan<'a, Option<&'a str>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Target<'a> {
|
||||||
|
/// Parses multiple targets with `or` separating them
|
||||||
|
pub(super) fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||||
|
let _level_guard = s.level.nest(i)?;
|
||||||
|
let mut p = opt(preceded(ws(keyword("or")), |i: &mut _| {
|
||||||
|
Self::parse_one(i, s)
|
||||||
|
}));
|
||||||
|
|
||||||
|
let target = Self::parse_one(i, s)?;
|
||||||
|
let Some(snd_target) = p.parse_next(i)? else {
|
||||||
|
return Ok(target);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut targets = vec![target, snd_target];
|
||||||
|
while let Some(target) = p.parse_next(i)? {
|
||||||
|
targets.push(target);
|
||||||
|
}
|
||||||
|
Ok(Self::OrChain(targets))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a single target without an `or`, unless it is wrapped in parentheses.
|
||||||
|
fn parse_one(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||||
|
let mut opt_opening_paren = opt(ws('(')).map(|o| o.is_some());
|
||||||
|
let mut opt_opening_brace = opt(ws('{')).map(|o| o.is_some());
|
||||||
|
let mut opt_opening_bracket = opt(ws('[')).map(|o| o.is_some());
|
||||||
|
|
||||||
|
let lit = opt(Self::lit).parse_next(i)?;
|
||||||
|
if let Some(lit) = lit {
|
||||||
|
return Ok(lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// match tuples and unused parentheses
|
||||||
|
let target_is_tuple = opt_opening_paren.parse_next(i)?;
|
||||||
|
if target_is_tuple {
|
||||||
|
let (singleton, mut targets) =
|
||||||
|
collect_targets(i, ')', |i: &mut _| Self::unnamed(i, s))?;
|
||||||
|
if singleton {
|
||||||
|
return Ok(targets.pop().unwrap());
|
||||||
|
}
|
||||||
|
return Ok(Self::Tuple(
|
||||||
|
Vec::new(),
|
||||||
|
only_one_rest_pattern(targets, false, "tuple")?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let target_is_array = opt_opening_bracket.parse_next(i)?;
|
||||||
|
if target_is_array {
|
||||||
|
let (singleton, mut targets) =
|
||||||
|
collect_targets(i, ']', |i: &mut _| Self::unnamed(i, s))?;
|
||||||
|
if singleton {
|
||||||
|
return Ok(targets.pop().unwrap());
|
||||||
|
}
|
||||||
|
return Ok(Self::Array(
|
||||||
|
Vec::new(),
|
||||||
|
only_one_rest_pattern(targets, true, "array")?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = path_or_identifier.try_map(|v| match v {
|
||||||
|
PathOrIdentifier::Path(v) => Ok(v),
|
||||||
|
PathOrIdentifier::Identifier(v) => Err(v),
|
||||||
|
});
|
||||||
|
|
||||||
|
// match structs
|
||||||
|
let path = opt(path).parse_next(i)?;
|
||||||
|
if let Some(path) = path {
|
||||||
|
let i_before_matching_with = *i;
|
||||||
|
let _ = opt(ws(keyword("with"))).parse_next(i)?;
|
||||||
|
|
||||||
|
let is_unnamed_struct = opt_opening_paren.parse_next(i)?;
|
||||||
|
if is_unnamed_struct {
|
||||||
|
let (_, targets) = collect_targets(i, ')', |i: &mut _| Self::unnamed(i, s))?;
|
||||||
|
return Ok(Self::Tuple(
|
||||||
|
path,
|
||||||
|
only_one_rest_pattern(targets, false, "struct")?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_named_struct = opt_opening_brace.parse_next(i)?;
|
||||||
|
if is_named_struct {
|
||||||
|
let (_, targets) = collect_targets(i, '}', |i: &mut _| Self::named(i, s))?;
|
||||||
|
return Ok(Self::Struct(path, targets));
|
||||||
|
}
|
||||||
|
|
||||||
|
*i = i_before_matching_with;
|
||||||
|
return Ok(Self::Path(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
// neither literal nor struct nor path
|
||||||
|
let i_before_identifier = *i;
|
||||||
|
let name = identifier.parse_next(i)?;
|
||||||
|
let target = match name {
|
||||||
|
"_" => Self::Placeholder(WithSpan::new((), i_before_identifier)),
|
||||||
|
_ => verify_name(i_before_identifier, name)?,
|
||||||
|
};
|
||||||
|
Ok(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lit(i: &mut &'a str) -> ParseResult<'a, Self> {
|
||||||
|
alt((
|
||||||
|
str_lit.map(Self::StrLit),
|
||||||
|
char_lit.map(Self::CharLit),
|
||||||
|
num_lit
|
||||||
|
.with_taken()
|
||||||
|
.map(|(num, full)| Target::NumLit(full, num)),
|
||||||
|
bool_lit.map(Self::BoolLit),
|
||||||
|
))
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unnamed(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||||
|
alt((Self::rest, |i: &mut _| Self::parse(i, s))).parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn named(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, (&'a str, Self)> {
|
||||||
|
let start = *i;
|
||||||
|
let rest = opt(Self::rest.with_taken()).parse_next(i)?;
|
||||||
|
if let Some(rest) = rest {
|
||||||
|
let chr = peek(ws(opt(one_of([',', ':'])))).parse_next(i)?;
|
||||||
|
if let Some(chr) = chr {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
format!(
|
||||||
|
"unexpected `{chr}` character after `..`\n\
|
||||||
|
note that in a named struct, `..` must come last to ignore other members"
|
||||||
|
),
|
||||||
|
*i,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if let Target::Rest(ref s) = rest.0 {
|
||||||
|
if s.inner.is_some() {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"`@ ..` cannot be used in struct",
|
||||||
|
s.span,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok((rest.1, rest.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
*i = start;
|
||||||
|
let (src, target) = (
|
||||||
|
identifier,
|
||||||
|
opt(preceded(ws(':'), |i: &mut _| Self::parse(i, s))),
|
||||||
|
)
|
||||||
|
.parse_next(i)?;
|
||||||
|
|
||||||
|
if src == "_" {
|
||||||
|
*i = start;
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"cannot use placeholder `_` as source in named struct",
|
||||||
|
*i,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let target = match target {
|
||||||
|
Some(target) => target,
|
||||||
|
None => verify_name(start, src)?,
|
||||||
|
};
|
||||||
|
Ok((src, target))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rest(i: &mut &'a str) -> ParseResult<'a, Self> {
|
||||||
|
let start = *i;
|
||||||
|
let (ident, _) = (opt((identifier, ws('@'))), "..").parse_next(i)?;
|
||||||
|
Ok(Self::Rest(WithSpan::new(
|
||||||
|
ident.map(|(ident, _)| ident),
|
||||||
|
start,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_name<'a>(
|
||||||
|
input: &'a str,
|
||||||
|
name: &'a str,
|
||||||
|
) -> Result<Target<'a>, winnow::error::ErrMode<ErrorContext<'a>>> {
|
||||||
|
if is_rust_keyword(name) {
|
||||||
|
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
format!("cannot use `{name}` as a name: it is a rust keyword"),
|
||||||
|
input,
|
||||||
|
)))
|
||||||
|
} else if name.starts_with("__askama") {
|
||||||
|
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
format!("cannot use `{name}` as a name: it is reserved for `askama`"),
|
||||||
|
input,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(Target::Name(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_targets<'a, T>(
|
||||||
|
i: &mut &'a str,
|
||||||
|
delim: char,
|
||||||
|
one: impl ModalParser<&'a str, T, ErrorContext<'a>>,
|
||||||
|
) -> ParseResult<'a, (bool, Vec<T>)> {
|
||||||
|
let opt_comma = ws(opt(',')).map(|o| o.is_some());
|
||||||
|
let mut opt_end = ws(opt(one_of(delim))).map(|o| o.is_some());
|
||||||
|
|
||||||
|
let has_end = opt_end.parse_next(i)?;
|
||||||
|
if has_end {
|
||||||
|
return Ok((false, Vec::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let targets = opt(separated(1.., one, ws(',')).map(|v: Vec<_>| v)).parse_next(i)?;
|
||||||
|
let Some(targets) = targets else {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"expected comma separated list of members",
|
||||||
|
*i,
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
let (has_comma, has_end) = (opt_comma, opt_end).parse_next(i)?;
|
||||||
|
if !has_end {
|
||||||
|
let msg = match has_comma {
|
||||||
|
true => format!("expected member, or `{delim}` as terminator"),
|
||||||
|
false => format!("expected `,` for more members, or `{delim}` as terminator"),
|
||||||
|
};
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(msg, *i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let singleton = !has_comma && targets.len() == 1;
|
||||||
|
Ok((singleton, targets))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn only_one_rest_pattern<'a>(
|
||||||
|
targets: Vec<Target<'a>>,
|
||||||
|
allow_named_rest: bool,
|
||||||
|
type_kind: &str,
|
||||||
|
) -> Result<Vec<Target<'a>>, ParseErr<'a>> {
|
||||||
|
let mut found_rest = false;
|
||||||
|
|
||||||
|
for target in &targets {
|
||||||
|
if let Target::Rest(s) = target {
|
||||||
|
if !allow_named_rest && s.inner.is_some() {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
"`@ ..` is only allowed in slice patterns",
|
||||||
|
s.span,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if found_rest {
|
||||||
|
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
|
||||||
|
format!("`..` can only be used once per {type_kind} pattern"),
|
||||||
|
s.span,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
found_rest = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(targets)
|
||||||
|
}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
use crate::node::{Lit, Whitespace, Ws};
|
use crate::node::{Lit, Whitespace, Ws};
|
||||||
use crate::{Ast, Expr, Filter, InnerSyntax, Node, Num, StrLit, Syntax, WithSpan};
|
use crate::{
|
||||||
|
Ast, Expr, Filter, InnerSyntax, Node, Num, Span, StrLit, Syntax, SyntaxBuilder, WithSpan,
|
||||||
|
};
|
||||||
|
|
||||||
impl<T> WithSpan<'static, T> {
|
impl<T> WithSpan<'static, T> {
|
||||||
fn no_span(inner: T) -> Self {
|
fn no_span(inner: T) -> Self {
|
||||||
Self { inner, span: "" }
|
Self {
|
||||||
|
inner,
|
||||||
|
span: Span::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +49,8 @@ fn test_parse_filter() {
|
|||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Filter(Filter {
|
WithSpan::no_span(Expr::Filter(Filter {
|
||||||
name: "e",
|
name: "e",
|
||||||
arguments: vec![WithSpan::no_span(Expr::Var("strvar"))]
|
arguments: vec![WithSpan::no_span(Expr::Var("strvar"))],
|
||||||
|
generics: vec![],
|
||||||
})),
|
})),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -54,7 +60,8 @@ fn test_parse_filter() {
|
|||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Filter(Filter {
|
WithSpan::no_span(Expr::Filter(Filter {
|
||||||
name: "abs",
|
name: "abs",
|
||||||
arguments: vec![WithSpan::no_span(int_lit("2"))]
|
arguments: vec![WithSpan::no_span(int_lit("2"))],
|
||||||
|
generics: vec![],
|
||||||
})),
|
})),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -67,7 +74,8 @@ fn test_parse_filter() {
|
|||||||
arguments: vec![WithSpan::no_span(Expr::Unary(
|
arguments: vec![WithSpan::no_span(Expr::Unary(
|
||||||
"-",
|
"-",
|
||||||
WithSpan::no_span(int_lit("2")).into()
|
WithSpan::no_span(int_lit("2")).into()
|
||||||
))]
|
))],
|
||||||
|
generics: vec![],
|
||||||
})),
|
})),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -87,6 +95,7 @@ fn test_parse_filter() {
|
|||||||
))
|
))
|
||||||
.into()
|
.into()
|
||||||
))],
|
))],
|
||||||
|
generics: vec![],
|
||||||
})),
|
})),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -112,9 +121,13 @@ fn test_parse_numbers() {
|
|||||||
fn test_parse_var() {
|
fn test_parse_var() {
|
||||||
let s = Syntax::default();
|
let s = Syntax::default();
|
||||||
|
|
||||||
assert_eq!(Ast::from_str("{{ foo }}", None, &s).unwrap().nodes, vec![
|
assert_eq!(
|
||||||
Node::Expr(Ws(None, None), WithSpan::no_span(Expr::Var("foo")))
|
Ast::from_str("{{ foo }}", None, &s).unwrap().nodes,
|
||||||
],);
|
vec![Node::Expr(
|
||||||
|
Ws(None, None),
|
||||||
|
WithSpan::no_span(Expr::Var("foo"))
|
||||||
|
)]
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ast::from_str("{{ foo_bar }}", None, &s).unwrap().nodes,
|
Ast::from_str("{{ foo_bar }}", None, &s).unwrap().nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
@@ -123,18 +136,26 @@ fn test_parse_var() {
|
|||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(Ast::from_str("{{ none }}", None, &s).unwrap().nodes, vec![
|
assert_eq!(
|
||||||
Node::Expr(Ws(None, None), WithSpan::no_span(Expr::Var("none")))
|
Ast::from_str("{{ none }}", None, &s).unwrap().nodes,
|
||||||
],);
|
vec![Node::Expr(
|
||||||
|
Ws(None, None),
|
||||||
|
WithSpan::no_span(Expr::Var("none"))
|
||||||
|
)]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_const() {
|
fn test_parse_const() {
|
||||||
let s = Syntax::default();
|
let s = Syntax::default();
|
||||||
|
|
||||||
assert_eq!(Ast::from_str("{{ FOO }}", None, &s).unwrap().nodes, vec![
|
assert_eq!(
|
||||||
Node::Expr(Ws(None, None), WithSpan::no_span(Expr::Path(vec!["FOO"])))
|
Ast::from_str("{{ FOO }}", None, &s).unwrap().nodes,
|
||||||
],);
|
vec![Node::Expr(
|
||||||
|
Ws(None, None),
|
||||||
|
WithSpan::no_span(Expr::Path(vec!["FOO"]))
|
||||||
|
)]
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ast::from_str("{{ FOO_BAR }}", None, &s).unwrap().nodes,
|
Ast::from_str("{{ FOO_BAR }}", None, &s).unwrap().nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
@@ -143,26 +164,35 @@ fn test_parse_const() {
|
|||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(Ast::from_str("{{ NONE }}", None, &s).unwrap().nodes, vec![
|
assert_eq!(
|
||||||
Node::Expr(Ws(None, None), WithSpan::no_span(Expr::Path(vec!["NONE"])))
|
Ast::from_str("{{ NONE }}", None, &s).unwrap().nodes,
|
||||||
],);
|
vec![Node::Expr(
|
||||||
|
Ws(None, None),
|
||||||
|
WithSpan::no_span(Expr::Path(vec!["NONE"]))
|
||||||
|
)]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_path() {
|
fn test_parse_path() {
|
||||||
let s = Syntax::default();
|
let s = Syntax::default();
|
||||||
|
|
||||||
assert_eq!(Ast::from_str("{{ None }}", None, &s).unwrap().nodes, vec![
|
assert_eq!(
|
||||||
Node::Expr(Ws(None, None), WithSpan::no_span(Expr::Path(vec!["None"])))
|
Ast::from_str("{{ None }}", None, &s).unwrap().nodes,
|
||||||
],);
|
vec![Node::Expr(
|
||||||
|
Ws(None, None),
|
||||||
|
WithSpan::no_span(Expr::Path(vec!["None"]))
|
||||||
|
)]
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ast::from_str("{{ Some(123) }}", None, &s).unwrap().nodes,
|
Ast::from_str("{{ Some(123) }}", None, &s).unwrap().nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Path(vec!["Some"]))),
|
path: Box::new(WithSpan::no_span(Expr::Path(vec!["Some"]))),
|
||||||
vec![WithSpan::no_span(int_lit("123"))]
|
args: vec![WithSpan::no_span(int_lit("123"))],
|
||||||
)),
|
generics: vec![],
|
||||||
|
}),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -170,20 +200,22 @@ fn test_parse_path() {
|
|||||||
Ast::from_str("{{ Ok(123) }}", None, &s).unwrap().nodes,
|
Ast::from_str("{{ Ok(123) }}", None, &s).unwrap().nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Path(vec!["Ok"]))),
|
path: Box::new(WithSpan::no_span(Expr::Path(vec!["Ok"]))),
|
||||||
vec![WithSpan::no_span(int_lit("123"))]
|
args: vec![WithSpan::no_span(int_lit("123"))],
|
||||||
)),
|
generics: vec![],
|
||||||
|
}),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ast::from_str("{{ Err(123) }}", None, &s).unwrap().nodes,
|
Ast::from_str("{{ Err(123) }}", None, &s).unwrap().nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Path(vec!["Err"]))),
|
path: Box::new(WithSpan::no_span(Expr::Path(vec!["Err"]))),
|
||||||
vec![WithSpan::no_span(int_lit("123"))]
|
args: vec![WithSpan::no_span(int_lit("123"))],
|
||||||
)),
|
generics: vec![],
|
||||||
|
}),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -196,16 +228,17 @@ fn test_parse_var_call() {
|
|||||||
.nodes,
|
.nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Var("function"))),
|
path: Box::new(WithSpan::no_span(Expr::Var("function"))),
|
||||||
vec![
|
args: vec![
|
||||||
WithSpan::no_span(Expr::StrLit(StrLit {
|
WithSpan::no_span(Expr::StrLit(StrLit {
|
||||||
content: "123",
|
content: "123",
|
||||||
prefix: None,
|
prefix: None,
|
||||||
})),
|
})),
|
||||||
WithSpan::no_span(int_lit("3"))
|
WithSpan::no_span(int_lit("3"))
|
||||||
]
|
],
|
||||||
)),
|
generics: vec![],
|
||||||
|
}),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -227,10 +260,11 @@ fn test_parse_path_call() {
|
|||||||
.nodes,
|
.nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Path(vec!["Option", "Some"]))),
|
path: Box::new(WithSpan::no_span(Expr::Path(vec!["Option", "Some"]))),
|
||||||
vec![WithSpan::no_span(int_lit("123"))],
|
args: vec![WithSpan::no_span(int_lit("123"))],
|
||||||
),)
|
generics: vec![],
|
||||||
|
})
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -240,16 +274,17 @@ fn test_parse_path_call() {
|
|||||||
.nodes,
|
.nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Path(vec!["self", "function"]))),
|
path: Box::new(WithSpan::no_span(Expr::Path(vec!["self", "function"]))),
|
||||||
vec![
|
args: vec![
|
||||||
WithSpan::no_span(Expr::StrLit(StrLit {
|
WithSpan::no_span(Expr::StrLit(StrLit {
|
||||||
content: "123",
|
content: "123",
|
||||||
prefix: None,
|
prefix: None,
|
||||||
})),
|
})),
|
||||||
WithSpan::no_span(int_lit("3"))
|
WithSpan::no_span(int_lit("3"))
|
||||||
],
|
],
|
||||||
),)
|
generics: vec![],
|
||||||
|
})
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -263,12 +298,13 @@ fn test_parse_root_path() {
|
|||||||
.nodes,
|
.nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Path(vec![
|
path: Box::new(WithSpan::no_span(Expr::Path(vec![
|
||||||
"std", "string", "String", "new"
|
"std", "string", "String", "new"
|
||||||
]))),
|
]))),
|
||||||
vec![]
|
args: vec![],
|
||||||
)),
|
generics: vec![],
|
||||||
|
}),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -277,12 +313,13 @@ fn test_parse_root_path() {
|
|||||||
.nodes,
|
.nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Path(vec![
|
path: Box::new(WithSpan::no_span(Expr::Path(vec![
|
||||||
"", "std", "string", "String", "new"
|
"", "std", "string", "String", "new"
|
||||||
]))),
|
]))),
|
||||||
vec![]
|
args: vec![],
|
||||||
)),
|
generics: vec![],
|
||||||
|
}),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -308,36 +345,41 @@ fn test_rust_macro() {
|
|||||||
WithSpan::no_span(Expr::RustMacro(vec!["alloc", "vec"], "1, 2, 3")),
|
WithSpan::no_span(Expr::RustMacro(vec!["alloc", "vec"], "1, 2, 3")),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(Ast::from_str("{{a!()}}", None, &syntax).unwrap().nodes, [
|
assert_eq!(
|
||||||
Node::Expr(
|
Ast::from_str("{{a!()}}", None, &syntax).unwrap().nodes,
|
||||||
|
[Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::RustMacro(vec!["a"], ""))
|
WithSpan::no_span(Expr::RustMacro(vec!["a"], ""))
|
||||||
)
|
)]
|
||||||
],);
|
);
|
||||||
assert_eq!(Ast::from_str("{{a !()}}", None, &syntax).unwrap().nodes, [
|
assert_eq!(
|
||||||
Node::Expr(
|
Ast::from_str("{{a !()}}", None, &syntax).unwrap().nodes,
|
||||||
|
[Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::RustMacro(vec!["a"], ""))
|
WithSpan::no_span(Expr::RustMacro(vec!["a"], ""))
|
||||||
)
|
)]
|
||||||
],);
|
);
|
||||||
assert_eq!(Ast::from_str("{{a! ()}}", None, &syntax).unwrap().nodes, [
|
assert_eq!(
|
||||||
Node::Expr(
|
Ast::from_str("{{a! ()}}", None, &syntax).unwrap().nodes,
|
||||||
|
[Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::RustMacro(vec!["a"], ""))
|
WithSpan::no_span(Expr::RustMacro(vec!["a"], ""))
|
||||||
)
|
)]
|
||||||
],);
|
);
|
||||||
assert_eq!(Ast::from_str("{{a ! ()}}", None, &syntax).unwrap().nodes, [
|
assert_eq!(
|
||||||
Node::Expr(
|
Ast::from_str("{{a ! ()}}", None, &syntax).unwrap().nodes,
|
||||||
|
[Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::RustMacro(vec!["a"], ""))
|
WithSpan::no_span(Expr::RustMacro(vec!["a"], ""))
|
||||||
)
|
)]
|
||||||
],);
|
);
|
||||||
assert_eq!(Ast::from_str("{{A!()}}", None, &syntax).unwrap().nodes, [
|
assert_eq!(
|
||||||
Node::Expr(
|
Ast::from_str("{{A!()}}", None, &syntax).unwrap().nodes,
|
||||||
|
[Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::RustMacro(vec!["A"], ""))
|
WithSpan::no_span(Expr::RustMacro(vec!["A"], ""))
|
||||||
)
|
)]
|
||||||
],);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&*Ast::from_str("{{a.b.c!( hello )}}", None, &syntax)
|
&*Ast::from_str("{{a.b.c!( hello )}}", None, &syntax)
|
||||||
.unwrap_err()
|
.unwrap_err()
|
||||||
@@ -373,7 +415,7 @@ fn unicode_delimiters_in_syntax() {
|
|||||||
val: "Here comes the expression:",
|
val: "Here comes the expression:",
|
||||||
rws: " ",
|
rws: " ",
|
||||||
})),
|
})),
|
||||||
Node::Expr(Ws(None, None), WithSpan::no_span(Expr::Var("e")),),
|
Node::Expr(Ws(None, None), WithSpan::no_span(Expr::Var("e"))),
|
||||||
Node::Lit(WithSpan::no_span(Lit {
|
Node::Lit(WithSpan::no_span(Lit {
|
||||||
lws: "",
|
lws: "",
|
||||||
val: ".",
|
val: ".",
|
||||||
@@ -401,7 +443,7 @@ fn test_precedence() {
|
|||||||
))
|
))
|
||||||
.into(),
|
.into(),
|
||||||
WithSpan::no_span(Expr::Var("c")).into()
|
WithSpan::no_span(Expr::Var("c")).into()
|
||||||
),)
|
))
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -598,13 +640,14 @@ fn test_odd_calls() {
|
|||||||
Ast::from_str("{{ a[b](c) }}", None, &syntax).unwrap().nodes,
|
Ast::from_str("{{ a[b](c) }}", None, &syntax).unwrap().nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Index(
|
path: Box::new(WithSpan::no_span(Expr::Index(
|
||||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||||
Box::new(WithSpan::no_span(Expr::Var("b")))
|
Box::new(WithSpan::no_span(Expr::Var("b")))
|
||||||
))),
|
))),
|
||||||
vec![WithSpan::no_span(Expr::Var("c"))],
|
args: vec![WithSpan::no_span(Expr::Var("c"))],
|
||||||
),)
|
generics: vec![],
|
||||||
|
})
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -613,16 +656,17 @@ fn test_odd_calls() {
|
|||||||
.nodes,
|
.nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(
|
path: Box::new(WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(
|
||||||
Expr::BinOp(
|
Expr::BinOp(
|
||||||
"+",
|
"+",
|
||||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||||
Box::new(WithSpan::no_span(Expr::Var("b")))
|
Box::new(WithSpan::no_span(Expr::Var("b")))
|
||||||
)
|
)
|
||||||
))))),
|
))))),
|
||||||
vec![WithSpan::no_span(Expr::Var("c"))],
|
args: vec![WithSpan::no_span(Expr::Var("c"))],
|
||||||
),)
|
generics: vec![],
|
||||||
|
})
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -634,10 +678,11 @@ fn test_odd_calls() {
|
|||||||
WithSpan::no_span(Expr::BinOp(
|
WithSpan::no_span(Expr::BinOp(
|
||||||
"+",
|
"+",
|
||||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||||
Box::new(WithSpan::no_span(Expr::Call(
|
Box::new(WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Var("b"))),
|
path: Box::new(WithSpan::no_span(Expr::Var("b"))),
|
||||||
vec![WithSpan::no_span(Expr::Var("c"))]
|
args: vec![WithSpan::no_span(Expr::Var("c"))],
|
||||||
))),
|
generics: vec![],
|
||||||
|
})),
|
||||||
)),
|
)),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -645,12 +690,13 @@ fn test_odd_calls() {
|
|||||||
Ast::from_str("{{ (-a)(b) }}", None, &syntax).unwrap().nodes,
|
Ast::from_str("{{ (-a)(b) }}", None, &syntax).unwrap().nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Call(
|
WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(
|
path: Box::new(WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(
|
||||||
Expr::Unary("-", Box::new(WithSpan::no_span(Expr::Var("a"))))
|
Expr::Unary("-", Box::new(WithSpan::no_span(Expr::Var("a"))))
|
||||||
))))),
|
))))),
|
||||||
vec![WithSpan::no_span(Expr::Var("b"))],
|
args: vec![WithSpan::no_span(Expr::Var("b"))],
|
||||||
),)
|
generics: vec![],
|
||||||
|
})
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -659,11 +705,12 @@ fn test_odd_calls() {
|
|||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Unary(
|
WithSpan::no_span(Expr::Unary(
|
||||||
"-",
|
"-",
|
||||||
Box::new(WithSpan::no_span(Expr::Call(
|
Box::new(WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
path: Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||||
vec![WithSpan::no_span(Expr::Var("b"))]
|
args: vec![WithSpan::no_span(Expr::Var("b"))],
|
||||||
)))
|
generics: vec![],
|
||||||
),)
|
}))
|
||||||
|
))
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -672,11 +719,13 @@ fn test_odd_calls() {
|
|||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Filter(Filter {
|
WithSpan::no_span(Expr::Filter(Filter {
|
||||||
name: "c",
|
name: "c",
|
||||||
arguments: vec![WithSpan::no_span(Expr::Call(
|
arguments: vec![WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
path: Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||||
vec![WithSpan::no_span(Expr::Var("b"))]
|
args: vec![WithSpan::no_span(Expr::Var("b"))],
|
||||||
))]
|
generics: vec![],
|
||||||
}),)
|
})],
|
||||||
|
generics: vec![],
|
||||||
|
}))
|
||||||
)]
|
)]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -685,10 +734,12 @@ fn test_odd_calls() {
|
|||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Filter(Filter {
|
WithSpan::no_span(Expr::Filter(Filter {
|
||||||
name: "c",
|
name: "c",
|
||||||
arguments: vec![WithSpan::no_span(Expr::Call(
|
arguments: vec![WithSpan::no_span(Expr::Call {
|
||||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
path: Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||||
vec![WithSpan::no_span(Expr::Var("b"))]
|
args: vec![WithSpan::no_span(Expr::Var("b"))],
|
||||||
))]
|
generics: vec![],
|
||||||
|
})],
|
||||||
|
generics: vec![],
|
||||||
})),
|
})),
|
||||||
)]
|
)]
|
||||||
);
|
);
|
||||||
@@ -761,14 +812,14 @@ fn test_parse_tuple() {
|
|||||||
Ast::from_str("{{ () }}", None, &syntax).unwrap().nodes,
|
Ast::from_str("{{ () }}", None, &syntax).unwrap().nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Tuple(vec![]),)
|
WithSpan::no_span(Expr::Tuple(vec![]))
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ast::from_str("{{ (1) }}", None, &syntax).unwrap().nodes,
|
Ast::from_str("{{ (1) }}", None, &syntax).unwrap().nodes,
|
||||||
vec![Node::Expr(
|
vec![Node::Expr(
|
||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(int_lit("1"))),))
|
WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(int_lit("1")))))
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -838,7 +889,8 @@ fn test_parse_tuple() {
|
|||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Filter(Filter {
|
WithSpan::no_span(Expr::Filter(Filter {
|
||||||
name: "abs",
|
name: "abs",
|
||||||
arguments: vec![WithSpan::no_span(Expr::Tuple(vec![]))]
|
arguments: vec![WithSpan::no_span(Expr::Tuple(vec![]))],
|
||||||
|
generics: vec![],
|
||||||
})),
|
})),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -850,7 +902,8 @@ fn test_parse_tuple() {
|
|||||||
name: "abs",
|
name: "abs",
|
||||||
arguments: vec![WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(
|
arguments: vec![WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(
|
||||||
int_lit("1")
|
int_lit("1")
|
||||||
))))]
|
))))],
|
||||||
|
generics: vec![],
|
||||||
})),
|
})),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -864,7 +917,8 @@ fn test_parse_tuple() {
|
|||||||
name: "abs",
|
name: "abs",
|
||||||
arguments: vec![WithSpan::no_span(Expr::Tuple(vec![WithSpan::no_span(
|
arguments: vec![WithSpan::no_span(Expr::Tuple(vec![WithSpan::no_span(
|
||||||
int_lit("1")
|
int_lit("1")
|
||||||
)]))]
|
)]))],
|
||||||
|
generics: vec![],
|
||||||
})),
|
})),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -879,7 +933,8 @@ fn test_parse_tuple() {
|
|||||||
arguments: vec![WithSpan::no_span(Expr::Tuple(vec![
|
arguments: vec![WithSpan::no_span(Expr::Tuple(vec![
|
||||||
WithSpan::no_span(int_lit("1")),
|
WithSpan::no_span(int_lit("1")),
|
||||||
WithSpan::no_span(int_lit("2"))
|
WithSpan::no_span(int_lit("2"))
|
||||||
]))]
|
]))],
|
||||||
|
generics: vec![],
|
||||||
})),
|
})),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -972,7 +1027,8 @@ fn test_parse_array() {
|
|||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Filter(Filter {
|
WithSpan::no_span(Expr::Filter(Filter {
|
||||||
name: "foo",
|
name: "foo",
|
||||||
arguments: vec![WithSpan::no_span(Expr::Array(vec![]))]
|
arguments: vec![WithSpan::no_span(Expr::Array(vec![]))],
|
||||||
|
generics: vec![],
|
||||||
}))
|
}))
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -982,7 +1038,8 @@ fn test_parse_array() {
|
|||||||
Ws(None, None),
|
Ws(None, None),
|
||||||
WithSpan::no_span(Expr::Filter(Filter {
|
WithSpan::no_span(Expr::Filter(Filter {
|
||||||
name: "foo",
|
name: "foo",
|
||||||
arguments: vec![WithSpan::no_span(Expr::Array(vec![]))]
|
arguments: vec![WithSpan::no_span(Expr::Array(vec![]))],
|
||||||
|
generics: vec![],
|
||||||
}))
|
}))
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
@@ -1064,3 +1121,224 @@ fn fuzzed_filter_recursion() {
|
|||||||
const TEMPLATE: &str = include_str!("../tests/filter-recursion.txt");
|
const TEMPLATE: &str = include_str!("../tests/filter-recursion.txt");
|
||||||
assert!(Ast::from_str(TEMPLATE, None, &Syntax::default()).is_err());
|
assert!(Ast::from_str(TEMPLATE, None, &Syntax::default()).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fuzzed_excessive_syntax_lengths() {
|
||||||
|
const LONG_DELIM: Option<&str> =
|
||||||
|
Some("\0]***NEWFILE\u{1f}***:7/v/.-3/\u{1b}/~~~~z~0/*:7/v/./t/t/.p//NEWVILE**::7/v");
|
||||||
|
|
||||||
|
for (kind, syntax_builder) in [
|
||||||
|
(
|
||||||
|
"opening block",
|
||||||
|
SyntaxBuilder {
|
||||||
|
block_start: LONG_DELIM,
|
||||||
|
..SyntaxBuilder::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"closing block",
|
||||||
|
SyntaxBuilder {
|
||||||
|
block_end: LONG_DELIM,
|
||||||
|
..SyntaxBuilder::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"opening expression",
|
||||||
|
SyntaxBuilder {
|
||||||
|
expr_start: LONG_DELIM,
|
||||||
|
..SyntaxBuilder::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"closing expression",
|
||||||
|
SyntaxBuilder {
|
||||||
|
expr_end: LONG_DELIM,
|
||||||
|
..SyntaxBuilder::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"opening comment",
|
||||||
|
SyntaxBuilder {
|
||||||
|
comment_start: LONG_DELIM,
|
||||||
|
..SyntaxBuilder::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"closing comment",
|
||||||
|
SyntaxBuilder {
|
||||||
|
comment_end: LONG_DELIM,
|
||||||
|
..SyntaxBuilder::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let err = syntax_builder.to_syntax().unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
err,
|
||||||
|
format!(
|
||||||
|
"delimiters must be at most 32 characters long. The {} delimiter \
|
||||||
|
(\"\\0]***NEWFILE\\u{{1f}}***\"...) is too long",
|
||||||
|
kind
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extends_with_whitespace_control() {
|
||||||
|
const CONTROL: &[&str] = &["", "\t", "-", "+", "~"];
|
||||||
|
|
||||||
|
let syntax = Syntax::default();
|
||||||
|
let expected = Ast::from_str(r#"front {% extends "nothing" %} back"#, None, &syntax).unwrap();
|
||||||
|
for front in CONTROL {
|
||||||
|
for back in CONTROL {
|
||||||
|
let src = format!(r#"front {{%{front} extends "nothing" {back}%}} back"#);
|
||||||
|
let actual = Ast::from_str(&src, None, &syntax).unwrap();
|
||||||
|
assert_eq!(expected.nodes(), actual.nodes(), "source: {:?}", src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fuzzed_span_is_not_substring_of_source() {
|
||||||
|
let _: Result<Ast<'_>, crate::ParseError> = Ast::from_str(
|
||||||
|
include_str!("../tests/fuzzed_span_is_not_substring_of_source.bin"),
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fuzzed_excessive_filter_block() {
|
||||||
|
let src = include_str!("../tests/excessive_filter_block.txt");
|
||||||
|
let err = Ast::from_str(src, None, &Syntax::default()).unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
err.to_string().lines().next(),
|
||||||
|
Some("your template code is too deeply nested, or the last expression is too complex"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let src = include!("../tests/fuzzed_excessive_filter_block.inc");
|
||||||
|
let err = Ast::from_str(src, None, &Syntax::default()).unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
err.to_string().lines().next(),
|
||||||
|
Some("your template code is too deeply nested, or the last expression is too complex"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generics_parsing() {
|
||||||
|
// Method call.
|
||||||
|
Ast::from_str("{{ a.b::<&str, H<B<C>>>() }}", None, &Syntax::default()).unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&str, H<B<C> , &u32>>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Call.
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a::<&str, H<B<C> , &u32>>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Filter.
|
||||||
|
Ast::from_str("{{ 12 | a::<&str> }}", None, &Syntax::default()).unwrap();
|
||||||
|
Ast::from_str("{{ 12 | a::<&str, u32>('a') }}", None, &Syntax::default()).unwrap();
|
||||||
|
|
||||||
|
// Unclosed `<`.
|
||||||
|
assert!(
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&str, H<B<C> , &u32>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default()
|
||||||
|
)
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
|
||||||
|
// With path and spaces
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&&core::primitive::str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b ::<&&core::primitive::str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b:: <&&core::primitive::str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::< &&core::primitive::str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<& &core::primitive::str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&& core::primitive::str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&&core ::primitive::str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&&core:: primitive::str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&&core::primitive ::str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&&core::primitive:: str>() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&&core::primitive::str >() }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ast::from_str(
|
||||||
|
"{{ a.b::<&&core::primitive::str> () }}",
|
||||||
|
None,
|
||||||
|
&Syntax::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fuzzed_deeply_tested_if_let() {
|
||||||
|
let src = include_str!("../tests/fuzzed-deeply-tested-if-let.txt");
|
||||||
|
let syntax = Syntax::default();
|
||||||
|
let err = Ast::from_str(src, None, &syntax).unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
err.to_string().lines().next(),
|
||||||
|
Some("your template code is too deeply nested, or the last expression is too complex"),
|
||||||
|
);
|
||||||
|
}
|
||||||
50
third_party/rust/askama_parser/tests/excessive_filter_block.txt
vendored
Normal file
50
third_party/rust/askama_parser/tests/excessive_filter_block.txt
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{% filter x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
|
||||||
|
x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x %} {% endfilter %}
|
||||||
9731
third_party/rust/askama_parser/tests/fuzzed-deeply-tested-if-let.txt
vendored
Normal file
9731
third_party/rust/askama_parser/tests/fuzzed-deeply-tested-if-let.txt
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
third_party/rust/askama_parser/tests/fuzzed_excessive_filter_block.inc
vendored
Normal file
1
third_party/rust/askama_parser/tests/fuzzed_excessive_filter_block.inc
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
third_party/rust/askama_parser/tests/fuzzed_span_is_not_substring_of_source.bin
vendored
Normal file
BIN
third_party/rust/askama_parser/tests/fuzzed_span_is_not_substring_of_source.bin
vendored
Normal file
Binary file not shown.
23
third_party/rust/askama_parser/tomlfmt.toml
vendored
Normal file
23
third_party/rust/askama_parser/tomlfmt.toml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Keep in the same order as <https://doc.rust-lang.org/cargo/reference/manifest.html>
|
||||||
|
table_order = [
|
||||||
|
"package",
|
||||||
|
# targets
|
||||||
|
"lib",
|
||||||
|
"bin",
|
||||||
|
"example",
|
||||||
|
"test",
|
||||||
|
"bench",
|
||||||
|
# dependencies
|
||||||
|
"dependencies",
|
||||||
|
"dev-dependencies",
|
||||||
|
"build-dependencies",
|
||||||
|
"target",
|
||||||
|
# misc
|
||||||
|
"badges",
|
||||||
|
"features",
|
||||||
|
"lints",
|
||||||
|
"patch",
|
||||||
|
"replace",
|
||||||
|
"profile",
|
||||||
|
"workspace",
|
||||||
|
]
|
||||||
@@ -1 +1 @@
|
|||||||
{"files":{"CHANGELOG.md":"8b3e29799cdedf02f169bb519072ace2e2b6b9413f4ce8fa0666c2d1d964084e","Cargo.toml":"57d432cd172cc87ee4c31b0e4c21c52d06ba1a48da9decd34581b2671c47d71d","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"d51a5b3347bed2441b20986be81bfd4611ca2c5614f950116b273199a9bcf2de","src/dependency.rs":"c593ddc73d863c5712e2aba58b5f4d9bd915a5ac0bc17df71642aa79aa93bfdc","src/diagnostic.rs":"fee47d27390f1026ff99ffade5dfd2ab3e9b9839c3f33ce91a7dcde875551374","src/errors.rs":"797afd61efdd843ae570d9e972dd2425d33823d4a78c0c488028493dffb45c7a","src/lib.rs":"5ec701f3589c5d71c152b5abe7ad5f222aee4d4a5f9992bced1d357bad36e227","src/messages.rs":"a8e3ee31dc8cce5762b4b085be29fe4d7189a789f3a149ef2b6c17604d94528b","tests/selftest.rs":"73afd494c1bf7dd4e1a99971e9ff66a0e21fc7bf3e327663df15d2350dcdfc70","tests/test_samples.rs":"ee2b4737adfa1930c1610bb3ec0fc94b7f1a3691bb09545da69044eef2f5ba6b"},"package":"08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07"}
|
{"files":{"CHANGELOG.md":"1d3bec96cd4145a0128d3c055785f77470505ab6451d05f5559c6644c9cdbf5a","Cargo.lock":"6321366c2fb28190a03c72bd4a85c8530d3669ee9eb3f12ead8df09823b7f768","Cargo.toml":"69b8d90a5855062a89a81138122eca95bb0ba67280a3eccf17e5972f89921727","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"6be7aaba23d8646b470a966eef2f00c9fa04cf4d1de25cfb71680cef562e1574","clippy.toml":"0eb773e1a242ba7e06f0ca5c23be71b5989f4952fe18cae35e5322066e706495","src/dependency.rs":"00088f0d6d5233affdcd0b573359aeda2ef5e0550420b848f4844a14d9b7ace4","src/diagnostic.rs":"fee47d27390f1026ff99ffade5dfd2ab3e9b9839c3f33ce91a7dcde875551374","src/errors.rs":"797afd61efdd843ae570d9e972dd2425d33823d4a78c0c488028493dffb45c7a","src/lib.rs":"45dff3eb98f4a1f4d9175bce516011cef11a6b55bbcc2b469b505a35c6567d00","src/libtest.rs":"705519c92f6f1ad1594908b9024d6311777a3c20e23e74a3424e4577850f374c","src/messages.rs":"da21b25d289f86f29c55501adc3c9349331df5bd9e9859935caccd50d6a6d305","tests/selftest.rs":"ea54f90b56d14fb0ce852da1c4ced9709c8ea80f183ef63a58419ea96fbc35f9","tests/test_samples.rs":"b6cd4db25d133faf9f80bc00ddf89c22cdc4cf4adacbcd2f1a46032d1f4236bc"},"package":"dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"}
|
||||||
38
third_party/rust/cargo_metadata/CHANGELOG.md
vendored
38
third_party/rust/cargo_metadata/CHANGELOG.md
vendored
@@ -2,17 +2,47 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- n/a
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Re-exported `semver` crate directly.
|
- n/a
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Updated dependencies:
|
||||||
|
- `thiserror` from `1.0.31` to `2.0.3`
|
||||||
|
- `derive_builder` from `0.12` to `0.20`
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- n/a
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- n/a
|
||||||
|
|
||||||
|
## [0.19.0] - 2024-11-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Re-exported `semver` crate directly.
|
||||||
|
- Added implementation of `std::ops::Index<&PackageId>` for `Resolve`.
|
||||||
|
- Added `pub fn is_kind(&self, name: TargetKind) -> bool` to `Target`.
|
||||||
|
- Added derived implementations of `PartialEq`, `Eq` and `Hash` for `Metadata` and its members' types.
|
||||||
|
- Added default fields to `PackageBuilder`.
|
||||||
|
- Added `pub fn new(name:version:id:path:) -> Self` to `PackageBuilder` for providing all required fields upfront.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Bumped MSRV from `1.42.0` to `1.56.0`.
|
||||||
- Made `parse_stream` more versatile by accepting anything that implements `Read`.
|
- Made `parse_stream` more versatile by accepting anything that implements `Read`.
|
||||||
|
- Converted `TargetKind` and `CrateType` to an enum representation.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Removed re-exports for `BuildMetadata` and `Prerelease` from `semver` crate.
|
- Removed re-exports for `BuildMetadata` and `Prerelease` from `semver` crate.
|
||||||
|
- Removed `.is_lib(…)`, `.is_bin(…)`, `.is_example(…)`, `.is_test(…)`, `.is_bench(…)`, `.is_custom_build(…)`, and `.is_proc_macro(…)` from `Target` (in favor of adding `.is_kind(…)`).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@@ -36,3 +66,9 @@
|
|||||||
- Updated `derive_builder` to the latest version.
|
- Updated `derive_builder` to the latest version.
|
||||||
- Made use of `matches!` macros where possible.
|
- Made use of `matches!` macros where possible.
|
||||||
- Fixed some tests
|
- Fixed some tests
|
||||||
|
|
||||||
|
## [0.15.1] - 2022-10-13
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `TestMessage`, `TestEvent`, `SuiteEvent` for parsing the `cargo test -- --format json` output.
|
||||||
|
|||||||
232
third_party/rust/cargo_metadata/Cargo.lock
generated
vendored
Normal file
232
third_party/rust/cargo_metadata/Cargo.lock
generated
vendored
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "camino"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-platform"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo_metadata"
|
||||||
|
version = "0.19.2"
|
||||||
|
dependencies = [
|
||||||
|
"camino",
|
||||||
|
"cargo-platform",
|
||||||
|
"derive_builder",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_builder"
|
||||||
|
version = "0.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||||
|
dependencies = [
|
||||||
|
"derive_builder_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_builder_core"
|
||||||
|
version = "0.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_builder_macro"
|
||||||
|
version = "0.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||||
|
dependencies = [
|
||||||
|
"derive_builder_core",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.218"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.218"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.139"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
|
||||||
41
third_party/rust/cargo_metadata/Cargo.toml
vendored
41
third_party/rust/cargo_metadata/Cargo.toml
vendored
@@ -10,19 +10,42 @@
|
|||||||
# See Cargo.toml.orig for the original contents.
|
# See Cargo.toml.orig for the original contents.
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
rust-version = "1.42.0"
|
rust-version = "1.78.0"
|
||||||
name = "cargo_metadata"
|
name = "cargo_metadata"
|
||||||
version = "0.15.3"
|
version = "0.19.2"
|
||||||
authors = ["Oliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>"]
|
authors = ["Oliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>"]
|
||||||
|
build = false
|
||||||
|
autolib = false
|
||||||
|
autobins = false
|
||||||
|
autoexamples = false
|
||||||
|
autotests = false
|
||||||
|
autobenches = false
|
||||||
description = "structured access to the output of `cargo metadata`"
|
description = "structured access to the output of `cargo metadata`"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/oli-obk/cargo_metadata"
|
repository = "https://github.com/oli-obk/cargo_metadata"
|
||||||
|
|
||||||
[package.metadata.cargo_metadata_test]
|
[package.metadata.cargo_metadata_test]
|
||||||
some_field = true
|
|
||||||
other_field = "foo"
|
other_field = "foo"
|
||||||
|
some_field = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
builder = ["derive_builder"]
|
||||||
|
default = []
|
||||||
|
unstable = []
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "cargo_metadata"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "selftest"
|
||||||
|
path = "tests/selftest.rs"
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "test_samples"
|
||||||
|
path = "tests/test_samples.rs"
|
||||||
|
|
||||||
[dependencies.camino]
|
[dependencies.camino]
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -32,7 +55,7 @@ features = ["serde1"]
|
|||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
|
||||||
[dependencies.derive_builder]
|
[dependencies.derive_builder]
|
||||||
version = "0.11.1"
|
version = "0.20"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.semver]
|
[dependencies.semver]
|
||||||
@@ -44,12 +67,8 @@ version = "1.0.136"
|
|||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
[dependencies.serde_json]
|
[dependencies.serde_json]
|
||||||
version = "1.0.79"
|
version = "1.0.118"
|
||||||
features = ["unbounded_depth"]
|
features = ["unbounded_depth"]
|
||||||
|
|
||||||
[dependencies.thiserror]
|
[dependencies.thiserror]
|
||||||
version = "1.0.31"
|
version = "2.0.3"
|
||||||
|
|
||||||
[features]
|
|
||||||
builder = ["derive_builder"]
|
|
||||||
default = []
|
|
||||||
|
|||||||
2
third_party/rust/cargo_metadata/README.md
vendored
2
third_party/rust/cargo_metadata/README.md
vendored
@@ -6,7 +6,7 @@ Also supports serialization to aid in implementing `--message-format=json`-like
|
|||||||
output generation in `cargo-*` subcommands, since some of the types in what
|
output generation in `cargo-*` subcommands, since some of the types in what
|
||||||
`cargo --message-format=json` emits are exactly the same as the ones from `cargo metadata`.
|
`cargo --message-format=json` emits are exactly the same as the ones from `cargo metadata`.
|
||||||
|
|
||||||
[](https://travis-ci.org/oli-obk/cargo_metadata)
|
[](https://github.com/oli-obk/cargo_metadata/actions/workflows/main.yml?query=branch%3Amain)
|
||||||
[](https://crates.io/crates/cargo_metadata)
|
[](https://crates.io/crates/cargo_metadata)
|
||||||
|
|
||||||
[Documentation](https://docs.rs/cargo_metadata/)
|
[Documentation](https://docs.rs/cargo_metadata/)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user