zerodds_grpc_bridge/
path.rs1use alloc::string::String;
7use core::fmt;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum PathError {
12 NotAbsolute,
14 MalformedPath,
16}
17
18impl fmt::Display for PathError {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 match self {
21 Self::NotAbsolute => f.write_str("path must start with /"),
22 Self::MalformedPath => f.write_str("path must be /<service>/<method>"),
23 }
24 }
25}
26
27#[cfg(feature = "std")]
28impl std::error::Error for PathError {}
29
30pub fn parse_path(path: &str) -> Result<(String, String), PathError> {
37 let stripped = path.strip_prefix('/').ok_or(PathError::NotAbsolute)?;
38 let mut parts = stripped.splitn(2, '/');
39 let service = parts.next().ok_or(PathError::MalformedPath)?;
40 let method = parts.next().ok_or(PathError::MalformedPath)?;
41 if service.is_empty() || method.is_empty() {
42 return Err(PathError::MalformedPath);
43 }
44 if method.contains('/') {
45 return Err(PathError::MalformedPath);
47 }
48 Ok((String::from(service), String::from(method)))
49}
50
51#[cfg(test)]
52#[allow(clippy::expect_used)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn parses_standard_grpc_path() {
58 let (s, m) = parse_path("/helloworld.Greeter/SayHello").expect("ok");
60 assert_eq!(s, "helloworld.Greeter");
61 assert_eq!(m, "SayHello");
62 }
63
64 #[test]
65 fn rejects_relative_path() {
66 assert_eq!(
67 parse_path("helloworld.Greeter/SayHello"),
68 Err(PathError::NotAbsolute)
69 );
70 }
71
72 #[test]
73 fn rejects_path_without_method() {
74 assert_eq!(
75 parse_path("/helloworld.Greeter"),
76 Err(PathError::MalformedPath)
77 );
78 }
79
80 #[test]
81 fn rejects_empty_service() {
82 assert_eq!(parse_path("//SayHello"), Err(PathError::MalformedPath));
83 }
84
85 #[test]
86 fn rejects_empty_method() {
87 assert_eq!(parse_path("/Service/"), Err(PathError::MalformedPath));
88 }
89
90 #[test]
91 fn rejects_method_with_slash() {
92 assert_eq!(
94 parse_path("/Service/Method/Sub"),
95 Err(PathError::MalformedPath)
96 );
97 }
98
99 #[test]
100 fn parses_dotted_service_name() {
101 let (s, m) = parse_path("/google.api.something.LongServiceName/MyMethod").expect("ok");
103 assert_eq!(s, "google.api.something.LongServiceName");
104 assert_eq!(m, "MyMethod");
105 }
106}