prost_reflect/dynamic/serde/
case.rs

1/// Convert `src` from snake case to camel case, returning an error if roundtripping
2/// back to snake case would not be possible.
3pub(crate) fn snake_case_to_camel_case(dst: &mut String, src: &str) -> Result<(), ()> {
4    let mut ucase_next = false;
5    for ch in src.chars() {
6        if ch.is_ascii_uppercase() {
7            return Err(());
8        }
9
10        if ucase_next {
11            let upper_ch = ch.to_ascii_uppercase();
12            if upper_ch == ch {
13                return Err(());
14            }
15
16            dst.push(upper_ch);
17            ucase_next = false;
18        } else if ch == '_' {
19            ucase_next = true;
20        } else {
21            dst.push(ch)
22        }
23    }
24
25    Ok(())
26}
27
28pub(crate) fn camel_case_to_snake_case(result: &mut String, part: &str) -> Result<(), ()> {
29    for ch in part.chars() {
30        if ch.is_ascii_uppercase() {
31            result.push('_');
32            result.push(ch.to_ascii_lowercase());
33        } else if ch == '_' {
34            return Err(());
35        } else {
36            result.push(ch);
37        }
38    }
39
40    Ok(())
41}
42
43#[cfg(test)]
44mod tests {
45    use proptest::prelude::*;
46
47    use super::*;
48
49    #[test]
50    fn snake_to_camel() {
51        let mut buf = String::new();
52
53        snake_case_to_camel_case(&mut buf, "foo").unwrap();
54        assert_eq!(&buf, "foo");
55        buf.clear();
56
57        snake_case_to_camel_case(&mut buf, "foo_bar").unwrap();
58        assert_eq!(&buf, "fooBar");
59        buf.clear();
60    }
61
62    #[test]
63    fn camel_to_snake() {
64        let mut buf = String::new();
65
66        camel_case_to_snake_case(&mut buf, "foo").unwrap();
67        assert_eq!(&buf, "foo");
68        buf.clear();
69
70        camel_case_to_snake_case(&mut buf, "fooBar").unwrap();
71        assert_eq!(&buf, "foo_bar");
72        buf.clear();
73    }
74
75    #[test]
76    fn bad_roundtrips() {
77        let mut buf = String::new();
78        assert!(snake_case_to_camel_case(&mut buf, "fooBar").is_err());
79        assert!(snake_case_to_camel_case(&mut buf, "foo_3_bar").is_err());
80        assert!(snake_case_to_camel_case(&mut buf, "foo__bar").is_err());
81    }
82
83    proptest! {
84        #[test]
85        fn roundtrip_cases(snake_case in "[a-zA-Z0-9]+") {
86            let mut camel_case = String::new();
87            if snake_case_to_camel_case(&mut camel_case, &snake_case).is_ok() {
88                let mut roundtripped_snake_case = String::new();
89                camel_case_to_snake_case(&mut roundtripped_snake_case, &camel_case).unwrap();
90
91                prop_assert_eq!(snake_case, roundtripped_snake_case);
92            }
93        }
94    }
95}