]> git.proxmox.com Git - cargo.git/blob - vendor/serde_derive/src/internals/case.rs
New upstream version 0.33.0
[cargo.git] / vendor / serde_derive / src / internals / case.rs
1 //! Code to convert the Rust-styled field/variant (e.g. `my_field`, `MyType`) to the
2 //! case of the source (e.g. `my-field`, `MY_FIELD`).
3
4 // See https://users.rust-lang.org/t/psa-dealing-with-warning-unused-import-std-ascii-asciiext-in-today-s-nightly/13726
5 #[allow(deprecated, unused_imports)]
6 use std::ascii::AsciiExt;
7
8 use std::str::FromStr;
9
10 use self::RenameRule::*;
11
12 /// The different possible ways to change case of fields in a struct, or variants in an enum.
13 #[derive(Copy, Clone, PartialEq)]
14 pub enum RenameRule {
15 /// Don't apply a default rename rule.
16 None,
17 /// Rename direct children to "lowercase" style.
18 LowerCase,
19 /// Rename direct children to "UPPERCASE" style.
20 UPPERCASE,
21 /// Rename direct children to "PascalCase" style, as typically used for
22 /// enum variants.
23 PascalCase,
24 /// Rename direct children to "camelCase" style.
25 CamelCase,
26 /// Rename direct children to "snake_case" style, as commonly used for
27 /// fields.
28 SnakeCase,
29 /// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly
30 /// used for constants.
31 ScreamingSnakeCase,
32 /// Rename direct children to "kebab-case" style.
33 KebabCase,
34 /// Rename direct children to "SCREAMING-KEBAB-CASE" style.
35 ScreamingKebabCase,
36 }
37
38 impl RenameRule {
39 /// Apply a renaming rule to an enum variant, returning the version expected in the source.
40 pub fn apply_to_variant(&self, variant: &str) -> String {
41 match *self {
42 None | PascalCase => variant.to_owned(),
43 LowerCase => variant.to_ascii_lowercase(),
44 UPPERCASE => variant.to_ascii_uppercase(),
45 CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..],
46 SnakeCase => {
47 let mut snake = String::new();
48 for (i, ch) in variant.char_indices() {
49 if i > 0 && ch.is_uppercase() {
50 snake.push('_');
51 }
52 snake.push(ch.to_ascii_lowercase());
53 }
54 snake
55 }
56 ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
57 KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
58 ScreamingKebabCase => ScreamingSnakeCase
59 .apply_to_variant(variant)
60 .replace('_', "-"),
61 }
62 }
63
64 /// Apply a renaming rule to a struct field, returning the version expected in the source.
65 pub fn apply_to_field(&self, field: &str) -> String {
66 match *self {
67 None | LowerCase | SnakeCase => field.to_owned(),
68 UPPERCASE => field.to_ascii_uppercase(),
69 PascalCase => {
70 let mut pascal = String::new();
71 let mut capitalize = true;
72 for ch in field.chars() {
73 if ch == '_' {
74 capitalize = true;
75 } else if capitalize {
76 pascal.push(ch.to_ascii_uppercase());
77 capitalize = false;
78 } else {
79 pascal.push(ch);
80 }
81 }
82 pascal
83 }
84 CamelCase => {
85 let pascal = PascalCase.apply_to_field(field);
86 pascal[..1].to_ascii_lowercase() + &pascal[1..]
87 }
88 ScreamingSnakeCase => field.to_ascii_uppercase(),
89 KebabCase => field.replace('_', "-"),
90 ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"),
91 }
92 }
93 }
94
95 impl FromStr for RenameRule {
96 type Err = ();
97
98 fn from_str(rename_all_str: &str) -> Result<Self, Self::Err> {
99 match rename_all_str {
100 "lowercase" => Ok(LowerCase),
101 "UPPERCASE" => Ok(UPPERCASE),
102 "PascalCase" => Ok(PascalCase),
103 "camelCase" => Ok(CamelCase),
104 "snake_case" => Ok(SnakeCase),
105 "SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase),
106 "kebab-case" => Ok(KebabCase),
107 "SCREAMING-KEBAB-CASE" => Ok(ScreamingKebabCase),
108 _ => Err(()),
109 }
110 }
111 }
112
113 #[test]
114 fn rename_variants() {
115 for &(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab) in &[
116 (
117 "Outcome", "outcome", "OUTCOME", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME",
118 ),
119 (
120 "VeryTasty",
121 "verytasty",
122 "VERYTASTY",
123 "veryTasty",
124 "very_tasty",
125 "VERY_TASTY",
126 "very-tasty",
127 "VERY-TASTY",
128 ),
129 ("A", "a", "A", "a", "a", "A", "a", "A"),
130 ("Z42", "z42", "Z42", "z42", "z42", "Z42", "z42", "Z42"),
131 ] {
132 assert_eq!(None.apply_to_variant(original), original);
133 assert_eq!(LowerCase.apply_to_variant(original), lower);
134 assert_eq!(UPPERCASE.apply_to_variant(original), upper);
135 assert_eq!(PascalCase.apply_to_variant(original), original);
136 assert_eq!(CamelCase.apply_to_variant(original), camel);
137 assert_eq!(SnakeCase.apply_to_variant(original), snake);
138 assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
139 assert_eq!(KebabCase.apply_to_variant(original), kebab);
140 assert_eq!(
141 ScreamingKebabCase.apply_to_variant(original),
142 screaming_kebab
143 );
144 }
145 }
146
147 #[test]
148 fn rename_fields() {
149 for &(original, upper, pascal, camel, screaming, kebab, screaming_kebab) in &[
150 (
151 "outcome", "OUTCOME", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME",
152 ),
153 (
154 "very_tasty",
155 "VERY_TASTY",
156 "VeryTasty",
157 "veryTasty",
158 "VERY_TASTY",
159 "very-tasty",
160 "VERY-TASTY",
161 ),
162 ("a", "A", "A", "a", "A", "a", "A"),
163 ("z42", "Z42", "Z42", "z42", "Z42", "z42", "Z42"),
164 ] {
165 assert_eq!(None.apply_to_field(original), original);
166 assert_eq!(UPPERCASE.apply_to_field(original), upper);
167 assert_eq!(PascalCase.apply_to_field(original), pascal);
168 assert_eq!(CamelCase.apply_to_field(original), camel);
169 assert_eq!(SnakeCase.apply_to_field(original), original);
170 assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
171 assert_eq!(KebabCase.apply_to_field(original), kebab);
172 assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);
173 }
174 }