1use std::collections::HashMap;
2
3use camel_api::CamelError;
4
5#[derive(Debug, Clone, PartialEq)]
9pub struct UriComponents {
10 pub scheme: String,
12 pub path: String,
14 pub params: HashMap<String, String>,
16}
17
18pub fn parse_uri(uri: &str) -> Result<UriComponents, CamelError> {
22 let (scheme, rest) = uri.split_once(':').ok_or_else(|| {
23 CamelError::InvalidUri(format!("missing scheme separator ':' in '{uri}'"))
24 })?;
25
26 if scheme.is_empty() {
27 return Err(CamelError::InvalidUri(format!("empty scheme in '{uri}'")));
28 }
29
30 let (path, params) = match rest.split_once('?') {
31 Some((path, query)) => (path, parse_query(query)),
32 None => (rest, HashMap::new()),
33 };
34
35 Ok(UriComponents {
36 scheme: scheme.to_string(),
37 path: path.to_string(),
38 params,
39 })
40}
41
42fn parse_query(query: &str) -> HashMap<String, String> {
43 query
44 .split('&')
45 .filter(|s| !s.is_empty())
46 .filter_map(|pair| {
47 let (key, value) = pair.split_once('=')?;
48 Some((key.to_string(), value.to_string()))
49 })
50 .collect()
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn test_parse_simple_uri() {
59 let result = parse_uri("timer:tick").unwrap();
60 assert_eq!(result.scheme, "timer");
61 assert_eq!(result.path, "tick");
62 assert!(result.params.is_empty());
63 }
64
65 #[test]
66 fn test_parse_uri_with_params() {
67 let result = parse_uri("timer:tick?period=1000&delay=500").unwrap();
68 assert_eq!(result.scheme, "timer");
69 assert_eq!(result.path, "tick");
70 assert_eq!(result.params.get("period"), Some(&"1000".to_string()));
71 assert_eq!(result.params.get("delay"), Some(&"500".to_string()));
72 }
73
74 #[test]
75 fn test_parse_uri_with_single_param() {
76 let result = parse_uri("log:info?level=debug").unwrap();
77 assert_eq!(result.scheme, "log");
78 assert_eq!(result.path, "info");
79 assert_eq!(result.params.get("level"), Some(&"debug".to_string()));
80 }
81
82 #[test]
83 fn test_parse_uri_no_scheme() {
84 let result = parse_uri("noscheme");
85 assert!(result.is_err());
86 }
87
88 #[test]
89 fn test_parse_uri_empty_scheme() {
90 let result = parse_uri(":path");
91 assert!(result.is_err());
92 }
93
94 #[test]
95 fn test_parse_direct_uri() {
96 let result = parse_uri("direct:myRoute").unwrap();
97 assert_eq!(result.scheme, "direct");
98 assert_eq!(result.path, "myRoute");
99 assert!(result.params.is_empty());
100 }
101
102 #[test]
103 fn test_parse_mock_uri() {
104 let result = parse_uri("mock:result").unwrap();
105 assert_eq!(result.scheme, "mock");
106 assert_eq!(result.path, "result");
107 }
108
109 #[test]
110 fn test_parse_http_uri_simple() {
111 let result = parse_uri("http://localhost:8080/api/users").unwrap();
112 assert_eq!(result.scheme, "http");
113 assert_eq!(result.path, "//localhost:8080/api/users");
114 assert!(result.params.is_empty());
115 }
116
117 #[test]
118 fn test_parse_https_uri_with_camel_params() {
119 let result = parse_uri(
120 "https://api.example.com/v1/data?httpMethod=POST&throwExceptionOnFailure=false",
121 )
122 .unwrap();
123 assert_eq!(result.scheme, "https");
124 assert_eq!(result.path, "//api.example.com/v1/data");
125 assert_eq!(result.params.get("httpMethod"), Some(&"POST".to_string()));
126 assert_eq!(
127 result.params.get("throwExceptionOnFailure"),
128 Some(&"false".to_string())
129 );
130 }
131
132 #[test]
133 fn test_parse_http_uri_no_path() {
134 let result = parse_uri("http://localhost:8080").unwrap();
135 assert_eq!(result.scheme, "http");
136 assert_eq!(result.path, "//localhost:8080");
137 assert!(result.params.is_empty());
138 }
139
140 #[test]
141 fn test_parse_http_uri_with_port_and_query() {
142 let result = parse_uri("http://example.com:3000/api?connectTimeout=5000").unwrap();
143 assert_eq!(result.scheme, "http");
144 assert_eq!(result.path, "//example.com:3000/api");
145 assert_eq!(
146 result.params.get("connectTimeout"),
147 Some(&"5000".to_string())
148 );
149 }
150}