winterbaume_core/protocol/
common.rs1use std::collections::HashMap;
4
5pub fn extract_path(uri: &str) -> String {
10 let without_scheme = if let Some(rest) = uri.strip_prefix("https://") {
12 rest
13 } else if let Some(rest) = uri.strip_prefix("http://") {
14 rest
15 } else {
16 uri
17 };
18 if let Some(slash) = without_scheme.find('/') {
20 let path_and_query = &without_scheme[slash..];
21 if let Some(q) = path_and_query.find('?') {
23 path_and_query[..q].to_string()
24 } else {
25 path_and_query.to_string()
26 }
27 } else {
28 "/".to_string()
30 }
31}
32
33pub fn extract_query_string(uri: &str) -> &str {
35 uri.split_once('?').map(|(_, q)| q).unwrap_or("")
36}
37
38pub fn percent_decode(s: &str) -> String {
40 let mut result = String::with_capacity(s.len());
41 let mut bytes = s.bytes();
42 while let Some(b) = bytes.next() {
43 match b {
44 b'%' => {
45 let hi = bytes.next().and_then(hex_val);
46 let lo = bytes.next().and_then(hex_val);
47 if let (Some(hi), Some(lo)) = (hi, lo) {
48 result.push((hi << 4 | lo) as char);
49 }
50 }
51 b'+' => result.push(' '),
52 _ => result.push(b as char),
53 }
54 }
55 result
56}
57
58pub fn hex_val(b: u8) -> Option<u8> {
60 match b {
61 b'0'..=b'9' => Some(b - b'0'),
62 b'a'..=b'f' => Some(b - b'a' + 10),
63 b'A'..=b'F' => Some(b - b'A' + 10),
64 _ => None,
65 }
66}
67
68pub fn urldecode(s: &str) -> String {
70 percent_decode(s)
71}
72
73pub fn parse_query_string(s: &str) -> HashMap<String, String> {
77 let mut map = HashMap::new();
78 for pair in s.split('&') {
79 if let Some((key, value)) = pair.split_once('=') {
80 let key = urldecode(key);
81 let value = urldecode(value);
82 map.insert(key, value);
83 }
84 }
85 map
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_extract_path() {
94 assert_eq!(
95 extract_path("https://kms.us-east-1.amazonaws.com/some/path"),
96 "/some/path"
97 );
98 assert_eq!(
99 extract_path("https://kms.us-east-1.amazonaws.com/some/path?query=1"),
100 "/some/path"
101 );
102 assert_eq!(extract_path("https://kms.us-east-1.amazonaws.com"), "/");
103 assert_eq!(extract_path("/local/path?q=1"), "/local/path");
104 }
105
106 #[test]
107 fn test_percent_decode() {
108 assert_eq!(percent_decode("hello%20world"), "hello world");
109 assert_eq!(percent_decode("hello+world"), "hello world");
110 assert_eq!(percent_decode("a%2Fb"), "a/b");
111 assert_eq!(percent_decode("plain"), "plain");
112 }
113
114 #[test]
115 fn test_parse_query_string() {
116 let qs = "Action=CreateUser&UserName=test%20user&Version=2010-05-08";
117 let params = parse_query_string(qs);
118 assert_eq!(params.get("Action").unwrap(), "CreateUser");
119 assert_eq!(params.get("UserName").unwrap(), "test user");
120 assert_eq!(params.get("Version").unwrap(), "2010-05-08");
121 }
122}