{"id":33941800,"url":"https://github.com/rutrum/convert-case","last_synced_at":"2026-04-01T17:22:03.044Z","repository":{"id":49433148,"uuid":"256334677","full_name":"rutrum/convert-case","owner":"rutrum","description":"Converts to and from various cases.","archived":false,"fork":false,"pushed_at":"2026-01-31T11:48:27.000Z","size":237,"stargazers_count":159,"open_issues_count":3,"forks_count":14,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-03-24T21:27:33.103Z","etag":null,"topics":["case","case-converter","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rutrum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-04-16T21:32:40.000Z","updated_at":"2026-03-17T02:35:09.000Z","dependencies_parsed_at":"2024-06-18T20:09:30.011Z","dependency_job_id":"d69d9733-c553-401a-8d6b-a6b162f6bb86","html_url":"https://github.com/rutrum/convert-case","commit_stats":{"total_commits":76,"total_committers":2,"mean_commits":38.0,"dds":0.02631578947368418,"last_synced_commit":"fae1f3f249b9e8255188b9d2a7c87ff7ee596ae8"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rutrum/convert-case","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rutrum%2Fconvert-case","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rutrum%2Fconvert-case/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rutrum%2Fconvert-case/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rutrum%2Fconvert-case/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rutrum","download_url":"https://codeload.github.com/rutrum/convert-case/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rutrum%2Fconvert-case/sbom","scorecard":{"id":790881,"data":{"date":"2025-08-11","repo":{"name":"github.com/rutrum/convert-case","commit":"b9dd92b4394745e15943a604890cdc57fa6bd289"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T07:29:59.890Z","repository_id":49433148,"created_at":"2025-08-23T07:29:59.890Z","updated_at":"2025-08-23T07:29:59.890Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30976070,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-26T14:13:19.610Z","status":"ssl_error","status_checked_at":"2026-03-26T14:12:43.279Z","response_time":114,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["case","case-converter","rust"],"created_at":"2025-12-12T16:56:41.381Z","updated_at":"2026-03-27T04:11:47.721Z","avatar_url":"https://github.com/rutrum.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `convert-case`\n\nRust library for converting between string cases.\n\n```{rust}\nuse convert_case::ccase;\n\nassert_eq!(\n    ccase!(camel, \"My_Var_Name\"),\n    \"myVarName\",\n);\nassert_eq!(\n    ccase!(snake, \"IOStream\"),\n    \"io_stream\",\n);\nassert_eq!(\n    ccase!(snake -\u003e title, \"2020-04-16_family_photo\"),\n    \"2020-04-16 Family Photo\",\n);\n```\n\n`convert-case` is highly customizable.  You can read the API documentation on [docs.rs](https://docs.rs/convert_case/) for a list of all features and read lots of examples.\n\n## Cases\n\nThis is list of cases that `convert_case` provides out of the box.  You can always make your own custom case.\n\n| Case | Example |\n| ---- | ------- |\n| Snake | `my_variable_name` |\n| Constant\u003cbr /\u003eUpperSnake | `MY_VARIABLE_NAME` |\n| Ada | `My_Variable_Name` |\n| Kebab | `my-variable-name` |\n| Cobol\u003cbr /\u003eUpperKebab | `MY-VARIABLE-NAME` |\n| Train | `My-Variable-Name` |\n| Flat | `myvariablename` |\n| UpperFlat | `MYVARIABLENAME` |\n| Pascal\u003cbr /\u003eUpperCamel | `MyVariableName` |\n| Camel | `myVariableName` |\n| Upper | `MY VARIABLE NAME` |\n| Lower | `my variable name` |\n| Title | `My Variable Name` |\n| Sentence | `My variable name` |\n\n## Additional utilities with `convert_case_extras`\n\nSome cases and utilities that didn't feel appropriate in this library are made available in a distinct crate called [`convert_case_extras`](https://github.com/rutrum/convert-case-extras).  This crate is a demonstration of what can be built on top of the `convert_case` API.\n\n## Command Line Utility `ccase`\n\nThe [command line utility `ccase`](https://github.com/rutrum/ccase) was made to expose the tools of the `convert_case` library to the command line.\n```\n$ ccase -t title super_mario_64\nSuper Mario 64\n\n$ ccase -f snake -t title 2020-04-15_my_cat\n2020-04-16 My Cat\n\n$ ccase -t camel \"convert to camel\"\nconvertToCamel\n```\n\n## Links\n\n| | `convert_case` | `convert_case_extras` | `ccase` |\n| --- | --- | --- | --- |\n| Repository | [github](https://github.com/rutrum/convert-case) | [github](https://github.com/rutrum/convert-case-extras) | [github](https://github.com/rutrum/ccase) |\n| Crate | [crates.io](https://crates.io/crates/convert_case) | [crates.io](https://crates.io/crates/convert_case_extras) | [crates.io](https://crates.io/crates/ccase) |\n| Documentation | [docs.rs](https://docs.rs/convert_case) | [docs.rs](https://docs.rs/convert_case_extras) | |\n\n## Change Log\n\n### 0.11.0: Multiple Patterns\n\nThe headline change permits trailing, leading, and duplicate delimiters to persist (default) or be removed.  Instead of applying a single pattern, `Converter` supports multiple patterns and will apply them in order.  This comes with another pattern `Pattern::RemoveEmpty` that drops empty words.   This allows for opting into what was default behavior in 0.8.0 and before.\n\nNew features:\n* `Converter` now supports multiple patterns via `Converter.patterns: Vec\u003cPattern\u003e`. Patterns are applied in sequence, allowing for composition.\n* `Pattern::RemoveEmpty` filters out empty words. This is useful when splitting produces empty words from leading, trailing, or duplicate delimiters (e.g., `\"--a--b\"` split on hyphens).\n* `Casing::remove_empty()` is a convenience method that adds `Pattern::RemoveEmpty` to the `patterns` vector.\n```rust\n\"--leading-delims\".remove_empty().to_case(Case::Camel)\n// \"leadingDelims\"\n```\n\nBreaking changes:\n* `delim_boundary` macro is now `separator`\n* Removed `Casing::is_case`.  Similar functionality is now implemented in `convert-case-extras`.\n* Instances of `delim` have been renamed to `delimiter`:\n    * `Converter.delim` -\u003e `Converter.delimiter` \n    * `Converter::set_delim` -\u003e `Converter::set_delimiter`\n    * `Case::Custom.delim` -\u003e `Case::Custom.delimiter` \n    * `Case::delim` -\u003e `Case::delimiter`\n* `Converter.pattern` is now `Converter.patterns` with type `Vec\u003cPattern\u003e`\n    * New pattern methods on `Converter` just like boundaries: `set_patterns`, `add_pattern`, `add_patterns`, `remove_pattern`, `remove_patterns`.\n    * `Pattern::Noop` removed. An empty patterns vector represents no mutation.\n* `Boundary::Custom` and `Pattern::Custom` variants are no longer comparable. \n  * Implementations of `PartialEq`, `Eq`, and `Hash` are no longer derived on `Boundary` and `Pattern`, and have been manually implemented.\n  * Custom variants always return `false` for equality checks because function pointers cannot be reliably compared. This has been true for all of rust, but the compiler warning is relatively new to the author.  This means `remove_boundary` and `remove_pattern` will not work for custom variants.\n  * Further, all `Custom` variants now hash to the same value.\n    \nOther changes:\n* Much improved documentation: fixed typos, better examples.\n* More comprehensive testing using `rstest`.\n\n### 0.10.0: More clean up to prepare for 1.0.0\n\nSince the library is so extensible with its new API, there is no longer a need for some niche or fun transformations to be made available in this library.  Some of the features that are removed are now in a new library `convert_case_extras`.  That library will have a lower threshold on what is included (i.e. more features), and will also serve as a demonstration of what's capable with the `convert_case` API.\n\nRemoved:\n* `Case::Toggle` and `Pattern::Toggle`\n* `Case::Alternating` and `Pattern::Alternating`\n* `Case::Random` and `Pattern::Random`\n* `Case::PseudoRandom` and `Pattern::PseudoRandom`\n* `random` feature is removed.  The library no longer has any features.\n* `Case::deterministic_cases` is removed\n* `Case::random_cases` is removed\n\nOther breaking changes:\n* `Boundary::Custom` has lost the `arg` parameter and `Boundary::Custom.condition` is more simply `fn(\u0026[\u0026str]) -\u003e bool`.\n  * `arg` was originally used for building boundaries from delimiters with the `Boundary::from_delim` function, which is also removed because\n  * `delim_boundary!` macro has replaced `Boundary::from_delim` functionality, without the need of the `arg` parameters\n* `Casing::with_boundaries` is now `Casing::set_boundaries` and `Casing::without_boundaries` is now `Casing::remove_boundaries` to align with `Converter`\n\n### 0.9.0: Back to enums, but keep the customization\n\nThis release is trying to finalize the API for a 1.0.0 release.  In hindsight, making `Pattern` a function and `Boundary` a struct were poor decisions.  While trying to add customized behavior, I made it harder to use.  Luckily, by following the convention that was done with `Case` in 0.8.0, which was providing a struct-variant `Case::Custom`, I can keep the new customization options while reverting back to the enum pattern the library has had since the beginning.\n\nIf you are updating from before 0.7.0, I don't think any changes are necessary.  If you're coming from 0.7.0 or higher,\n* Change boundaries from constants to enum variants: `Boundary::UPPER_SNAKE` into `Boundary::UpperSnake`\n* Change patterns from functions to enum variants on `Pattern`: `pattern::lowercase` into `Pattern::Lowercase`\n\nMost excitingly, I've introduced a brand new way to use the crate for 99% of use cases, which is the default behavior out of the box to just convert a string to a case, with no modifications to behavior.  Instead of\n```\nuse convert_case::{Case, Casing};\n\"MyVarName\".to_case(Case::Snake);\n\"my-var name\".from_case(Case::Title).to_case(Case::Snake);\n```\nYou can use the `ccase!` macro:\n```\nuse convert_case::ccase;\nccase!(snake, \"MyVarName\");\nccase!(title -\u003e snake, \"my-var name\");\n```\nThis means only one import and its invocation is brief and easy to read.  This is now the first thing you see when viewing the docs, which themselves have gotten a huge overhaul and many improvements in this version.\n\nNew features:\n* `ccase!` macro that performs case conversion on a string _without needing to import `Case` or `Casing`_.  It has two forms:\n    * `ccase!(snake, \"string\")` is equivalent to `\"string\".to_case(Case::Snake)`\n    * `ccase!(kebab -\u003e snake, \"string\")` is equivalent to `\"string\".from_case(Case::Kebab).to_case(Case::Snake)`\n* While not intended to be used directly, the new `case!` macro returns a `Case` variant from the snake case version of the variant.  For instance, `case!(snake)` is substituted for `Case::Snake` and `case!(upper_flat)` for `Case::UpperFlat`.\n\nOther breaking changes:\n* Leading, trailing, and duplicate delimiters are no longer removed and are returned as is.\n* `Boundary` is reverted back to being an enum, but with a `Custom` variant that gives all the same flexibility that `Boundary` had as a struct.  This aligns with the `Case::Custom` pattern.\n    * `Boundary.match` will return true if a set of graphemes matches the boundary condition\n    * `Boundary` methods for `start` and `len` describe how enum variants consume letters when matched\n* `Pattern` is reverted back to being an enum, but with a `Custom` variant that allowed you to pass your own `fn (\u0026[\u0026str]) -\u003e Vec\u003cString\u003e` as input.\n    * `Pattern.mutate` will perform the associated mutation function\n* `Converter.with_boundaries` has been renamed to `Converter.set_boundaries`.\n* Removed `Converter.remove_delim` and `Converter.remove_pattern`.  You can use `set_delim(\"\")` and `set_pattern(Pattern::Noop)` instead.\n* `ToString` is no longer a required trait for using `is_case`\n* `word_pattern` module has been removed\n\n### 0.8.0: Pattern Overhaul, Custom Case\n\nPattern is no longer an enum.  It is now a type alias for `fn(\u0026[\u0026str]) -\u003e Vec\u003cString\u003e`.  The variants of Pattern can now be referenced as functions inside the `pattern` module.  For upgrading this means changing `Pattern::Lowercase` to `pattern::lowercase`, and calling the function directly instead of invoking the `mutate` method on the enum.\n\nInside the pattern module is also the type alias `Pattern` itself.\n\nOther breaking changes:\n* Add `Case::Ada` (capital pattern with underscore delimiter.)\n* Add `Case::Custom` variant.  It is a struct variant that takes three parameters:\n    * pattern with type `Pattern`\n    * delim with type `\u0026static str`, and\n    * boundaries with type `\u0026'static [Boundary]`.\n* Because of the new `pattern::noop` function, `Converter` attribute `pattern` is now of type `Pattern` and not `Option\u003cPattern\u003e`\n* `Case::deterministic_cases`, `Case::all_cases`, and `Case::random_cases` now return static arrays instead of vecs\n\nOther changes:\n* Added `Case::split`, `Case::mutate`, and `Case::join` which expose operations related to the boundaries, pattern, and delimiter of a case\n* Is now `no_std` compatible\n\n### 0.7.1\n\n* Removed debug print statement.\n\n### 0.7.0: Custom Boundaries\n\nBoundary is no longer an enum.  It now is a struct, and each enum variant corresponds to an associated constant.  For upgrading this just means changing `Boundary::LowerUpper` to just `Boundary::LOWER_UPPER`.\n\nThe benefit of this is that you can make your boundary conditions now, by instantiating the `Boundary` struct, or using `Boundary::from_delim()`.  Now you can split on newlines, periods, double-colons, emojis, or a more complex case like a symbol followed by a digit.  You also define which characters, if any, are removed during segmentation, and where the split happens.\n\nChanges from this feature:\n* Previous `Boundary::PascalName` enum variants now much referred to as `Boundary::CONSTANT_NAME` constants.\n* All functions that returned groups of boundaries (such as `Boundary::defaults()`, `Boundary::digit_letter()`, etc) now are const and return fixed-sized arrays `[Boundary; N]`, not `Vec\u003cBoundary\u003e`.\n* `Boundary::all()` was removed, since there's no longer a sense of \"all\" boundaries, since you can create your own.\n* `Boundary::list_from()` has been renamed to `Boundary::defaults_from()` and no longer outputs `Boundary::UPPER_LOWER`, since this function now only checks default boundaries.\n* Create custom delimiter boundaries using `Boundary::from_delim()`.\n\nOther breaking changes:\n\n* Rename `Case::ScreamingSnake` to `Case::Constant`.\n* Add `Case::Sentence` (sentence pattern and space delimiter.)\n* `Casing` trait implemented for `Arc\u003cstr\u003e` and `Rc\u003cstr\u003e` again.\n\nOther changes:\n\n* Remove most imports from doc comments.\n* Remove loop over `str::chars` in favor of `graphemes` from `unicode-segmentation`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frutrum%2Fconvert-case","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frutrum%2Fconvert-case","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frutrum%2Fconvert-case/lists"}