grpcio_compiler/
util.rs

1// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2
3use std::fmt;
4use std::str;
5
6// A struct that divide a name into serveral parts that meets rust's guidelines.
7struct NameSpliter<'a> {
8    name: &'a [u8],
9    pos: usize,
10}
11
12impl<'a> NameSpliter<'a> {
13    fn new(s: &str) -> NameSpliter {
14        NameSpliter {
15            name: s.as_bytes(),
16            pos: 0,
17        }
18    }
19}
20
21impl<'a> Iterator for NameSpliter<'a> {
22    type Item = &'a str;
23
24    fn next(&mut self) -> Option<&'a str> {
25        if self.pos == self.name.len() {
26            return None;
27        }
28        // skip all prefix '_'
29        while self.pos < self.name.len() && self.name[self.pos] == b'_' {
30            self.pos += 1;
31        }
32        let mut pos = self.name.len();
33        let mut upper_len = 0;
34        let mut meet_lower = false;
35        for i in self.pos..self.name.len() {
36            let c = self.name[i];
37            if c.is_ascii_uppercase() {
38                if meet_lower {
39                    // So it should be AaA or aaA
40                    pos = i;
41                    break;
42                }
43                upper_len += 1;
44            } else if c == b'_' {
45                pos = i;
46                break;
47            } else {
48                meet_lower = true;
49                if upper_len > 1 {
50                    // So it should be AAa
51                    pos = i - 1;
52                    break;
53                }
54            }
55        }
56        let s = str::from_utf8(&self.name[self.pos..pos]).unwrap();
57        self.pos = pos;
58        Some(s)
59    }
60}
61
62/// Adjust method name to follow rust-guidelines.
63pub fn to_snake_case(name: &str) -> String {
64    let mut snake_method_name = String::with_capacity(name.len());
65    for s in NameSpliter::new(name) {
66        snake_method_name.push_str(&s.to_lowercase());
67        snake_method_name.push('_');
68    }
69    snake_method_name.pop();
70    snake_method_name
71}
72
73#[cfg(feature = "protobuf-codec")]
74pub fn to_camel_case(name: &str) -> String {
75    let mut camel_case_name = String::with_capacity(name.len());
76    for s in NameSpliter::new(name) {
77        let mut chs = s.chars();
78        camel_case_name.extend(chs.next().unwrap().to_uppercase());
79        camel_case_name.push_str(&s[1..].to_lowercase());
80    }
81    camel_case_name
82}
83
84pub fn fq_grpc(item: &str) -> String {
85    format!("::grpcio::{item}")
86}
87
88pub enum MethodType {
89    Unary,
90    ClientStreaming,
91    ServerStreaming,
92    Duplex,
93}
94
95impl fmt::Display for MethodType {
96    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97        write!(
98            f,
99            "{}",
100            match self {
101                MethodType::Unary => "MethodType::Unary",
102                MethodType::ClientStreaming => "MethodType::ClientStreaming",
103                MethodType::ServerStreaming => "MethodType::ServerStreaming",
104                MethodType::Duplex => "MethodType::Duplex",
105            }
106        )
107    }
108}
109
110#[cfg(test)]
111mod test {
112    #[test]
113    fn test_snake_name() {
114        let cases = vec![
115            ("AsyncRequest", "async_request"),
116            ("asyncRequest", "async_request"),
117            ("async_request", "async_request"),
118            ("createID", "create_id"),
119            ("AsyncRClient", "async_r_client"),
120            ("CreateIDForReq", "create_id_for_req"),
121            ("Create_ID_For_Req", "create_id_for_req"),
122            ("Create_ID_For__Req", "create_id_for_req"),
123            ("ID", "id"),
124            ("id", "id"),
125        ];
126
127        for (origin, exp) in cases {
128            let res = super::to_snake_case(origin);
129            assert_eq!(res, exp);
130        }
131    }
132
133    #[test]
134    #[cfg(feature = "protobuf-codec")]
135    fn test_camel_name() {
136        let cases = vec![
137            ("AsyncRequest", "AsyncRequest"),
138            ("asyncRequest", "AsyncRequest"),
139            ("async_request", "AsyncRequest"),
140            ("createID", "CreateId"),
141            ("AsyncRClient", "AsyncRClient"),
142            ("async_r_client", "AsyncRClient"),
143            ("CreateIDForReq", "CreateIdForReq"),
144            ("Create_ID_For_Req", "CreateIdForReq"),
145            ("Create_ID_For__Req", "CreateIdForReq"),
146            ("ID", "Id"),
147            ("id", "Id"),
148        ];
149
150        for (origin, exp) in cases {
151            let res = super::to_camel_case(origin);
152            assert_eq!(res, exp);
153        }
154    }
155}