1 // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2
3 use std::fmt;
4 use std::str;
5
6 // A struct that divide a name into serveral parts that meets rust's guidelines.
7 struct NameSpliter<'a> {
8 name: &'a [u8],
9 pos: usize,
10 }
11
12 impl<'a> NameSpliter<'a> {
new(s: &str) -> NameSpliter13 fn new(s: &str) -> NameSpliter {
14 NameSpliter {
15 name: s.as_bytes(),
16 pos: 0,
17 }
18 }
19 }
20
21 impl<'a> Iterator for NameSpliter<'a> {
22 type Item = &'a str;
23
next(&mut self) -> Option<&'a str>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 (b'A'..=b'Z').contains(&c) {
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.
to_snake_case(name: &str) -> String63 pub 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")]
to_camel_case(name: &str) -> String74 pub 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
fq_grpc(item: &str) -> String84 pub fn fq_grpc(item: &str) -> String {
85 format!("::grpcio::{}", item)
86 }
87
88 pub enum MethodType {
89 Unary,
90 ClientStreaming,
91 ServerStreaming,
92 Duplex,
93 }
94
95 impl fmt::Display for MethodType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result96 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)]
111 mod test {
112 #[test]
test_snake_name()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")]
test_camel_name()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 }
156