summaryrefslogtreecommitdiffstats
path: root/vendor/convert_case
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
commit4547b622d8d29df964fa2914213088b148c498fc (patch)
tree9fc6b25f3c3add6b745be9a2400a6e96140046e9 /vendor/convert_case
parentReleasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz
rustc-4547b622d8d29df964fa2914213088b148c498fc.zip
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/convert_case')
-rw-r--r--vendor/convert_case/.cargo-checksum.json1
-rw-r--r--vendor/convert_case/Cargo.toml38
-rw-r--r--vendor/convert_case/README.md67
-rw-r--r--vendor/convert_case/src/case.rs212
-rw-r--r--vendor/convert_case/src/lib.rs337
-rw-r--r--vendor/convert_case/src/words.rs377
6 files changed, 1032 insertions, 0 deletions
diff --git a/vendor/convert_case/.cargo-checksum.json b/vendor/convert_case/.cargo-checksum.json
new file mode 100644
index 000000000..8d1329c0f
--- /dev/null
+++ b/vendor/convert_case/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"bbd8c27bde074b1c78539307cc2f1770efdaf890253cce4b9557cfdc32d4e605","README.md":"e2c239f152b34058cca9848c12a357eabd4af3df9d97bac11cfdcdd09f612b72","src/case.rs":"2c46022c41ce580a23f298d56599ae506ae905b12fecb55f5ed02db4c05a1362","src/lib.rs":"bcd9f76f198f739ec65d01b7186a12e7cbbaad8101728e4053b7f1a1efd3b3c8","src/words.rs":"b1cf6a6d1fa57297c7111e4f64fa4fed22b322a14fa20062aa79fddb34bc1651"},"package":"6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"} \ No newline at end of file
diff --git a/vendor/convert_case/Cargo.toml b/vendor/convert_case/Cargo.toml
new file mode 100644
index 000000000..9246a4b03
--- /dev/null
+++ b/vendor/convert_case/Cargo.toml
@@ -0,0 +1,38 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "convert_case"
+version = "0.4.0"
+authors = ["David Purdum <purdum41@gmail.com>"]
+description = "Convert strings into any case"
+readme = "README.md"
+keywords = ["casing", "case", "string"]
+categories = ["text-processing"]
+license = "MIT"
+repository = "https://github.com/rutrum/convert-case"
+[profile.release]
+lto = true
+codegen-units = 1
+panic = "abort"
+[dependencies.rand]
+version = "^0.7"
+optional = true
+[dev-dependencies.strum]
+version = "0.18.0"
+
+[dev-dependencies.strum_macros]
+version = "0.18.0"
+
+[features]
+random = ["rand"]
diff --git a/vendor/convert_case/README.md b/vendor/convert_case/README.md
new file mode 100644
index 000000000..706bb1f4a
--- /dev/null
+++ b/vendor/convert_case/README.md
@@ -0,0 +1,67 @@
+# Convert Case
+
+Converts to and from various cases.
+
+## Rust Library `convert_case`
+
+Convert case was written in Rust and is ready to be used inline with your rust code as a library.
+```{rust}
+use convert_case::{Case, Casing};
+
+assert_eq!("ronnieJamesDio", "Ronnie_James_dio".to_case(Case::Camel));
+assert_eq!("io_stream", "IOStream".to_case(Case::Snake));
+assert_eq!(
+ "2020-04-16 My Cat Cali",
+ "2020-04-16_my_cat_cali".from_case(Case::Snake).to_case(Case::Title)
+);
+```
+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. Bonus: this crate has _0 dependencies!_
+
+## Command Line Utility `ccase`
+
+The command line utility `ccase` was made to leverage the tools in the `convert_case` library.
+```
+$ ccase -t title super_mario_64
+Super Mario 64
+
+$ ccase -f snake -t title 2020-04-15_my_cat_cali
+2020-04-16 My Cat Cali
+
+$ ccase -t camel "convert to camel"
+convertToCamel
+```
+
+You can read more about the `ccase` executable in the [`ccase` directory](https://github.com/rutrum/convert-case/tree/master/ccase) within this repository.
+
+## Links
+
+| | `convert_case` | `ccase` |
+| --- | --- | --- |
+| Repository | [github](https://github.com/rutrum/convert-case) | [github](https://github.com/rutrum/convert-case/tree/master/ccase) |
+| Crate | [crates.io](https://crates.io/crates/convert_case) | [crates.io](https://crates.io/crates/ccase) |
+| Documentation | [docs.rs](https://docs.rs/convert_case) | |
+
+## Cases
+
+This is list of cases that convert\_case supports. Some cases are simply aliases of others. The "Random" and "PseudoRandom" cases are provided in the `convert_case` library with the "random" feature, and are automatically provided in the `ccase` binary.
+
+| Case | Example |
+| ---- | ------- |
+| Upper | MY VARIABLE NAME |
+| Lower | my variable name |
+| Title | My Variable Name |
+| Toggle | mY vARIABLE nAME |
+| Alternating | mY vArIaBlE nAmE |
+| Camel | myVariableName |
+| Pascal | MyVariableName |
+| UpperCamel | MyVariableName |
+| Snake | my\_variable\_name |
+| UpperSnake | MY\_VARIABLE\_NAME |
+| ScreamingSnake | MY\_VARIABLE\_NAME |
+| Kebab | my-variable-name |
+| Cobol | MY-VARIABLE-NAME |
+| Train | My-Variable-Name |
+| Flat | myvariablename |
+| UpperFlat | MYVARIABLENAME |
+| Random | MY vaRiabLe nAME |
+| PseudoRandom | mY VaRiAblE nAMe |
diff --git a/vendor/convert_case/src/case.rs b/vendor/convert_case/src/case.rs
new file mode 100644
index 000000000..f7a9c8c9c
--- /dev/null
+++ b/vendor/convert_case/src/case.rs
@@ -0,0 +1,212 @@
+#[cfg(test)]
+use strum_macros::EnumIter;
+
+/// Defines the type of casing a string can be.
+///
+/// ```
+/// use convert_case::{Case, Casing};
+///
+/// let super_mario_title: String = "super_mario_64".to_case(Case::Title);
+/// assert_eq!("Super Mario 64", super_mario_title);
+/// ```
+#[cfg_attr(test, derive(EnumIter))]
+#[derive(Eq, PartialEq, Clone, Copy, Debug)]
+pub enum Case {
+
+ /// Uppercase strings are delimited by spaces and all characters are uppercase.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("MY VARIABLE NAME", "My variable NAME".to_case(Case::Upper))
+ /// ```
+ Upper,
+
+ /// Lowercase strings are delimited by spaces and all characters are lowercase.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("my variable name", "My variable NAME".to_case(Case::Lower))
+ /// ```
+ Lower,
+
+ /// Title case strings are delimited by spaces. Only the leading character of
+ /// each word is uppercase. No inferences are made about language, so words
+ /// like "as", "to", and "for" will still be capitalized.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("My Variable Name", "My variable NAME".to_case(Case::Title))
+ /// ```
+ Title,
+
+ /// Toggle case strings are delimited by spaces. All characters are uppercase except
+ /// for the leading character of each word, which is lowercase.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("mY vARIABLE nAME", "My variable NAME".to_case(Case::Toggle))
+ /// ```
+ Toggle,
+
+ /// Camel case strings are lowercase, but for every word _except the first_ the
+ /// first letter is capitalized.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("myVariableName", "My variable NAME".to_case(Case::Camel))
+ /// ```
+ Camel,
+
+ /// Pascal case strings are lowercase, but for every word the
+ /// first letter is capitalized.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("MyVariableName", "My variable NAME".to_case(Case::Pascal))
+ /// ```
+ Pascal,
+
+ /// Upper camel case is an alternative name for Pascal case.
+ UpperCamel,
+
+ /// Snake case strings are delimited by underscores `_` and are all lowercase.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("my_variable_name", "My variable NAME".to_case(Case::Snake))
+ /// ```
+ Snake,
+
+ /// Upper snake case strings are delimited by underscores `_` and are all uppercase.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("MY_VARIABLE_NAME", "My variable NAME".to_case(Case::UpperSnake))
+ /// ```
+ UpperSnake,
+
+ /// Screaming snake case is an alternative name for upper snake case.
+ ScreamingSnake,
+
+ /// Kebab case strings are delimited by hyphens `-` and are all lowercase.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("my-variable-name", "My variable NAME".to_case(Case::Kebab))
+ /// ```
+ Kebab,
+
+ /// Cobol case strings are delimited by hyphens `-` and are all uppercase.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("MY-VARIABLE-NAME", "My variable NAME".to_case(Case::Cobol))
+ /// ```
+ Cobol,
+
+ /// Train case strings are delimited by hyphens `-`. All characters are lowercase
+ /// except for the leading character of each word.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("My-Variable-Name", "My variable NAME".to_case(Case::Train))
+ /// ```
+ Train,
+
+ /// Flat case strings are all lowercase, with no delimiter. Converting to
+ /// this case is **lossy**. That is, word boundaries are lost.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("myvariablename", "My variable NAME".to_case(Case::Flat))
+ /// ```
+ Flat,
+
+ /// Upper flat case strings are all uppercase, with no delimiter. Converting to
+ /// this case is **lossy**. That is, word boundaries are lost.
+ ///
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("MYVARIABLENAME", "My variable NAME".to_case(Case::UpperFlat))
+ /// ```
+ UpperFlat,
+
+ /// Alternating case strings are delimited by spaces. Characters alternate between uppercase
+ /// and lowercase.
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// assert_eq!("mY vArIaBlE nAmE", "My variable NAME".to_case(Case::Alternating));
+ /// ```
+ Alternating,
+
+ /// Random case strings are delimited by spaces and characters are
+ /// randomly upper case or lower case. This uses the `rand` crate
+ /// and is only available with the "random" feature.
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// let new = "My variable NAME".to_case(Case::Random);
+ /// ```
+ /// `new` could be "My vaRIAbLE nAme" for example.
+ #[cfg(feature = "random")]
+ Random,
+
+ /// Pseudo-random case strings are delimited by spaces and characters are randomly
+ /// upper case or lower case, but there will never more than two consecutive lower
+ /// case or upper case letters in a row. This uses the `rand` crate and is
+ /// only available with the "random" feature.
+ /// ```
+ /// use convert_case::{Case, Casing};
+ /// let new = "My variable NAME".to_case(Case::Random);
+ /// ```
+ /// `new` could be "mY vArIAblE NamE" for example.
+ #[cfg(feature = "random")]
+ PseudoRandom,
+}
+
+impl Case {
+ // Created to avoid using the EnumIter trait from strum in
+ // final library. A test confirms that all cases are listed here.
+ /// Returns a vector with all case enum variants. This was
+ /// created for use in the `ccase` binary.
+ pub fn all_cases() -> Vec<Case> {
+ use Case::*;
+ vec![
+ Upper,
+ Lower,
+ Title,
+ Toggle,
+ Camel,
+ Pascal,
+ UpperCamel,
+ Snake,
+ UpperSnake,
+ ScreamingSnake,
+ Kebab,
+ Cobol,
+ Train,
+ Flat,
+ UpperFlat,
+ Alternating,
+
+ #[cfg(feature = "random")]
+ Random,
+ #[cfg(feature = "random")]
+ PseudoRandom,
+ ]
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use super::*;
+ use strum::IntoEnumIterator;
+
+ #[test]
+ fn all_cases_in_iter() {
+ let all = Case::all_cases();
+ for case in Case::iter() {
+ assert!(all.contains(&case));
+ }
+ }
+}
diff --git a/vendor/convert_case/src/lib.rs b/vendor/convert_case/src/lib.rs
new file mode 100644
index 000000000..29711655f
--- /dev/null
+++ b/vendor/convert_case/src/lib.rs
@@ -0,0 +1,337 @@
+//! Converts to and from various cases.
+//!
+//! # Command Line Utility `ccase`
+//!
+//! Since version "0.3.0" this crate is just a case conversion _library_. The command line utility
+//! that uses the tools in this library has been moved to the `ccase` crate. You can read about it
+//! at the [github repository](https://github.com/rutrum/convert-case/tree/master/ccase).
+//!
+//! # Rust Library
+//!
+//! Provides a [`Case`](enum.Case.html) enum which defines a variety of cases to convert into.
+//! A `Case` can be used with an item that implements the [`Casing`](trait.Casing.html) trait,
+//! which allows the item to be converted to a given case.
+//!
+//! You can convert a string or string slice 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 all word boundaries, that is
+//! * space characters ` `,
+//! * underscores `_`,
+//! * hyphens `-`,
+//! * and changes in capitalization `aA`.
+//!
+//! For more accuracy, the `from_case` method splits based on the word boundaries
+//! of a particular case. For example, splitting from snake case will only treat
+//! underscores as word boundaries.
+//! ```
+//! use convert_case::{Case, Casing};
+//!
+//! 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)
+//! );
+//! ```
+//!
+//! By default (and when converting from camel case or similar cases) `convert_case`
+//! will detect acronyms. It also ignores any leading, trailing, or deplicate delimeters.
+//! ```
+//! use convert_case::{Case, Casing};
+//!
+//! 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 diagraph `ij` in dutch will not be capitalized, because it is represented
+//! as two distinct unicode characters. However, `æ` would be capitalized.
+//! ```
+//! use convert_case::{Case, Casing};
+//!
+//! assert_eq!("granat-äpfel", "GranatÄpfel".to_case(Case::Kebab));
+//!
+//! // The example from str::to_lowercase documentation
+//! let odysseus = "ὈΔΥΣΣΕΎΣ";
+//! assert_eq!("ὀδυσσεύς", odysseus.to_case(Case::Lower));
+//! ```
+//!
+//! For the purposes of case conversion, characters followed by numerics and vice-versa are
+//! considered word boundaries. In addition, any special ascii characters (besides `_` and `-`)
+//! are ignored.
+//! ```
+//! use convert_case::{Case, Casing};
+//!
+//! 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));
+//! ```
+//!
+//! # 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));
+//! ```
+//!
+//! # Random Feature
+//!
+//! To ensure this library had zero dependencies, randomness was moved to the _random_ feature,
+//! which requires the `rand` crate. You can enable this feature by including the
+//! following in your `Cargo.toml`.
+//! ```{toml}
+//! [dependencies]
+//! convert_case = { version = "^0.3, features = ["random"] }
+//! ```
+//! This will add two additional cases: Random and PseudoRandom. You can read about their
+//! construction in the [Case enum](enum.Case.html).
+
+mod case;
+mod words;
+pub use case::Case;
+use words::Words;
+
+/// Describes items that can be converted into a case.
+///
+/// Implemented for string slices `&str` and owned strings `String`.
+pub trait Casing {
+ /// References `self` and converts to the given case.
+ fn to_case(&self, case: Case) -> String;
+
+ /// Creates a `FromCasing` struct, which saves information about
+ /// how to parse `self` before converting to a case.
+ fn from_case(&self, case: Case) -> FromCasing;
+}
+
+impl Casing for str {
+ fn to_case(&self, case: Case) -> String {
+ Words::new(self).into_case(case)
+ }
+
+ fn from_case(&self, case: Case) -> FromCasing {
+ FromCasing::new(self.to_string(), case)
+ }
+}
+
+impl Casing for String {
+ fn to_case(&self, case: Case) -> String {
+ Words::new(self).into_case(case)
+ }
+
+ fn from_case(&self, case: Case) -> FromCasing {
+ FromCasing::new(self.to_string(), case)
+ }
+}
+
+/// Holds information about parsing before converting into a case.
+///
+/// This struct is used when invoking the `from_case` method on
+/// `Casing`. `FromCasing` also implements `Casing`.
+/// ```
+/// use convert_case::{Case, Casing};
+///
+/// let title = "ninety-nine_problems".from_case(Case::Snake).to_case(Case::Title);
+/// assert_eq!("Ninety-nine Problems", title);
+/// ```
+pub struct FromCasing {
+ name: String,
+ case: Case,
+}
+
+impl FromCasing {
+ const fn new(name: String, case: Case) -> Self {
+ Self { name, case }
+ }
+}
+
+impl Casing for FromCasing {
+ fn to_case(&self, case: Case) -> String {
+ Words::from_casing(&self.name, self.case).into_case(case)
+ }
+
+ fn from_case(&self, case: Case) -> Self {
+ Self::new(self.name.to_string(), case)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use strum::IntoEnumIterator;
+
+ #[test]
+ fn lossless_against_lossless() {
+ let examples = vec![
+ (Case::Lower, "my variable 22 name"),
+ (Case::Upper, "MY VARIABLE 22 NAME"),
+ (Case::Title, "My Variable 22 Name"),
+ (Case::Camel, "myVariable22Name"),
+ (Case::Pascal, "MyVariable22Name"),
+ (Case::Snake, "my_variable_22_name"),
+ (Case::ScreamingSnake, "MY_VARIABLE_22_NAME"),
+ (Case::Kebab, "my-variable-22-name"),
+ (Case::Cobol, "MY-VARIABLE-22-NAME"),
+ (Case::Toggle, "mY vARIABLE 22 nAME"),
+ (Case::Train, "My-Variable-22-Name"),
+ (Case::Alternating, "mY vArIaBlE 22 nAmE"),
+ ];
+
+ for (case_a, str_a) in examples.iter() {
+ for (case_b, str_b) in examples.iter() {
+ assert_eq!(*str_a, str_b.from_case(*case_b).to_case(*case_a))
+ }
+ }
+ }
+
+ #[test]
+ fn obvious_default_parsing() {
+ let examples = vec![
+ "SuperMario64Game",
+ "super-mario64-game",
+ "superMario64 game",
+ "Super Mario 64_game",
+ "SUPERMario 64-game",
+ "super_mario-64 game",
+ ];
+
+ for example in examples {
+ assert_eq!("super_mario_64_game", example.to_case(Case::Snake));
+ }
+ }
+
+ #[test]
+ fn multiline_strings() {
+ assert_eq!(
+ "One\ntwo\nthree",
+ "one\ntwo\nthree".to_case(Case::Title)
+ );
+ }
+
+ #[test]
+ fn camel_case_acroynms() {
+ assert_eq!(
+ "xml_http_request",
+ "XMLHttpRequest".from_case(Case::Camel).to_case(Case::Snake)
+ );
+ assert_eq!(
+ "xml_http_request",
+ "XMLHttpRequest"
+ .from_case(Case::UpperCamel)
+ .to_case(Case::Snake)
+ );
+ assert_eq!(
+ "xml_http_request",
+ "XMLHttpRequest"
+ .from_case(Case::Pascal)
+ .to_case(Case::Snake)
+ );
+ }
+
+ #[test]
+ fn leading_tailing_delimeters() {
+ assert_eq!(
+ "leading_underscore",
+ "_leading_underscore"
+ .from_case(Case::Snake)
+ .to_case(Case::Snake)
+ );
+ assert_eq!(
+ "tailing_underscore",
+ "tailing_underscore_"
+ .from_case(Case::Snake)
+ .to_case(Case::Snake)
+ );
+ assert_eq!(
+ "leading_hyphen",
+ "-leading-hyphen"
+ .from_case(Case::Kebab)
+ .to_case(Case::Snake)
+ );
+ assert_eq!(
+ "tailing_hyphen",
+ "tailing-hyphen-"
+ .from_case(Case::Kebab)
+ .to_case(Case::Snake)
+ );
+ }
+
+ #[test]
+ fn double_delimeters() {
+ assert_eq!(
+ "many_underscores",
+ "many___underscores"
+ .from_case(Case::Snake)
+ .to_case(Case::Snake)
+ );
+ assert_eq!(
+ "many-underscores",
+ "many---underscores"
+ .from_case(Case::Kebab)
+ .to_case(Case::Kebab)
+ );
+ }
+
+ #[test]
+ fn early_word_boundaries() {
+ assert_eq!(
+ "a_bagel",
+ "aBagel".from_case(Case::Camel).to_case(Case::Snake)
+ );
+ }
+
+ #[test]
+ fn late_word_boundaries() {
+ assert_eq!(
+ "team_a",
+ "teamA".from_case(Case::Camel).to_case(Case::Snake)
+ );
+ }
+
+ #[test]
+ fn empty_string() {
+ for (case_a, case_b) in Case::iter().zip(Case::iter()) {
+ assert_eq!("", "".from_case(case_a).to_case(case_b));
+ }
+ }
+
+ #[test]
+ fn owned_string() {
+ assert_eq!(
+ "test_variable",
+ String::from("TestVariable").to_case(Case::Snake)
+ )
+ }
+
+ #[test]
+ fn default_all_boundaries() {
+ assert_eq!(
+ "abc_abc_abc_abc_abc_abc",
+ "ABC-abc_abcAbc ABCAbc".to_case(Case::Snake)
+ );
+ }
+
+ #[test]
+ fn alternating_ignore_symbols() {
+ assert_eq!("tHaT's", "that's".to_case(Case::Alternating));
+ }
+}
diff --git a/vendor/convert_case/src/words.rs b/vendor/convert_case/src/words.rs
new file mode 100644
index 000000000..e7640107a
--- /dev/null
+++ b/vendor/convert_case/src/words.rs
@@ -0,0 +1,377 @@
+use crate::Case;
+
+#[cfg(feature = "random")]
+use rand::prelude::*;
+
+pub(super) struct Words {
+ words: Vec<String>,
+}
+
+impl Words {
+ pub fn new(name: &str) -> Self {
+ let words = name
+ .split(|c| "-_ ".contains(c))
+ .flat_map(Self::split_camel)
+ .filter(|s| !s.is_empty())
+ .collect();
+ Words { words }
+ }
+
+ pub fn from_casing(name: &str, case: Case) -> Self {
+ use Case::*;
+ let words = match case {
+ Title | Upper | Lower | Toggle | Alternating => name
+ .split_ascii_whitespace()
+ .map(ToString::to_string)
+ .collect(),
+ Kebab | Cobol | Train => name
+ .split('-')
+ .map(ToString::to_string)
+ .filter(|s| !s.is_empty())
+ .collect(),
+ Snake | UpperSnake | ScreamingSnake => name
+ .split('_')
+ .map(ToString::to_string)
+ .filter(|s| !s.is_empty())
+ .collect(),
+ Pascal | Camel | UpperCamel => Self::split_camel(name),
+ Flat | UpperFlat => vec![name.to_string()],
+
+ // Same behavior as title, upper, etc.
+ #[cfg(feature = "random")]
+ Random | PseudoRandom => name
+ .split_ascii_whitespace()
+ .map(ToString::to_string)
+ .collect(),
+ };
+ Self { words }
+ }
+
+ fn split_camel(name: &str) -> Vec<String> {
+ let left_iter = name.chars();
+ let mid_iter = name.chars().skip(1);
+ let right_iter = name.chars().skip(2);
+
+ let mut split_indices = left_iter
+ .zip(mid_iter)
+ .zip(right_iter)
+ .enumerate()
+ .filter(|(_, ((f, s), t))| Self::three_char_is_boundary(*f, *s, *t))
+ .map(|(i, _)| i + 1)
+ .collect::<Vec<usize>>();
+
+ // Check for boundary in the last two characters
+ // Can be rewritten nicer (use fold)
+ let mut backwards_seek = name.chars().rev();
+ let last = backwards_seek.next();
+ let second_last = backwards_seek.next();
+ if let (Some(a), Some(b)) = (second_last, last) {
+ if Self::two_char_is_boundary(a, b) {
+ split_indices.push(name.len() - 1);
+ }
+ }
+
+ Self::split_at_indices(name, split_indices)
+ }
+
+ /// Allowed boundaries are (lower upper) (digit (!digit and !punc)) ((!digit and !punc) digit).
+ fn two_char_is_boundary(f: char, s: char) -> bool {
+ (f.is_lowercase() && s.is_uppercase())
+ || (f.is_ascii_digit() && !(s.is_ascii_digit() || s.is_ascii_punctuation()))
+ || (!(f.is_ascii_digit() || f.is_ascii_punctuation()) && s.is_ascii_digit())
+ }
+
+ /// Checks if three characters are the end of an acronym, otherwise
+ /// calls `two_char_is_boundary`.
+ fn three_char_is_boundary(f: char, s: char, t: char) -> bool {
+ (f.is_uppercase() && s.is_uppercase() && t.is_lowercase())
+ || Self::two_char_is_boundary(f, s)
+ }
+
+ fn split_at_indices(name: &str, indices: Vec<usize>) -> Vec<String> {
+ let mut words = Vec::new();
+
+ let mut first = name;
+ let mut second;
+ for &x in indices.iter().rev() {
+ let pair = first.split_at(x);
+ first = pair.0;
+ second = pair.1;
+ words.push(second);
+ }
+ words.push(first);
+
+ words.iter().rev().map(ToString::to_string).collect()
+ }
+
+ pub fn into_case(mut self, case: Case) -> String {
+ use Case::*;
+ match case {
+ Camel => {
+ self.make_camel_case();
+ self.join("")
+ }
+ Title => {
+ self.capitalize_first_letter();
+ self.join(" ")
+ }
+ Pascal | UpperCamel => {
+ self.capitalize_first_letter();
+ self.join("")
+ }
+ Toggle => {
+ self.lower_first_letter();
+ self.join(" ")
+ }
+ Snake => {
+ self.make_lowercase();
+ self.join("_")
+ }
+ Cobol => {
+ self.make_uppercase();
+ self.join("-")
+ }
+ Kebab => {
+ self.make_lowercase();
+ self.join("-")
+ }
+ UpperSnake | ScreamingSnake => {
+ self.make_uppercase();
+ self.join("_")
+ }
+ Lower => {
+ self.make_lowercase();
+ self.join(" ")
+ }
+ Upper => {
+ self.make_uppercase();
+ self.join(" ")
+ }
+ Flat => {
+ self.make_lowercase();
+ self.join("")
+ }
+ Train => {
+ self.capitalize_first_letter();
+ self.join("-")
+ }
+ UpperFlat => {
+ self.make_uppercase();
+ self.join("")
+ }
+ Alternating => {
+ self.make_alternating();
+ self.join(" ")
+ }
+ #[cfg(feature = "random")]
+ Random => {
+ self.randomize();
+ self.join(" ")
+ }
+ #[cfg(feature = "random")]
+ PseudoRandom => {
+ self.pseudo_randomize();
+ self.join(" ")
+ }
+ }
+ }
+
+ // Randomly picks whether to be upper case or lower case
+ #[cfg(feature = "random")]
+ fn randomize(&mut self) {
+ let mut rng = rand::thread_rng();
+ self.words = self
+ .words
+ .iter()
+ .map(|word| {
+ word.chars()
+ .map(|letter| {
+ if rng.gen::<f32>() > 0.5 {
+ letter.to_uppercase().to_string()
+ } else {
+ letter.to_lowercase().to_string()
+ }
+ })
+ .collect()
+ })
+ .collect();
+ }
+
+ // Randomly selects patterns: [upper, lower] or [lower, upper]
+ // for a more random feeling pattern.
+ #[cfg(feature = "random")]
+ fn pseudo_randomize(&mut self) {
+ let mut rng = rand::thread_rng();
+
+ // Keeps track of when to alternate
+ let mut alt: Option<bool> = None;
+ self.words = self
+ .words
+ .iter()
+ .map(|word| {
+ word.chars()
+ .map(|letter| {
+ match alt {
+ // No existing pattern, start one
+ None => {
+ if rng.gen::<f32>() > 0.5 {
+ alt = Some(false); // Make the next char lower
+ letter.to_uppercase().to_string()
+ } else {
+ alt = Some(true); // Make the next char upper
+ letter.to_lowercase().to_string()
+ }
+ }
+ // Existing pattern, do what it says
+ Some(upper) => {
+ alt = None;
+ if upper {
+ letter.to_uppercase().to_string()
+ } else {
+ letter.to_lowercase().to_string()
+ }
+ }
+ }
+ })
+ .collect()
+ })
+ .collect();
+ }
+
+ fn make_camel_case(&mut self) {
+ self.words = self
+ .words
+ .iter()
+ .enumerate()
+ .map(|(i, word)| {
+ if i != 0 {
+ let mut chars = word.chars();
+ if let Some(a) = chars.next() {
+ a.to_uppercase()
+ .chain(chars.as_str().to_lowercase().chars())
+ .collect()
+ } else {
+ String::new()
+ }
+ } else {
+ word.to_lowercase()
+ }
+ })
+ .collect();
+ }
+
+ fn make_alternating(&mut self) {
+ let mut upper = false;
+ self.words = self
+ .words
+ .iter()
+ .map(|word| {
+ word.chars()
+ .map(|letter| {
+ if letter.is_uppercase() || letter.is_lowercase() {
+ if upper {
+ upper = false;
+ letter.to_uppercase().to_string()
+ } else {
+ upper = true;
+ letter.to_lowercase().to_string()
+ }
+ } else {
+ letter.to_string()
+ }
+ })
+ .collect()
+ })
+ .collect();
+ }
+
+ fn make_uppercase(&mut self) {
+ self.words = self.words.iter().map(|word| word.to_uppercase()).collect();
+ }
+
+ fn make_lowercase(&mut self) {
+ self.words = self.words.iter().map(|word| word.to_lowercase()).collect();
+ }
+
+ fn capitalize_first_letter(&mut self) {
+ self.words = self
+ .words
+ .iter()
+ .map(|word| {
+ let mut chars = word.chars();
+ if let Some(a) = chars.next() {
+ a.to_uppercase()
+ .chain(chars.as_str().to_lowercase().chars())
+ .collect()
+ } else {
+ String::new()
+ }
+ })
+ .collect();
+ }
+
+ fn lower_first_letter(&mut self) {
+ self.words = self
+ .words
+ .iter()
+ .map(|word| {
+ let mut chars = word.chars();
+ if let Some(a) = chars.next() {
+ a.to_lowercase()
+ .chain(chars.as_str().to_uppercase().chars())
+ .collect()
+ } else {
+ String::new()
+ }
+ })
+ .collect();
+ }
+
+ // Alternative: construct [my, -, variable, -, name] then collect
+ fn join(self, delim: &str) -> String {
+ self.words
+ .iter()
+ .enumerate()
+ .map(|(i, val)| {
+ if i == 0 {
+ val.to_owned()
+ } else {
+ delim.to_owned() + val
+ }
+ })
+ .collect()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn correct_two_char_boundaries() {
+ assert!(!Words::two_char_is_boundary('a', 'a'));
+ assert!(Words::two_char_is_boundary('a', 'A'));
+ assert!(Words::two_char_is_boundary('a', '5'));
+ assert!(!Words::two_char_is_boundary('a', ','));
+ assert!(!Words::two_char_is_boundary('A', 'A'));
+ assert!(!Words::two_char_is_boundary('A', 'a'));
+ assert!(Words::two_char_is_boundary('A', '5'));
+ assert!(!Words::two_char_is_boundary('A', ','));
+ assert!(Words::two_char_is_boundary('5', 'a'));
+ assert!(Words::two_char_is_boundary('5', 'A'));
+ assert!(!Words::two_char_is_boundary('5', '5'));
+ assert!(!Words::two_char_is_boundary('5', ','));
+ assert!(!Words::two_char_is_boundary(',', 'a'));
+ assert!(!Words::two_char_is_boundary(',', 'A'));
+ assert!(!Words::two_char_is_boundary(',', '5'));
+ assert!(!Words::two_char_is_boundary(',', ','));
+ }
+
+ #[test]
+ fn correct_three_char_boundaries() {
+ assert!(Words::three_char_is_boundary('A', 'A', 'a'));
+ assert!(!Words::three_char_is_boundary('A', 'a', 'a'));
+ assert!(!Words::three_char_is_boundary('A', 'a', 'A'));
+ assert!(!Words::three_char_is_boundary('A', 'A', '3'));
+ }
+}