]> git.proxmox.com Git - cargo.git/blob - vendor/serde_derive/src/internals/case.rs
New upstream version 0.52.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::fmt::{self, Debug, Display};
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 static RENAME_RULES: &[(&str, RenameRule)] = &[
39 ("lowercase", LowerCase),
40 ("UPPERCASE", UpperCase),
41 ("PascalCase", PascalCase),
42 ("camelCase", CamelCase),
43 ("snake_case", SnakeCase),
44 ("SCREAMING_SNAKE_CASE", ScreamingSnakeCase),
45 ("kebab-case", KebabCase),
46 ("SCREAMING-KEBAB-CASE", ScreamingKebabCase),
47 ];
48
49 impl RenameRule {
50 pub fn from_str(rename_all_str: &str) -> Result<Self, ParseError> {
51 for (name, rule) in RENAME_RULES {
52 if rename_all_str == *name {
53 return Ok(*rule);
54 }
55 }
56 Err(ParseError {
57 unknown: rename_all_str,
58 })
59 }
60
61 /// Apply a renaming rule to an enum variant, returning the version expected in the source.
62 pub fn apply_to_variant(&self, variant: &str) -> String {
63 match *self {
64 None | PascalCase => variant.to_owned(),
65 LowerCase => variant.to_ascii_lowercase(),
66 UpperCase => variant.to_ascii_uppercase(),
67 CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..],
68 SnakeCase => {
69 let mut snake = String::new();
70 for (i, ch) in variant.char_indices() {
71 if i > 0 && ch.is_uppercase() {
72 snake.push('_');
73 }
74 snake.push(ch.to_ascii_lowercase());
75 }
76 snake
77 }
78 ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
79 KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
80 ScreamingKebabCase => ScreamingSnakeCase
81 .apply_to_variant(variant)
82 .replace('_', "-"),
83 }
84 }
85
86 /// Apply a renaming rule to a struct field, returning the version expected in the source.
87 pub fn apply_to_field(&self, field: &str) -> String {
88 match *self {
89 None | LowerCase | SnakeCase => field.to_owned(),
90 UpperCase => field.to_ascii_uppercase(),
91 PascalCase => {
92 let mut pascal = String::new();
93 let mut capitalize = true;
94 for ch in field.chars() {
95 if ch == '_' {
96 capitalize = true;
97 } else if capitalize {
98 pascal.push(ch.to_ascii_uppercase());
99 capitalize = false;
100 } else {
101 pascal.push(ch);
102 }
103 }
104 pascal
105 }
106 CamelCase => {
107 let pascal = PascalCase.apply_to_field(field);
108 pascal[..1].to_ascii_lowercase() + &pascal[1..]
109 }
110 ScreamingSnakeCase => field.to_ascii_uppercase(),
111 KebabCase => field.replace('_', "-"),
112 ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"),
113 }
114 }
115 }
116
117 pub struct ParseError<'a> {
118 unknown: &'a str,
119 }
120
121 impl<'a> Display for ParseError<'a> {
122 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123 f.write_str("unknown rename rule `rename_all = ")?;
124 Debug::fmt(self.unknown, f)?;
125 f.write_str("`, expected one of ")?;
126 for (i, (name, _rule)) in RENAME_RULES.iter().enumerate() {
127 if i > 0 {
128 f.write_str(", ")?;
129 }
130 Debug::fmt(name, f)?;
131 }
132 Ok(())
133 }
134 }
135
136 #[test]
137 fn rename_variants() {
138 for &(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab) in &[
139 (
140 "Outcome", "outcome", "OUTCOME", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME",
141 ),
142 (
143 "VeryTasty",
144 "verytasty",
145 "VERYTASTY",
146 "veryTasty",
147 "very_tasty",
148 "VERY_TASTY",
149 "very-tasty",
150 "VERY-TASTY",
151 ),
152 ("A", "a", "A", "a", "a", "A", "a", "A"),
153 ("Z42", "z42", "Z42", "z42", "z42", "Z42", "z42", "Z42"),
154 ] {
155 assert_eq!(None.apply_to_variant(original), original);
156 assert_eq!(LowerCase.apply_to_variant(original), lower);
157 assert_eq!(UpperCase.apply_to_variant(original), upper);
158 assert_eq!(PascalCase.apply_to_variant(original), original);
159 assert_eq!(CamelCase.apply_to_variant(original), camel);
160 assert_eq!(SnakeCase.apply_to_variant(original), snake);
161 assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
162 assert_eq!(KebabCase.apply_to_variant(original), kebab);
163 assert_eq!(
164 ScreamingKebabCase.apply_to_variant(original),
165 screaming_kebab
166 );
167 }
168 }
169
170 #[test]
171 fn rename_fields() {
172 for &(original, upper, pascal, camel, screaming, kebab, screaming_kebab) in &[
173 (
174 "outcome", "OUTCOME", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME",
175 ),
176 (
177 "very_tasty",
178 "VERY_TASTY",
179 "VeryTasty",
180 "veryTasty",
181 "VERY_TASTY",
182 "very-tasty",
183 "VERY-TASTY",
184 ),
185 ("a", "A", "A", "a", "A", "a", "A"),
186 ("z42", "Z42", "Z42", "z42", "Z42", "z42", "Z42"),
187 ] {
188 assert_eq!(None.apply_to_field(original), original);
189 assert_eq!(UpperCase.apply_to_field(original), upper);
190 assert_eq!(PascalCase.apply_to_field(original), pascal);
191 assert_eq!(CamelCase.apply_to_field(original), camel);
192 assert_eq!(SnakeCase.apply_to_field(original), original);
193 assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
194 assert_eq!(KebabCase.apply_to_field(original), kebab);
195 assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);
196 }
197 }