serde_derive_state_internals/
case.rs

1// Copyright 2017 Serde Developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9// See https://users.rust-lang.org/t/psa-dealing-with-warning-unused-import-std-ascii-asciiext-in-today-s-nightly/13726
10#[allow(unused_imports)]
11use std::ascii::AsciiExt;
12
13use std::str::FromStr;
14
15use self::RenameRule::*;
16
17#[derive(Debug, PartialEq)]
18pub enum RenameRule {
19    /// Don't apply a default rename rule.
20    None,
21    /// Rename direct children to "lowercase" style.
22    LowerCase,
23    /// Rename direct children to "PascalCase" style, as typically used for enum variants.
24    PascalCase,
25    /// Rename direct children to "camelCase" style.
26    CamelCase,
27    /// Rename direct children to "snake_case" style, as commonly used for fields.
28    SnakeCase,
29    /// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly used for constants.
30    ScreamingSnakeCase,
31    /// Rename direct children to "kebab-case" style.
32    KebabCase,
33    /// Rename direct children to "SCREAMING-KEBAB-CASE" style.
34    ScreamingKebabCase
35}
36
37impl RenameRule {
38    pub fn apply_to_variant(&self, variant: &str) -> String {
39        match *self {
40            None | PascalCase => variant.to_owned(),
41            LowerCase => variant.to_ascii_lowercase(),
42            CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..],
43            SnakeCase => {
44                let mut snake = String::new();
45                for (i, ch) in variant.char_indices() {
46                    if i > 0 && ch.is_uppercase() {
47                        snake.push('_');
48                    }
49                    snake.push(ch.to_ascii_lowercase());
50                }
51                snake
52            }
53            ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
54            KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
55            ScreamingKebabCase => ScreamingSnakeCase.apply_to_variant(variant).replace('_', "-")
56        }
57    }
58
59    pub fn apply_to_field(&self, field: &str) -> String {
60        match *self {
61            None | LowerCase | SnakeCase => field.to_owned(),
62            PascalCase => {
63                let mut pascal = String::new();
64                let mut capitalize = true;
65                for ch in field.chars() {
66                    if ch == '_' {
67                        capitalize = true;
68                    } else if capitalize {
69                        pascal.push(ch.to_ascii_uppercase());
70                        capitalize = false;
71                    } else {
72                        pascal.push(ch);
73                    }
74                }
75                pascal
76            }
77            CamelCase => {
78                let pascal = PascalCase.apply_to_field(field);
79                pascal[..1].to_ascii_lowercase() + &pascal[1..]
80            }
81            ScreamingSnakeCase => field.to_ascii_uppercase(),
82            KebabCase => field.replace('_', "-"),
83            ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-")
84        }
85    }
86}
87
88impl FromStr for RenameRule {
89    type Err = ();
90
91    fn from_str(rename_all_str: &str) -> Result<Self, Self::Err> {
92        match rename_all_str {
93            "lowercase" => Ok(LowerCase),
94            "PascalCase" => Ok(PascalCase),
95            "camelCase" => Ok(CamelCase),
96            "snake_case" => Ok(SnakeCase),
97            "SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase),
98            "kebab-case" => Ok(KebabCase),
99            "SCREAMING-KEBAB-CASE" => Ok(ScreamingKebabCase),
100            _ => Err(()),
101        }
102    }
103}
104
105#[test]
106fn rename_variants() {
107    for &(original, lower, camel, snake, screaming, kebab, screaming_kebab) in
108        &[
109            ("Outcome", "outcome", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
110            ("VeryTasty", "verytasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
111            ("A", "a", "a", "a", "A", "a", "A"),
112            ("Z42", "z42", "z42", "z42", "Z42", "z42", "Z42"),
113        ] {
114        assert_eq!(None.apply_to_variant(original), original);
115        assert_eq!(LowerCase.apply_to_variant(original), lower);
116        assert_eq!(PascalCase.apply_to_variant(original), original);
117        assert_eq!(CamelCase.apply_to_variant(original), camel);
118        assert_eq!(SnakeCase.apply_to_variant(original), snake);
119        assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
120        assert_eq!(KebabCase.apply_to_variant(original), kebab);
121        assert_eq!(ScreamingKebabCase.apply_to_variant(original), screaming_kebab);
122    }
123}
124
125#[test]
126fn rename_fields() {
127    for &(original, pascal, camel, screaming, kebab, screaming_kebab) in
128        &[
129            ("outcome", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
130            ("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
131            ("a", "A", "a", "A", "a", "A"),
132            ("z42", "Z42", "z42", "Z42", "z42", "Z42"),
133        ] {
134        assert_eq!(None.apply_to_field(original), original);
135        assert_eq!(PascalCase.apply_to_field(original), pascal);
136        assert_eq!(CamelCase.apply_to_field(original), camel);
137        assert_eq!(SnakeCase.apply_to_field(original), original);
138        assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
139        assert_eq!(KebabCase.apply_to_field(original), kebab);
140        assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);
141    }
142}