heck
A Rust case conversion library that converts strings between camelCase, snake_case, kebab-case, Title Case and more with Unicode-aware word boundaries.
Repository Health
Technical Analysis
heck is a small, dependency-light Rust crate dedicated to one job: converting strings between case conventions. It covers UpperCamelCase, lowerCamelCase, snake_case, kebab-case, SHOUTY_SNAKE_CASE, Title Case, SHOUTY-KEBAB-CASE, and Train-Case, using a shared word-boundary algorithm that treats non-alphanumeric characters as separators and splits runs of letters on case transitions (so “XMLHttpRequest” segments as “XML”, “Http”, “Request” rather than one long word).
Every case exposes two ways to use it: an extension trait such as ToKebabCase that allocates and returns a String, and a zero-allocation Display wrapper such as AsKebabCase for writing converted output directly into a formatter. The crate is #![no_std] (with alloc), forbids unsafe code, and has no external dependencies, which is why it shows up so often as a transitive dependency inside code-generation and macro crates like syn, heck-consuming derive macros, and CLI scaffolding tools that need to rename identifiers between languages’ naming conventions.
What You Get
- Eight case conversions: UpperCamelCase, lowerCamelCase, snake_case, kebab-case, SHOUTY_SNAKE_CASE, Title Case, SHOUTY-KEBAB-CASE, and Train-Case
- Allocating extension traits (
ToKebabCase,ToSnakeCase, etc.) that return an ownedStringfrom any&str - Zero-allocation
Displaywrapper types (AsKebabCase,AsSnakeCase, etc.) for writing converted output straight into a formatter or writer - Unicode-aware word segmentation built on
char::is_alphanumericand case predicates, not a fixed ASCII table #![no_std]support with onlyallocrequired, and#![forbid(unsafe_code)]for embedding in constrained or security-sensitive builds- A consistent naming convention across every case (
To*Case/As*Case) so learning one converter teaches you all of them
Common Use Cases
- Code generation and derive macros converting Rust identifiers into other languages’ or formats’ naming conventions (JSON keys, GraphQL fields, database columns)
- CLI tools normalizing user-provided flag or command names into a canonical case before matching
- Static site generators and template engines producing slugs or kebab-case URLs from arbitrary titles
- Config and schema tooling translating between snake_case Rust structs and camelCase or kebab-case external formats
- Build scripts and proc-macro crates that need deterministic, dependency-free identifier renaming
Under The Hood
Architecture
All eight case converters share a single generic engine: the transform function in lib.rs walks a string, splits it on non-alphanumeric characters, and re-segments each resulting run using a tri-state WordMode enum (Boundary/Lowercase/Uppercase) that detects case transitions like the Http/Request boundary inside XMLHttpRequest. Each case module (kebab.rs, snake.rs, upper_camel.rs, title.rs, etc.) supplies transform with just two closures — how to render a word (lowercase, uppercase, or capitalize) and how to render the boundary between words (-, _, a space, or nothing) — so the segmentation logic itself is never duplicated. Changing word-boundary detection means editing one function in lib.rs; every one of the eight cases inherits the fix automatically.
Tech Stack
heck is a dependency-free, #![no_std] Rust crate (extern crate alloc for String/ToOwned) targeting edition 2021 with an MSRV of 1.56, built and published purely through Cargo with no build script, workspace, or optional feature flags — the entire crate is 27KB of Rust source across nine files. #![forbid(unsafe_code)] and #![deny(missing_docs)] are set crate-wide, and GitHub Actions runs CI on pushes.
Code Quality
Each case module carries its own #[cfg(test)] block using a small t! macro that expands fixed input/output pairs into individual #[test] functions, including deliberate Unicode edge cases — a Greek final sigma (Σ/ς), Arabic text with no case distinction, and CJK text with no word separation at all — verifying the algorithm degrades safely on scripts it wasn’t designed around. There’s no separate integration-test directory, fuzzing, or benchmark suite, but deny(missing_docs) forces every public item to carry a doc comment, and most of those doc comments include a runnable example that doubles as a doctest.
API Design
The public surface is intentionally narrow and uniform: for every case there is exactly one allocating trait method (to_kebab_case()) and one non-allocating Display wrapper (AsKebabCase(s)), named with the same To*Case/As*Case pattern throughout, so learning ToKebabCase teaches you ToSnakeCase, ToTitleCase, and the rest for free. Getting started requires a single trait import and a method call on any &str, with no configuration, builder, or setup step — the tradeoff is that heck offers no customization of separators or casing rules beyond the eight predefined cases.