diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..45724de --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,57 @@ +name: Rust + +on: [push, pull_request] + +jobs: + build_and_test: + name: Build and Test + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + rust: [stable] + fail-fast: true + permissions: + checks: write + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + + - name: Cache Cargo dependencies + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Run Clippy + if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' + uses: giraffate/clippy-action@v1 + with: + reporter: "github-pr-review" + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install cargo-audit + run: cargo install cargo-audit + + - name: Audit dependencies + run: cargo audit + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: cargo test --verbose diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..d54c84d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,27 @@ +# How to Contribute + +We welcome patches and contributions to this project. To ensure consistency and quality, we ask that contributors adhere to the few small guidelines. + +## Coding Guidelines + +- **Code Formatting**: Ensure your code is formatted according to Rust's standard formatting guidelines. You can use `cargo fmt` to automatically format your code. + +- **Documentation**: Exported types, constants, variables, and functions should be documented following Rust's documentation guidelines. + +- **Testing**: Changes must be covered with tests. We use `cargo test` to run tests, and all tests must pass. Ensure your new features or fixes include appropriate tests. + +- **Versioning**: This Rust client follows [semantic versioning](https://semver.org/). New functionality should be accompanied by an increment to the minor version number. + +## Releasing + +Releases are made from the `master` branch and should follow these steps: + +1. **Update Version Number**: Update the version number in `Cargo.toml` to reflect the new version of the client. + +2. **Create a Pull Request**: Make a pull request with the version change and any other relevant updates. + +3. **Merge and Release**: + - Once the pull request has been reviewed and merged, go to the [releases page](https://github.com/janos/casbsb-rs/releases) of the repository. + - Click "Draft a new release". + - Set the "Tag version" and "Release title" to the new version of the Rust client, prefixed with `v`, e.g., `v1.2.0`. + - Publish the release. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3ff40e0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,640 @@ +# 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 = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "casbab" +version = "0.1.0" +dependencies = [ + "criterion", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "is-terminal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +dependencies = [ + "hermit-abi", + "rustix", + "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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +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 = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +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 = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[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.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a1acb07 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "casbab" +version = "0.1.0" +edition = "2021" +description = "A Rust client library for converting representation style of compound words or phrases." +license = "BSD-3-Clause" +repository = "https://github.com/janos/casbab-rs" +authors = ["Janos Guljas "] +keywords = ["camel", "snake", "kebab", "pascal"] + +[dependencies] + +[dev-dependencies] +criterion = "0.5.1" + +[[bench]] +name = "benchmark" +harness = false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6888501 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2024, Janoš Guljaš +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of this project nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 7756733..d23e108 100644 --- a/README.md +++ b/README.md @@ -1 +1,64 @@ -# casbab-rs \ No newline at end of file +# Camel Snake Kebab + +[![Rust](https://github.com/janos/casbab-rs/actions/workflows/rust.yml/badge.svg)](https://github.com/janos/casbab-rs/actions/workflows/rust.yml) +[![Crate](https://img.shields.io/crates/v/casbab.svg)](https://crates.io/crates/casbab) +[![Docs](https://docs.rs/casbsb/badge.svg)](https://docs.rs/casbsb) + +Package casbab is a rust library for converting representation style of compound words or phrases. Different writing styles of compound words are used for different purposes in computer code and variables to easily distinguish type, properties or meaning. + +Functions in this package are separating words from input string and constructing an appropriate phrase representation. + +## Install + +Run the following Cargo command in your project directory: + +```sh +cargo add casbab +``` + +## Features + +This library implements the following functions that return appropriate styles of compound words: + +- camel: example `camelSnakeKebab` +- pascal: example `CamelSnakeKebab` +- snake: example `camel_snake_kebab` +- camel_snake: example `Camel_Snake_Kebab` +- screaming_snake: example `CAMEL_SNAKE_KEBAB` +- kebab: example `camel-snake-kebab` +- camel_kebab: example `Camel-Snake-Kebab` +- screaming_kebab: example `CAMEL-SNAKE-KEBAB` +- lower: example `camel snake kebab` +- title: example `Camel Snake Kebab` +- screaming: example `CAMEL SNAKE KEBAB` + +## Performance + +Benchmarks run `cargo bench` on MacBook Pro M1Pro yield these timings: + +``` +camel time: [1.0097 µs 1.0164 µs 1.0241 µs] +pascal time: [1.0299 µs 1.0371 µs 1.0444 µs] +snake time: [681.45 ns 685.85 ns 689.92 ns] +camel_snake time: [1.1460 µs 1.1534 µs 1.1606 µs] +screaming_snake time: [1.0004 µs 1.0057 µs 1.0108 µs] +kebab time: [717.66 ns 726.00 ns 734.36 ns] +camel_kebab time: [1.1163 µs 1.1238 µs 1.1309 µs] +screaming_kebab time: [999.09 ns 1.0103 µs 1.0210 µs] +title time: [1.0305 µs 1.0412 µs 1.0518 µs] +lower time: [640.24 ns 650.07 ns 659.39 ns] +screaming time: [929.30 ns 935.17 ns 941.02 ns] +``` + +## Versioning + +Each version of the client is tagged and the version is updated accordingly. +To see the list of past versions, run `git tag`. + +## Contributing + +We love pull requests! Please see the [contribution guidelines](CONTRIBUTING.md). + +## License + +This library is distributed under the BSD-style license found in the [LICENSE](LICENSE) file. diff --git a/benches/benchmark.rs b/benches/benchmark.rs new file mode 100644 index 0000000..216308c --- /dev/null +++ b/benches/benchmark.rs @@ -0,0 +1,92 @@ +// Copyright (c) 2024, Janoš Guljaš +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +use criterion::{criterion_group, criterion_main, Criterion}; + +use casbab; + +const BENCHMARK_PHASE: &str = "xCAMELSnakeKebab_screaming pascal XXX"; + +fn benchmark_camel(c: &mut Criterion) { + c.bench_function("camel", |b| { + b.iter(|| casbab::camel(BENCHMARK_PHASE)); + }); +} + +fn benchmark_pascal(c: &mut Criterion) { + c.bench_function("pascal", |b| { + b.iter(|| casbab::pascal(BENCHMARK_PHASE)); + }); +} + +fn benchmark_snake(c: &mut Criterion) { + c.bench_function("snake", |b| { + b.iter(|| casbab::snake(BENCHMARK_PHASE)); + }); +} + +fn benchmark_camel_snake(c: &mut Criterion) { + c.bench_function("camel_snake", |b| { + b.iter(|| casbab::camel_snake(BENCHMARK_PHASE)); + }); +} + +fn benchmark_screaming_snake(c: &mut Criterion) { + c.bench_function("screaming_snake", |b| { + b.iter(|| casbab::screaming_snake(BENCHMARK_PHASE)); + }); +} + +fn benchmark_kebab(c: &mut Criterion) { + c.bench_function("kebab", |b| { + b.iter(|| casbab::kebab(BENCHMARK_PHASE)); + }); +} + +fn benchmark_camel_kebab(c: &mut Criterion) { + c.bench_function("camel_kebab", |b| { + b.iter(|| casbab::camel_kebab(BENCHMARK_PHASE)); + }); +} + +fn benchmark_screaming_kebab(c: &mut Criterion) { + c.bench_function("screaming_kebab", |b| { + b.iter(|| casbab::screaming_kebab(BENCHMARK_PHASE)); + }); +} + +fn benchmark_lower(c: &mut Criterion) { + c.bench_function("lower", |b| { + b.iter(|| casbab::lower(BENCHMARK_PHASE)); + }); +} + +fn benchmark_title(c: &mut Criterion) { + c.bench_function("title", |b| { + b.iter(|| casbab::title(BENCHMARK_PHASE)); + }); +} + +fn benchmark_screaming(c: &mut Criterion) { + c.bench_function("screaming", |b| { + b.iter(|| casbab::screaming(BENCHMARK_PHASE)); + }); +} + +criterion_group!( + benches, + benchmark_camel, + benchmark_pascal, + benchmark_snake, + benchmark_camel_snake, + benchmark_screaming_snake, + benchmark_kebab, + benchmark_camel_kebab, + benchmark_screaming_kebab, + benchmark_lower, + benchmark_title, + benchmark_screaming, +); +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e5844df --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,251 @@ +// Copyright (c) 2024, Janoš Guljaš +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//! # Camel Snake Kebab +//! +//! Package *casbab* is a rust library for converting +//! representation style of compound words or phrases. +//! Different writing styles of compound words are used +//! for different purposes in computer code and variables +//! to easily distinguish type, properties or meaning. +//! +//! Functions in this package are separating words from +//! input string and constructing an appropriate phrase +//! representation. +//! +//! Examples: +//! +//! - `kebab("camel_snake_kebab")` returns `camel-snake-kebab` +//! - `screaming_snake("camel_snake_kebab")` returns `CAMEL_SNAKE_KEBAB` +//! - `camel("camel_snake_kebab")` returns `camelSnakeKebab` +//! - `pascal("camel_snake_kebab")` returns `CamelSnakeKebab` +//! - `snake("camelSNAKEKebab")` returns `camel_snake_kebab` +//! +//! Word separation works by detecting delimiters hyphen (-), +//! underscore (_), space ( ) and letter case change. +//! +//! Note: Leading and trailing separators will be preserved +//! only within the Snake family or within the Kebab family +//! and not across them. This restriction is based on different +//! semantics between different writings. +//! +//! Examples: +//! +//! - `camel_snake("__camel_snake_kebab__")` returns `__Camel_Snake_Kebab__` +//! - `kebab("__camel_snake_kebab")` returns `camel-snake-kebab` +//! - `screaming("__camel_snake_kebab")` returns `CAMEL SNAKE KEBAB` +//! - `camel_kebab("--camel-snake-kebab")` returns `--Camel-Snake-Kebab` +//! - `snake("--camel-snake-kebab")` returns `camel_snake_kebab` +//! - `screaming("--camel-snake-kebab")` returns `CAMEL SNAKE KEBAB` + +use std::usize; + +/// *Camel* case is the practice of writing compound words +/// or phrases such that each word or abbreviation in the +/// middle of the phrase begins with a capital letter, +/// with no spaces or hyphens. +/// +/// Example: `camelSnakeKebab`. +pub fn camel(s: &str) -> String { + let mut w = words(s); + camel_slice(&mut w, 1); + w.join("") +} + +/// *Pascal* case is a variant of Camel case writing where +/// the first letter of the first word is always capitalized. +/// +/// Example: `CamelSnakeKebab`. +pub fn pascal(s: &str) -> String { + let mut w = words(s); + camel_slice(&mut w, 0); + w.join("") +} + +/// *Snake* case is the practice of writing compound words +/// or phrases in which the elements are separated with +/// one underscore character (_) and no spaces, with all +/// element letters lowercased within the compound. +/// +/// Example: `camel_snake_kebab`. +pub fn snake(s: &str) -> String { + let (head, tail) = head_tail_count(s, '_'); + "_".repeat(head) + words(s).join("_").as_str() + "_".repeat(tail).as_str() +} + +/// *Camel snake* case is a variant of Camel case with +/// each element's first letter uppercased. +/// +/// Example: `Camel_Snake_Kebab`. +pub fn camel_snake(s: &str) -> String { + let (head, tail) = head_tail_count(s, '_'); + let mut w = words(s); + camel_slice(&mut w, 0); + "_".repeat(head) + w.join("_").as_str() + "_".repeat(tail).as_str() +} + +/// *Screaming snake* case is a variant of Camel case with +/// all letters uppercased. +/// +/// Example: `CAMEL_SNAKE_KEBAB`. +pub fn screaming_snake(s: &str) -> String { + let (head, tail) = head_tail_count(s, '_'); + let mut w = words(s); + scream_slice(&mut w); + "_".repeat(head) + w.join("_").as_str() + "_".repeat(tail).as_str() +} + +/// *Kebab* case is the practice of writing compound words +/// or phrases in which the elements are separated with +/// one hyphen character (-) and no spaces, with all +/// element letters lowercased within the compound. +/// +/// Example: `camel-snake-kebab`. +pub fn kebab(s: &str) -> String { + let (head, tail) = head_tail_count(s, '-'); + "-".repeat(head) + words(s).join("-").as_str() + "-".repeat(tail).as_str() +} + +/// *Camel kebab* case is a variant of Kebab case with +/// each element's first letter uppercased. +/// +/// Example: `Camel-Snake-Kebab`. +pub fn camel_kebab(s: &str) -> String { + let (head, tail) = head_tail_count(s, '-'); + let mut w = words(s); + camel_slice(&mut w, 0); + "-".repeat(head) + w.join("-").as_str() + "-".repeat(tail).as_str() +} + +/// *Screaming kebab* case is a variant of Kebab case with +/// all letters uppercased. +/// +/// Example: `CAMEL-SNAKE-KEBAB`. +pub fn screaming_kebab(s: &str) -> String { + let (head, tail) = head_tail_count(s, '-'); + let mut w = words(s); + scream_slice(&mut w); + "-".repeat(head) + w.join("-").as_str() + "-".repeat(tail).as_str() +} + +/// *Lower* is returning detected words, not in a compound +/// form, but separated by one space character with all +/// letters in lower case. +/// +/// Example: `camel snake kebab`. +pub fn lower(s: &str) -> String { + words(s).join(" ") +} + +/// *Title* is returning detected words, not in a compound +/// form, but separated by one space character with first +/// character in all letters in upper case and all other +/// letters in lower case. +/// +/// Example: `Camel Snake Kebab`. +pub fn title(s: &str) -> String { + let mut w = words(s); + camel_slice(&mut w, 0); + w.join(" ") +} + +/// *Screaming* is returning detected words, not in a compound +/// form, but separated by one space character with all +/// letters in upper case. +/// +/// Example: `CAMEL SNAKE KEBAB`. +pub fn screaming(s: &str) -> String { + let mut w = words(s); + scream_slice(&mut w); + w.join(" ") +} + +fn words(s: &str) -> Vec { + let mut start: usize = 0; + let l = s.len(); + let mut prev_lower = false; + let mut prev_upper = false; + let mut prev_upper_location: usize = 0; + + let mut words: Vec = Vec::new(); + for (i, c) in s.char_indices() { + if c == '-' || c == '_' || c == ' ' { + if start != i { + words.push(s[start..i].to_lowercase()); + }; + start = i + 1; + prev_lower = false; + prev_upper = false; + prev_upper_location = 0; + continue; + } + + if c.is_uppercase() { + prev_upper = true; + prev_upper_location = i; + if prev_lower { + if start != i { + words.push(s[start..i].to_lowercase()) + } + start = i; + prev_lower = false; + }; + } else { + prev_lower = true; + if prev_upper && prev_upper_location > 0 { + if start != prev_upper_location { + words.push(s[start..prev_upper_location].to_lowercase()) + } + start = prev_upper_location; + prev_upper = false; + prev_upper_location = 0; + }; + } + } + if start != l { + words.push(s[start..].to_lowercase()); + } + words +} + +fn scream_slice(s: &mut [String]) { + for e in s { + *e = e.to_uppercase() + } +} + +fn camel_slice(s: &mut [String], start: usize) { + for e in s.iter_mut().skip(start) { + to_titlecase(e); + } +} + +fn to_titlecase(e: &mut String) { + *e = match e.chars().next() { + None => e.to_owned(), + Some(f) => { + e.replace_range(..f.len_utf8(), &f.to_uppercase().to_string()[..]); + e.to_owned() + } + }; +} + +fn head_tail_count(s: &str, sub: char) -> (usize, usize) { + let mut head: usize = 0; + for (i, char) in s.char_indices() { + if char != sub { + head = i; + break; + } + } + let mut tail: usize = 0; + for (i, char) in s.chars().rev().enumerate() { + if char != sub { + tail = i; + break; + } + } + (head, tail) +} diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 0000000..cd94240 --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,211 @@ +// Copyright (c) 2024, Janoš Guljaš +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +use casbab; + +struct Case { + input: Vec, + camel: String, + pascal: String, + snake: String, + camel_snake: String, + screaming_snake: String, + kebab: String, + camel_kebab: String, + screaming_kebab: String, + lower: String, + title: String, + screaming: String, +} + +#[test] +fn casbab_test() { + let cases = vec![ + Case { + input: vec![ + "camelSnakeKebab".to_string(), + "CamelSnakeKebab".to_string(), + "camel_snake_kebab".to_string(), + "Camel_Snake_Kebab".to_string(), + "CAMEL_SNAKE_KEBAB".to_string(), + "camel-snake-kebab".to_string(), + "Camel-Snake-Kebab".to_string(), + "CAMEL-SNAKE-KEBAB".to_string(), + "camel snake kebab".to_string(), + "Camel Snake Kebab".to_string(), + "CAMEL SNAKE KEBAB".to_string(), + "camel__snake_kebab".to_string(), + "camel___snake_kebab".to_string(), + "camel____snake_kebab".to_string(), + "camel_ snake_kebab".to_string(), + "camel_ snake_kebab".to_string(), + "camel_ snake_kebab".to_string(), + "camel_-snake_kebab".to_string(), + "camel_ -snake_kebab".to_string(), + "camel - snake_kebab".to_string(), + " camel - snake_kebab".to_string(), + "CAMELSnakeKebab".to_string(), + "camelSNAKEKebab ".to_string(), + " camelSnakeKEBAB".to_string(), + ], + camel: "camelSnakeKebab".to_string(), + pascal: "CamelSnakeKebab".to_string(), + snake: "camel_snake_kebab".to_string(), + camel_snake: "Camel_Snake_Kebab".to_string(), + screaming_snake: "CAMEL_SNAKE_KEBAB".to_string(), + kebab: "camel-snake-kebab".to_string(), + camel_kebab: "Camel-Snake-Kebab".to_string(), + screaming_kebab: "CAMEL-SNAKE-KEBAB".to_string(), + lower: "camel snake kebab".to_string(), + title: "Camel Snake Kebab".to_string(), + screaming: "CAMEL SNAKE KEBAB".to_string(), + }, + Case { + input: vec![ + "__camel_snake_kebab__".to_string(), + "__camel_snakeKEBAB__".to_string(), + "__ Camel-snakeKEBAB__".to_string(), + ], + camel: "camelSnakeKebab".to_string(), + pascal: "CamelSnakeKebab".to_string(), + snake: "__camel_snake_kebab__".to_string(), + camel_snake: "__Camel_Snake_Kebab__".to_string(), + screaming_snake: "__CAMEL_SNAKE_KEBAB__".to_string(), + kebab: "camel-snake-kebab".to_string(), + camel_kebab: "Camel-Snake-Kebab".to_string(), + screaming_kebab: "CAMEL-SNAKE-KEBAB".to_string(), + lower: "camel snake kebab".to_string(), + title: "Camel Snake Kebab".to_string(), + screaming: "CAMEL SNAKE KEBAB".to_string(), + }, + Case { + input: vec![ + "__ camel-snake_kebab__ _".to_string(), + "__ camelSnake_Kebab_".to_string(), + "__CamelSnake_Kebab_".to_string(), + "__CamelSNAKE_Kebab_".to_string(), + ], + camel: "camelSnakeKebab".to_string(), + pascal: "CamelSnakeKebab".to_string(), + snake: "__camel_snake_kebab_".to_string(), + camel_snake: "__Camel_Snake_Kebab_".to_string(), + screaming_snake: "__CAMEL_SNAKE_KEBAB_".to_string(), + kebab: "camel-snake-kebab".to_string(), + camel_kebab: "Camel-Snake-Kebab".to_string(), + screaming_kebab: "CAMEL-SNAKE-KEBAB".to_string(), + lower: "camel snake kebab".to_string(), + title: "Camel Snake Kebab".to_string(), + screaming: "CAMEL SNAKE KEBAB".to_string(), + }, + Case { + input: vec![ + "--camel-snake-kebab".to_string(), + "--CAMELSnake_kebab".to_string(), + ], + camel: "camelSnakeKebab".to_string(), + pascal: "CamelSnakeKebab".to_string(), + snake: "camel_snake_kebab".to_string(), + camel_snake: "Camel_Snake_Kebab".to_string(), + screaming_snake: "CAMEL_SNAKE_KEBAB".to_string(), + kebab: "--camel-snake-kebab".to_string(), + camel_kebab: "--Camel-Snake-Kebab".to_string(), + screaming_kebab: "--CAMEL-SNAKE-KEBAB".to_string(), + lower: "camel snake kebab".to_string(), + title: "Camel Snake Kebab".to_string(), + screaming: "CAMEL SNAKE KEBAB".to_string(), + }, + Case { + input: vec![ + "-camel-snake-kebab----".to_string(), + "-CAMEL Snake_kebab ----".to_string(), + ], + camel: "camelSnakeKebab".to_string(), + pascal: "CamelSnakeKebab".to_string(), + snake: "camel_snake_kebab".to_string(), + camel_snake: "Camel_Snake_Kebab".to_string(), + screaming_snake: "CAMEL_SNAKE_KEBAB".to_string(), + kebab: "-camel-snake-kebab----".to_string(), + camel_kebab: "-Camel-Snake-Kebab----".to_string(), + screaming_kebab: "-CAMEL-SNAKE-KEBAB----".to_string(), + lower: "camel snake kebab".to_string(), + title: "Camel Snake Kebab".to_string(), + screaming: "CAMEL SNAKE KEBAB".to_string(), + }, + Case { + input: vec![ + "xCamelXXSnakeXXXKebab".to_string(), + "XCamelXXSnakeXXXKebab".to_string(), + "x_camel_xx_snake_xxx_kebab".to_string(), + "X_Camel_XX_Snake_XXX_Kebab".to_string(), + "X_CAMEL_XX_SNAKE_XXX_KEBAB".to_string(), + "x-camel-xx-snake-xxx-kebab".to_string(), + "X-Camel-XX_Snake-XXX-Kebab".to_string(), + "X-CAMEL-XX_SNAKE-XXX-KEBAB".to_string(), + "x camel xx snake xxx kebab".to_string(), + "X Camel XX Snake XXX Kebab".to_string(), + "X CAMEL XX SNAKE XXX KEBAB".to_string(), + ], + camel: "xCamelXxSnakeXxxKebab".to_string(), + pascal: "XCamelXxSnakeXxxKebab".to_string(), + snake: "x_camel_xx_snake_xxx_kebab".to_string(), + camel_snake: "X_Camel_Xx_Snake_Xxx_Kebab".to_string(), + screaming_snake: "X_CAMEL_XX_SNAKE_XXX_KEBAB".to_string(), + kebab: "x-camel-xx-snake-xxx-kebab".to_string(), + camel_kebab: "X-Camel-Xx-Snake-Xxx-Kebab".to_string(), + screaming_kebab: "X-CAMEL-XX-SNAKE-XXX-KEBAB".to_string(), + lower: "x camel xx snake xxx kebab".to_string(), + title: "X Camel Xx Snake Xxx Kebab".to_string(), + screaming: "X CAMEL XX SNAKE XXX KEBAB".to_string(), + }, + Case { + input: vec![ + "Ово је Brave NewСвет".to_string(), + " Ово је Brave NewСвет".to_string(), + " Ово је Brave NewСвет ".to_string(), + ], + camel: "овоЈеBraveNewСвет".to_string(), + pascal: "ОвоЈеBraveNewСвет".to_string(), + snake: "ово_је_brave_new_свет".to_string(), + camel_snake: "Ово_Је_Brave_New_Свет".to_string(), + screaming_snake: "ОВО_ЈЕ_BRAVE_NEW_СВЕТ".to_string(), + kebab: "ово-је-brave-new-свет".to_string(), + camel_kebab: "Ово-Је-Brave-New-Свет".to_string(), + screaming_kebab: "ОВО-ЈЕ-BRAVE-NEW-СВЕТ".to_string(), + lower: "ово је brave new свет".to_string(), + title: "Ово Је Brave New Свет".to_string(), + screaming: "ОВО ЈЕ BRAVE NEW СВЕТ".to_string(), + }, + Case { + input: vec!["".to_string()], + camel: "".to_string(), + pascal: "".to_string(), + snake: "".to_string(), + camel_snake: "".to_string(), + screaming_snake: "".to_string(), + kebab: "".to_string(), + camel_kebab: "".to_string(), + screaming_kebab: "".to_string(), + lower: "".to_string(), + title: "".to_string(), + screaming: "".to_string(), + }, + ]; + + for c in cases { + for input in c.input { + assert_eq!(casbab::camel(input.as_str()), c.camel); + assert_eq!(casbab::pascal(input.as_str()), c.pascal); + assert_eq!(casbab::snake(input.as_str()), c.snake); + assert_eq!(casbab::camel_snake(input.as_str()), c.camel_snake); + assert_eq!(casbab::screaming_snake(input.as_str()), c.screaming_snake); + assert_eq!(casbab::kebab(input.as_str()), c.kebab); + assert_eq!(casbab::camel_kebab(input.as_str()), c.camel_kebab); + assert_eq!(casbab::screaming_kebab(input.as_str()), c.screaming_kebab); + assert_eq!(casbab::lower(input.as_str()), c.lower); + assert_eq!(casbab::title(input.as_str()), c.title); + assert_eq!(casbab::screaming(input.as_str()), c.screaming); + } + } +}