| // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. |
| |
| use std::fmt; |
| use std::str; |
| |
| // A struct that divide a name into serveral parts that meets rust's guidelines. |
| struct NameSpliter<'a> { |
| name: &'a [u8], |
| pos: usize, |
| } |
| |
| impl<'a> NameSpliter<'a> { |
| fn new(s: &str) -> NameSpliter { |
| NameSpliter { |
| name: s.as_bytes(), |
| pos: 0, |
| } |
| } |
| } |
| |
| impl<'a> Iterator for NameSpliter<'a> { |
| type Item = &'a str; |
| |
| fn next(&mut self) -> Option<&'a str> { |
| if self.pos == self.name.len() { |
| return None; |
| } |
| // skip all prefix '_' |
| while self.pos < self.name.len() && self.name[self.pos] == b'_' { |
| self.pos += 1; |
| } |
| let mut pos = self.name.len(); |
| let mut upper_len = 0; |
| let mut meet_lower = false; |
| for i in self.pos..self.name.len() { |
| let c = self.name[i]; |
| if (b'A'..=b'Z').contains(&c) { |
| if meet_lower { |
| // So it should be AaA or aaA |
| pos = i; |
| break; |
| } |
| upper_len += 1; |
| } else if c == b'_' { |
| pos = i; |
| break; |
| } else { |
| meet_lower = true; |
| if upper_len > 1 { |
| // So it should be AAa |
| pos = i - 1; |
| break; |
| } |
| } |
| } |
| let s = str::from_utf8(&self.name[self.pos..pos]).unwrap(); |
| self.pos = pos; |
| Some(s) |
| } |
| } |
| |
| /// Adjust method name to follow rust-guidelines. |
| pub fn to_snake_case(name: &str) -> String { |
| let mut snake_method_name = String::with_capacity(name.len()); |
| for s in NameSpliter::new(name) { |
| snake_method_name.push_str(&s.to_lowercase()); |
| snake_method_name.push('_'); |
| } |
| snake_method_name.pop(); |
| snake_method_name |
| } |
| |
| #[cfg(feature = "protobuf-codec")] |
| pub fn to_camel_case(name: &str) -> String { |
| let mut camel_case_name = String::with_capacity(name.len()); |
| for s in NameSpliter::new(name) { |
| let mut chs = s.chars(); |
| camel_case_name.extend(chs.next().unwrap().to_uppercase()); |
| camel_case_name.push_str(&s[1..].to_lowercase()); |
| } |
| camel_case_name |
| } |
| |
| pub fn fq_grpc(item: &str) -> String { |
| format!("::grpcio::{item}") |
| } |
| |
| pub enum MethodType { |
| Unary, |
| ClientStreaming, |
| ServerStreaming, |
| Duplex, |
| } |
| |
| impl fmt::Display for MethodType { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| write!( |
| f, |
| "{}", |
| match self { |
| MethodType::Unary => "MethodType::Unary", |
| MethodType::ClientStreaming => "MethodType::ClientStreaming", |
| MethodType::ServerStreaming => "MethodType::ServerStreaming", |
| MethodType::Duplex => "MethodType::Duplex", |
| } |
| ) |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| #[test] |
| fn test_snake_name() { |
| let cases = vec![ |
| ("AsyncRequest", "async_request"), |
| ("asyncRequest", "async_request"), |
| ("async_request", "async_request"), |
| ("createID", "create_id"), |
| ("AsyncRClient", "async_r_client"), |
| ("CreateIDForReq", "create_id_for_req"), |
| ("Create_ID_For_Req", "create_id_for_req"), |
| ("Create_ID_For__Req", "create_id_for_req"), |
| ("ID", "id"), |
| ("id", "id"), |
| ]; |
| |
| for (origin, exp) in cases { |
| let res = super::to_snake_case(origin); |
| assert_eq!(res, exp); |
| } |
| } |
| |
| #[test] |
| #[cfg(feature = "protobuf-codec")] |
| fn test_camel_name() { |
| let cases = vec![ |
| ("AsyncRequest", "AsyncRequest"), |
| ("asyncRequest", "AsyncRequest"), |
| ("async_request", "AsyncRequest"), |
| ("createID", "CreateId"), |
| ("AsyncRClient", "AsyncRClient"), |
| ("async_r_client", "AsyncRClient"), |
| ("CreateIDForReq", "CreateIdForReq"), |
| ("Create_ID_For_Req", "CreateIdForReq"), |
| ("Create_ID_For__Req", "CreateIdForReq"), |
| ("ID", "Id"), |
| ("id", "Id"), |
| ]; |
| |
| for (origin, exp) in cases { |
| let res = super::to_camel_case(origin); |
| assert_eq!(res, exp); |
| } |
| } |
| } |