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") + ); + } +}