Skip to main content

camel_endpoint/
uri.rs

1use std::collections::HashMap;
2
3use camel_api::CamelError;
4
5/// Parsed components of a Camel URI.
6///
7/// Format: `scheme:path?key1=value1&key2=value2`
8#[derive(Debug, Clone, PartialEq)]
9pub struct UriComponents {
10    /// The scheme (component name), e.g. "timer", "log".
11    pub scheme: String,
12    /// The path portion after the scheme, e.g. "tick" in "timer:tick".
13    pub path: String,
14    /// Query parameters as key-value pairs.
15    pub params: HashMap<String, String>,
16}
17
18/// Parse a Camel-style URI into its components.
19///
20/// Format: `scheme:path?key1=value1&key2=value2`
21pub 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}