Expand description
Converts to and from various cases.
§Command Line Utility ccase
This library was developed for the purposes of a command line utility for converting
the case of strings and filenames. You can check out
ccase
on Github.
§Rust Library
Provides a Case
enum which defines a variety of cases to convert into.
Strings have implemented the Casing
trait, which adds methods for
case conversion.
You can convert strings into a case using the to_case
method.
use convert_case::{Case, Casing};
assert_eq!("Ronnie James Dio", "ronnie james dio".to_case(Case::Title));
assert_eq!("ronnieJamesDio", "Ronnie_James_dio".to_case(Case::Camel));
assert_eq!("Ronnie-James-Dio", "RONNIE_JAMES_DIO".to_case(Case::Train));
By default, to_case
will split along a set of default word boundaries, that is
- underscores
_
, - hyphens
-
, - spaces
- changes in capitalization from lowercase to uppercase
aA
, - adjacent digits and letters
a1
,1a
,A1
,1A
, - and acroynms
AAa
(as inHTTPRequest
).
For more precision, the from_case
method splits based on the word boundaries
of a particular case. For example, splitting from snake case will only use
underscores as word boundaries.
assert_eq!(
"2020 04 16 My Cat Cali",
"2020-04-16_my_cat_cali".to_case(Case::Title)
);
assert_eq!(
"2020-04-16 My Cat Cali",
"2020-04-16_my_cat_cali".from_case(Case::Snake).to_case(Case::Title)
);
This library can detect acronyms in camel-like strings. It also ignores any leading, trailing, or duplicate delimiters.
assert_eq!("io_stream", "IOStream".to_case(Case::Snake));
assert_eq!("my_json_parser", "myJSONParser".to_case(Case::Snake));
assert_eq!("weird_var_name", "__weird--var _name-".to_case(Case::Snake));
It also works non-ascii characters. However, no inferences on the language itself is made.
For instance, the digraph ij
in Dutch will not be capitalized, because it is represented
as two distinct Unicode characters. However, æ
would be capitalized. Accuracy with unicode
characters is done using the unicode-segmentation
crate, the sole dependency of this crate.
assert_eq!("granat-äpfel", "GranatÄpfel".to_case(Case::Kebab));
assert_eq!("Перспектива 24", "ПЕРСПЕКТИВА24".to_case(Case::Title));
// The example from str::to_lowercase documentation
let odysseus = "ὈΔΥΣΣΕΎΣ";
assert_eq!("ὀδυσσεύς", odysseus.to_case(Case::Lower));
By default, characters followed by digits and vice-versa are
considered word boundaries. In addition, any special ASCII characters (besides _
and -
)
are ignored.
assert_eq!("e_5150", "E5150".to_case(Case::Snake));
assert_eq!("10,000_days", "10,000Days".to_case(Case::Snake));
assert_eq!("HELLO, WORLD!", "Hello, world!".to_case(Case::Upper));
assert_eq!("One\ntwo\nthree", "ONE\nTWO\nTHREE".to_case(Case::Title));
You can also test what case a string is in.
assert!( "css-class-name".is_case(Case::Kebab));
assert!(!"css-class-name".is_case(Case::Snake));
assert!(!"UPPER_CASE_VAR".is_case(Case::Snake));
§Note on Accuracy
The Casing
methods from_case
and to_case
do not fail. Conversion to a case will always
succeed. However, the results can still be unexpected. Failure to detect any word boundaries
for a particular case means the entire string will be considered a single word.
use convert_case::{Case, Casing};
// Mistakenly parsing using Case::Snake
assert_eq!("My-kebab-var", "my-kebab-var".from_case(Case::Snake).to_case(Case::Title));
// Converts using an unexpected method
assert_eq!("my_kebab_like_variable", "myKebab-like-variable".to_case(Case::Snake));
§Boundary Specificity
It can be difficult to determine how to split a string into words. That is why this case
provides the from_case
functionality, but sometimes that isn’t enough
to meet a specific use case.
Say an identifier has the word 2D
, such as scale2D
. No exclusive usage of from_case
will
be enough to solve the problem. In this case we can further specify which boundaries to split
the string on. convert_case
provides some patterns for achieving this specificity.
We can specify what boundaries we want to split on using instances of the Boundary
struct.
use convert_case::{Boundary, Case, Casing};
// Not quite what we want
assert_eq!(
"scale_2_d",
"scale2D"
.from_case(Case::Camel)
.to_case(Case::Snake)
);
// Remove boundary from Case::Camel
assert_eq!(
"scale_2d",
"scale2D"
.from_case(Case::Camel)
.without_boundaries(&[Boundary::DIGIT_UPPER, Boundary::DIGIT_LOWER])
.to_case(Case::Snake)
);
// Write boundaries explicitly
assert_eq!(
"scale_2d",
"scale2D"
.with_boundaries(&[Boundary::LOWER_DIGIT])
.to_case(Case::Snake)
);
The Casing
trait provides initial methods, but any subsequent methods that do not resolve
the conversion return a StateConverter
struct. It contains similar methods as Casing
.
§Custom Boundaries
convert_case
provides a number of constants for boundaries associated with common cases.
But you can create your own boundary to split on other criteria. For simple, delimiter
based splits, use Boundary::from_delim
.
assert_eq!(
"Coolers Revenge",
"coolers.revenge"
.with_boundaries(&[Boundary::from_delim(".")])
.to_case(Case::Title)
)
For more complex boundaries, such as splitting based on the first character being a certain symbol and the second is lowercase, you can instantiate a boundary directly.
let at_then_letter = Boundary {
name: "AtLetter",
condition: |s, _| {
s.get(0).map(|c| *c == "@") == Some(true)
&& s.get(1).map(|c| *c == c.to_lowercase()) == Some(true)
},
arg: None,
start: 1,
len: 0,
};
assert_eq!(
"Name@ Domain",
"name@domain"
.with_boundaries(&[at_then_letter])
.to_case(Case::Title)
)
To learn more about building a boundary from scratch, read the Boundary
struct.
§Custom Case
Case has a special variant Case::Custom
that exposes the three components necessary
for case conversion. This allows you to define a custom case that behaves appropriately
in the .to_case
and .from_case
methods.
A common example might be a “dot case” that has lowercase letters and is delimited by periods. We could define this as follows.
use convert_case::{Case, Casing, pattern, Boundary};
let dot_case = Case::Custom {
boundaries: &[Boundary::from_delim(".")],
pattern: pattern::lowercase,
delim: ".",
};
assert_eq!(
"dot.case.var",
"Dot case var".to_case(dot_case)
)
And because we defined boundary conditions, this means .from_case
should also behave as expected.
assert_eq!(
"dotCaseVar",
"dot.case.var".from_case(dot_case).to_case(Case::Camel)
)
§Converter Struct
Case conversion takes place in two parts. The first splits an identifier into a series of words,
and the second joins the words back together. Each of these are steps are defined using the
.from_case
and .to_case
methods respectively.
Converter
is a struct that encapsulates the boundaries used for splitting and the pattern
and delimiter for mutating and joining. The convert
method will
apply the boundaries, pattern, and delimiter appropriately. This lets you define the
parameters for case conversion upfront.
use convert_case::{Converter, pattern};
let conv = Converter::new()
.set_pattern(pattern::camel)
.set_delim("_");
assert_eq!(
"my_Special_Case",
conv.convert("My Special Case")
)
For more details on how strings are converted, see the docs for Converter
.
§Random Feature
This feature adds two additional cases: [Case::Random
] and [Case::PseudoRandom
].
The random
feature depends on the rand
crate.
You can enable this feature by including the following in your Cargo.toml
.
[dependencies]
convert_case = { version = "^0.8.0", features = ["random"] }
Modules§
- pattern
- Functions for transforming a list of words.
Structs§
- Boundary
- Conditions for splitting an identifier into words.
- Converter
- The parameters for performing a case conversion.
- State
Converter - Holds information about parsing before converting into a case.
Enums§
- Case
- Defines the case of an identifier.
Traits§
- Casing
- Describes items that can be converted into a case. This trait is used
in conjunction with the
StateConverter
struct which is returned from a couple methods onCasing
.
Functions§
- split
- Split an identifier into a list of words using the list of boundaries.