Skip to main content

winrt_gen/
case.rs

1use crate::types::MethodKind;
2
3/// Change a CamelCase string to snake case
4///  
5/// This prepends the optional preamble to the string in an efficient way.
6///
7/// # Panics
8///
9/// This panics in debug mode if called with an empty string
10pub(crate) fn to_snake(camel: &str, kind: MethodKind) -> String {
11    debug_assert!(!camel.is_empty(), "Called `to_snake` with empty string");
12
13    let preamble = match kind {
14        MethodKind::Set => "set_",
15        MethodKind::Remove => "remove_",
16        _ => "",
17    };
18
19    let mut snake = String::with_capacity(preamble.len() + camel.len());
20    snake.push_str(preamble);
21
22    let mut since_last_underscore = 0;
23
24    let mut chars = camel.chars();
25    // first character as lowercased
26    for c in chars.next().unwrap().to_lowercase() {
27        since_last_underscore += 1;
28        snake.push(c);
29    }
30
31    // zip together iterator of previous characters and next characters
32    for (previous, next) in camel.chars().zip(camel.chars().skip(2)) {
33        // safe to unwrap since the iterator of next chars produced something
34        let current = chars.next().unwrap();
35
36        // If the current character isn't uppercase we can just push it and move on
37        if !current.is_uppercase() {
38            since_last_underscore += 1;
39            snake.push(current);
40            continue;
41        }
42
43        if previous.is_lowercase() || next.is_lowercase() && since_last_underscore > 1 {
44            since_last_underscore = 0;
45
46            if previous != '_' {
47                snake.push('_');
48            }
49        }
50
51        for c in current.to_lowercase() {
52            since_last_underscore += 1;
53
54            snake.push(c);
55        }
56    }
57
58    if let Some(last) = chars.next() {
59        snake.extend(last.to_lowercase());
60    }
61
62    snake
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn to_snake_works() {
71        assert_eq!(
72            to_snake("Windows", MethodKind::Normal),
73            "windows".to_owned()
74        );
75
76        assert_eq!(
77            to_snake("ApplicationModel", MethodKind::Normal),
78            "application_model".to_owned()
79        );
80
81        assert_eq!(to_snake("foo", MethodKind::Normal), "foo".to_owned());
82
83        assert_eq!(
84            to_snake("UIProgramming", MethodKind::Normal),
85            "ui_programming".to_owned()
86        );
87
88        assert_eq!(
89            to_snake("UIProgramming", MethodKind::Set),
90            "set_ui_programming".to_owned()
91        );
92
93        assert_eq!(
94            to_snake("CreateUInt8Array", MethodKind::Normal),
95            "create_uint8_array".to_owned()
96        );
97
98        assert_eq!(
99            to_snake("Socks", MethodKind::Remove),
100            "remove_socks".to_owned()
101        );
102
103        assert_eq!(
104            to_snake("appointmentId", MethodKind::Normal),
105            "appointment_id".to_owned()
106        );
107
108        assert_eq!(to_snake("a", MethodKind::Normal), "a".to_owned());
109
110        assert_eq!(
111            to_snake("CreateField_Default", MethodKind::Normal),
112            "create_field_default".to_owned()
113        );
114    }
115}