From 8c93564bdb41ed9b7fb6b6c5f54e63ffdb34b9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mo=C3=AFse=20Valvassori?= Date: Mon, 4 Mar 2024 06:57:19 +0100 Subject: [PATCH] feat: first commit. --- .gitignore | 1 + Cargo.lock | 524 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 24 +++ README.md | 45 +++++ flake.lock | 99 ++++++++++ flake.nix | 107 +++++++++++ rustfmt.toml | 4 + src/lib.rs | 377 ++++++++++++++++++++++++++++++++++++ 8 files changed, 1181 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 rustfmt.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e4b9729 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,524 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "kstring" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +dependencies = [ + "serde", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "liquid" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f68ae1011499ae2ef879f631891f21c78e309755f4a5e483c4a8f12e10b609" +dependencies = [ + "doc-comment", + "liquid-core", + "liquid-derive", + "liquid-lib", + "serde", +] + +[[package]] +name = "liquid-core" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e0724dfcaad5cfb7965ea0f178ca0870b8d7315178f4a7179f5696f7f04d5f" +dependencies = [ + "anymap2", + "itertools", + "kstring", + "liquid-derive", + "num-traits", + "pest", + "pest_derive", + "regex", + "serde", + "time", +] + +[[package]] +name = "liquid-derive" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2fb41a9bb4257a3803154bdf7e2df7d45197d1941c9b1a90ad815231630721" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "liquid-heck" +version = "0.1.0" +dependencies = [ + "heck", + "liquid", + "liquid-core", + "liquid-derive", + "pretty_assertions", + "tracing", +] + +[[package]] +name = "liquid-lib" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2a17e273a6fb1fb6268f7a5867ddfd0bd4683c7e19b51084f3d567fad4348c0" +dependencies = [ + "itertools", + "liquid-core", + "once_cell", + "percent-encoding", + "regex", + "time", + "unicode-segmentation", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bd94fe5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[package] +edition = "2021" +name = "liquid-heck" +version = "0.1.0" +keywords = ["liquid", "heck", "filter", "camel", "snake"] +authors= ["Valvassori Moïse "] +description = "Case conversion (CamelCase, snake_case, ...) filters for Liquid using Heck library." +homepage = "https://github.com/djedi23/liquid-heck" +repository = "https://github.com/djedi23/liquid-heck" +license = "MIT" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +heck = "0.4" +liquid-core = "0.26" +liquid-derive = "0.26" +tracing = { version = "0.1", optional = true } + +[dev-dependencies] +pretty_assertions = "1.4" +liquid = "0.26" + +[features] +tracing = ["dep:tracing"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..29bc64f --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# liquid-heck + +Case conversion (CamelCase, snake_case, ...) filters for +[Liquid](https://crates.io/crates/liquid) using +[Heck](https://crates.io/crates/heck) library. + + +This library provides case conversion filters for the Liquid template +engine, utilizing the Heck library for case conversion. + + +| ... in `filter()` | ... in templates | +|-------------------|------------------| +| KebabCase | kebabcase | +| LowerCamelCase | lowercamelcase | +| ShoutyKebabCase | shoutykebabcase | +| ShoutySnakeCase | shoutysnakecase | +| SnakeCase | snakecase | +| TitleCase | titlecase | +| TrainCase | traincase | +| UpperCamelCase | uppercamelcase | + +## Example + +To use the case conversion filters in your Liquid templates, you first +need to register the filters. + + +```rust +use liquid_heck::{UpperCamelCase,SnakeCase,TrainCase}; + +let template = liquid::ParserBuilder::with_stdlib() + .filter(UpperCamelCase) // Register the filters + .filter(SnakeCase) + .filter(TrainCase) + .build().unwrap() + .parse("{{text | uppercamelcase}} {{text | snakecase}} {{text | traincase}}").unwrap(); + +let mut globals = liquid::object!({ + "text": "Some text to convert" +}); + +let output = template.render(&globals).unwrap(); +assert_eq!(output, "SomeTextToConvert some_text_to_convert Some-Text-To-Convert".to_string()); +``` diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..78302c1 --- /dev/null +++ b/flake.lock @@ -0,0 +1,99 @@ +{ + "nodes": { + "advisory-db": { + "flake": false, + "locked": { + "lastModified": 1708645386, + "narHash": "sha256-OdK/fVWOpbMBsl37pSWawPqpk5sePqtu1lx1UM+7c9Q=", + "owner": "rustsec", + "repo": "advisory-db", + "rev": "feb54ac57e980ef6578f66b307cfb844869e5260", + "type": "github" + }, + "original": { + "owner": "rustsec", + "repo": "advisory-db", + "type": "github" + } + }, + "crane": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1708560786, + "narHash": "sha256-gcTA/iq9mfrwGPQsoxVryWhCAgBwL2GJLGO/s06/0wY=", + "owner": "ipetkov", + "repo": "crane", + "rev": "9a5972e2e8d0b1716cc4e42af8b75eca6914fbff", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1708692673, + "narHash": "sha256-qIQMXkkp3/Lo2Zu41BK/oN3Dt3b5rUJELvt+CbAXPXw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "48b75eb6e521f2303cb3cd53a94ec80021b422aa", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "advisory-db": "advisory-db", + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..6ee2aab --- /dev/null +++ b/flake.nix @@ -0,0 +1,107 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + crane = { + url = "github:ipetkov/crane"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + flake-utils.url = "github:numtide/flake-utils"; + + advisory-db = { + url = "github:rustsec/advisory-db"; + flake = false; + }; + }; + + outputs = { self, nixpkgs, crane, flake-utils, advisory-db, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + + inherit (pkgs) lib; + + craneLib = crane.lib.${system}; + src = craneLib.cleanCargoSource (craneLib.path ./.); + + # Common arguments can be set here to avoid repeating them later + commonArgs = { + inherit src; + strictDeps = true; + doCheck = false; + + nativeBuildInputs = with pkgs; [ installShellFiles gzip ]; + buildInputs = [ + # Add additional build inputs here + ] ++ lib.optionals pkgs.stdenv.isDarwin [ + # Additional darwin specific inputs can be set here + pkgs.libiconv + ]; + }; + # Build *just* the cargo dependencies, so we can reuse + # all of that work (e.g. via cachix) when running in CI + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + + # Build the actual crate itself, reusing the dependency + # artifacts from above. + liquid-heck = craneLib.buildPackage (commonArgs // { + inherit cargoArtifacts; + }); + in + { + checks = { + # Build the crate as part of `nix flake check` for convenience + inherit liquid-heck; + + # Run clippy (and deny all warnings) on the crate source, + # again, resuing the dependency artifacts from above. + # + # Note that this is done as a separate derivation so that + # we can block the CI if there are issues here, but not + # prevent downstream consumers from building our crate by itself. + liquid-heck-clippy = craneLib.cargoClippy (commonArgs // { + inherit cargoArtifacts; + cargoClippyExtraArgs = "--all-targets -- --deny warnings"; + }); + + liquid-heck-doc = craneLib.cargoDoc (commonArgs // { + inherit cargoArtifacts; + }); + + # Check formatting + liquid-heck-fmt = craneLib.cargoFmt { + inherit src; + }; + + # Audit dependencies + liquid-heck-audit = craneLib.cargoAudit { + inherit src advisory-db; + }; + + # Run tests with cargo-nextest + # Consider setting `doCheck = false` on `liquid-heck` if you do not want + # the tests to run twice + liquid-heck-nextest = craneLib.cargoNextest (commonArgs // { + inherit cargoArtifacts; + partitions = 1; + partitionType = "count"; + }); + }; + + packages = { + default = liquid-heck; + }; + + devShells.default = craneLib.devShell { + # Inherit inputs from checks. + checks = self.checks.${system}; + + # Extra inputs can be added here; cargo and rustc are provided by default. + packages = with pkgs; [ + bacon + cargo-readme + ]; + }; + }); +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..ba8cb1d --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +tab_spaces=2 +max_width=102 +newline_style="Unix" +edition = "2021" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d40c29b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,377 @@ +//! Case conversion (CamelCase, snake_case, ...) filters for +//! [Liquid](https://crates.io/crates/liquid) using +//! [Heck](https://crates.io/crates/heck) library. +//! +//! +//! This library provides case conversion filters for the Liquid template +//! engine, utilizing the Heck library for case conversion. +//! +//! +//! | ... in `filter()` | ... in templates | +//! |-------------------|------------------| +//! | KebabCase | kebabcase | +//! | LowerCamelCase | lowercamelcase | +//! | ShoutyKebabCase | shoutykebabcase | +//! | ShoutySnakeCase | shoutysnakecase | +//! | SnakeCase | snakecase | +//! | TitleCase | titlecase | +//! | TrainCase | traincase | +//! | UpperCamelCase | uppercamelcase | +//! +//! # Example +//! +//! To use the case conversion filters in your Liquid templates, you first +//! need to register the filters. +//! +//! +//! ``` +//! use liquid_heck::{UpperCamelCase,SnakeCase,TrainCase}; +//! +//! let template = liquid::ParserBuilder::with_stdlib() +//! .filter(UpperCamelCase) // Register the filters +//! .filter(SnakeCase) +//! .filter(TrainCase) +//! .build().unwrap() +//! .parse("{{text | uppercamelcase}} {{text | snakecase}} {{text | traincase}}").unwrap(); +//! +//! let mut globals = liquid::object!({ +//! "text": "Some text to convert" +//! }); +//! +//! let output = template.render(&globals).unwrap(); +//! assert_eq!(output, "SomeTextToConvert some_text_to_convert Some-Text-To-Convert".to_string()); +//! ``` +//! +//! # Feature +//! +//! * **tracing** : instruments all the conversion methods using [tracing](https://crates.io/crates/tracing) +use heck::{ + ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, + ToTrainCase, ToUpperCamelCase, +}; +use liquid_core::{Filter, Result, Runtime, Value, ValueView}; +use liquid_derive::{Display_filter, FilterReflection, ParseFilter}; + +#[derive(Clone, ParseFilter, FilterReflection)] +#[filter( + name = "uppercamelcase", + description = "Convert the string to UpperCamelCase.", + parsed(UpperCamelCaseFilter) +)] +pub struct UpperCamelCase; +#[derive(Debug, Default, Display_filter)] +#[name = "uppercamelcase"] +struct UpperCamelCaseFilter; +impl Filter for UpperCamelCaseFilter { + #[cfg_attr(feature = "tracing", tracing::instrument(skip(_runtime)))] + fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result { + let s = input.to_kstr(); + let s = s.as_str(); + Ok(Value::scalar(s.to_upper_camel_case())) + } +} + +#[derive(Clone, ParseFilter, FilterReflection)] +#[filter( + name = "lowercamelcase", + description = "Convert the string to lowerCamelCase.", + parsed(LowerCamelCaseFilter) +)] +pub struct LowerCamelCase; +#[derive(Debug, Default, Display_filter)] +#[name = "lowercamelcase"] +struct LowerCamelCaseFilter; +impl Filter for LowerCamelCaseFilter { + #[cfg_attr(feature = "tracing", tracing::instrument(skip(_runtime)))] + fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result { + let s = input.to_kstr(); + let s = s.as_str(); + Ok(Value::scalar(s.to_lower_camel_case())) + } +} + +#[derive(Clone, ParseFilter, FilterReflection)] +#[filter( + name = "snakecase", + description = "Convert the string to snake-case.", + parsed(SnakeCaseFilter) +)] +pub struct SnakeCase; +#[derive(Debug, Default, Display_filter)] +#[name = "snakecase"] +struct SnakeCaseFilter; +impl Filter for SnakeCaseFilter { + #[cfg_attr(feature = "tracing", tracing::instrument(skip(_runtime)))] + fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result { + let s = input.to_kstr(); + let s = s.as_str(); + Ok(Value::scalar(s.to_snake_case())) + } +} + +#[derive(Clone, ParseFilter, FilterReflection)] +#[filter( + name = "kebabcase", + description = "Convert the string to kebab-case.", + parsed(KebabCaseFilter) +)] +pub struct KebabCase; +#[derive(Debug, Default, Display_filter)] +#[name = "kebabcase"] +struct KebabCaseFilter; +impl Filter for KebabCaseFilter { + #[cfg_attr(feature = "tracing", tracing::instrument(skip(_runtime)))] + fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result { + let s = input.to_kstr(); + let s = s.as_str(); + Ok(Value::scalar(s.to_kebab_case())) + } +} + +#[derive(Clone, ParseFilter, FilterReflection)] +#[filter( + name = "shoutysnakecase", + description = "Convert the string to SHOUTY_SNAKE_CASE.", + parsed(ShoutySnakeCaseFilter) +)] +pub struct ShoutySnakeCase; +#[derive(Debug, Default, Display_filter)] +#[name = "shoutysnakecase"] +struct ShoutySnakeCaseFilter; +impl Filter for ShoutySnakeCaseFilter { + #[cfg_attr(feature = "tracing", tracing::instrument(skip(_runtime)))] + fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result { + let s = input.to_kstr(); + let s = s.as_str(); + Ok(Value::scalar(s.to_shouty_snake_case())) + } +} + +#[derive(Clone, ParseFilter, FilterReflection)] +#[filter( + name = "titlecase", + description = "Convert the string to title case.", + parsed(TitleCaseFilter) +)] +pub struct TitleCase; +#[derive(Debug, Default, Display_filter)] +#[name = "titlecase"] +struct TitleCaseFilter; +impl Filter for TitleCaseFilter { + #[cfg_attr(feature = "tracing", tracing::instrument(skip(_runtime)))] + fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result { + let s = input.to_kstr(); + let s = s.as_str(); + Ok(Value::scalar(s.to_title_case())) + } +} + +#[derive(Clone, ParseFilter, FilterReflection)] +#[filter( + name = "shoutykebabcase", + description = "Convert the string to SHOUTY-KEBAB-CASE.", + parsed(ShoutyKebabCaseFilter) +)] +pub struct ShoutyKebabCase; +#[derive(Debug, Default, Display_filter)] +#[name = "shoutykebabcase"] +struct ShoutyKebabCaseFilter; +impl Filter for ShoutyKebabCaseFilter { + #[cfg_attr(feature = "tracing", tracing::instrument(skip(_runtime)))] + fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result { + let s = input.to_kstr(); + let s = s.as_str(); + Ok(Value::scalar(s.to_shouty_kebab_case())) + } +} + +#[derive(Clone, ParseFilter, FilterReflection)] +#[filter( + name = "traincase", + description = "Convert the string to Train-Case.", + parsed(TrainCaseFilter) +)] +pub struct TrainCase; + +#[derive(Debug, Default, Display_filter)] +#[name = "traincase"] +struct TrainCaseFilter; + +impl Filter for TrainCaseFilter { + #[cfg_attr(feature = "tracing", tracing::instrument(skip(_runtime)))] + fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result { + let s = input.to_kstr(); + let s = s.as_str(); + Ok(Value::scalar(s.to_train_case())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn upper_camel_case() { + assert_eq!( + liquid_core::call_filter!(UpperCamelCase, "abc").unwrap(), + liquid_core::value!("Abc") + ); + assert_eq!( + liquid_core::call_filter!(UpperCamelCase, "hello world 21").unwrap(), + liquid_core::value!("HelloWorld21") + ); + assert_eq!( + liquid_core::call_filter!(UpperCamelCase, "hello world 21").unwrap(), + liquid_core::value!("HelloWorld21") + ); + assert_eq!( + liquid_core::call_filter!(UpperCamelCase, "hello_world_21").unwrap(), + liquid_core::value!("HelloWorld21") + ); + assert_eq!( + liquid_core::call_filter!(UpperCamelCase, "HelloWorld21").unwrap(), + liquid_core::value!("HelloWorld21") + ); + } + + #[test] + fn lower_camel_case() { + assert_eq!( + liquid_core::call_filter!(LowerCamelCase, "abc").unwrap(), + liquid_core::value!("abc") + ); + assert_eq!( + liquid_core::call_filter!(LowerCamelCase, "hello world 21").unwrap(), + liquid_core::value!("helloWorld21") + ); + assert_eq!( + liquid_core::call_filter!(LowerCamelCase, "hello_world_21").unwrap(), + liquid_core::value!("helloWorld21") + ); + assert_eq!( + liquid_core::call_filter!(LowerCamelCase, "HelloWorld21").unwrap(), + liquid_core::value!("helloWorld21") + ); + } + + #[test] + fn snake_case() { + assert_eq!( + liquid_core::call_filter!(SnakeCase, "abc").unwrap(), + liquid_core::value!("abc") + ); + assert_eq!( + liquid_core::call_filter!(SnakeCase, "hello world 21").unwrap(), + liquid_core::value!("hello_world_21") + ); + assert_eq!( + liquid_core::call_filter!(SnakeCase, "hello_world_21").unwrap(), + liquid_core::value!("hello_world_21") + ); + assert_eq!( + liquid_core::call_filter!(SnakeCase, "HelloWorld21").unwrap(), + liquid_core::value!("hello_world21") + ); + } + + #[test] + fn kebab_case() { + assert_eq!( + liquid_core::call_filter!(KebabCase, "abc").unwrap(), + liquid_core::value!("abc") + ); + assert_eq!( + liquid_core::call_filter!(KebabCase, "hello world 21").unwrap(), + liquid_core::value!("hello-world-21") + ); + assert_eq!( + liquid_core::call_filter!(KebabCase, "hello_world_21").unwrap(), + liquid_core::value!("hello-world-21") + ); + assert_eq!( + liquid_core::call_filter!(KebabCase, "HelloWorld21").unwrap(), + liquid_core::value!("hello-world21") + ); + } + + #[test] + fn shouty_snake_case() { + assert_eq!( + liquid_core::call_filter!(ShoutySnakeCase, "abc").unwrap(), + liquid_core::value!("ABC") + ); + assert_eq!( + liquid_core::call_filter!(ShoutySnakeCase, "hello world 21").unwrap(), + liquid_core::value!("HELLO_WORLD_21") + ); + assert_eq!( + liquid_core::call_filter!(ShoutySnakeCase, "hello_world_21").unwrap(), + liquid_core::value!("HELLO_WORLD_21") + ); + assert_eq!( + liquid_core::call_filter!(ShoutySnakeCase, "HelloWorld21").unwrap(), + liquid_core::value!("HELLO_WORLD21") + ); + } + + #[test] + fn title_case() { + assert_eq!( + liquid_core::call_filter!(TitleCase, "abc").unwrap(), + liquid_core::value!("Abc") + ); + assert_eq!( + liquid_core::call_filter!(TitleCase, "hello world 21").unwrap(), + liquid_core::value!("Hello World 21") + ); + assert_eq!( + liquid_core::call_filter!(TitleCase, "hello_world_21").unwrap(), + liquid_core::value!("Hello World 21") + ); + assert_eq!( + liquid_core::call_filter!(TitleCase, "HelloWorld21").unwrap(), + liquid_core::value!("Hello World21") + ); + } + + #[test] + fn shouty_kebab_case() { + assert_eq!( + liquid_core::call_filter!(ShoutyKebabCase, "abc").unwrap(), + liquid_core::value!("ABC") + ); + assert_eq!( + liquid_core::call_filter!(ShoutyKebabCase, "hello world 21").unwrap(), + liquid_core::value!("HELLO-WORLD-21") + ); + assert_eq!( + liquid_core::call_filter!(ShoutyKebabCase, "hello_world_21").unwrap(), + liquid_core::value!("HELLO-WORLD-21") + ); + assert_eq!( + liquid_core::call_filter!(ShoutyKebabCase, "HelloWorld21").unwrap(), + liquid_core::value!("HELLO-WORLD21") + ); + } + + #[test] + fn train_case() { + assert_eq!( + liquid_core::call_filter!(TrainCase, "abc").unwrap(), + liquid_core::value!("Abc") + ); + assert_eq!( + liquid_core::call_filter!(TrainCase, "hello world 21").unwrap(), + liquid_core::value!("Hello-World-21") + ); + assert_eq!( + liquid_core::call_filter!(TrainCase, "hello_world_21").unwrap(), + liquid_core::value!("Hello-World-21") + ); + assert_eq!( + liquid_core::call_filter!(TrainCase, "HelloWorld21").unwrap(), + liquid_core::value!("Hello-World21") + ); + } +}