abs_data/builders/
url_builder.rs

1use crate::error_code::ErrorCode;
2use url::Url;
3
4use crate::result::Result;
5
6pub struct UrlBuilder {
7    base_url: Box<str>,
8    path_segments: Vec<Box<str>>,
9    query_params: Vec<(Box<str>, Box<str>)>,
10}
11
12impl UrlBuilder {
13    pub fn new<U>(base_url: U) -> Self
14    where
15        U: Into<Box<str>>,
16    {
17        UrlBuilder {
18            base_url: base_url.into(),
19            path_segments: Vec::new(),
20            query_params: Vec::new(),
21        }
22    }
23
24    pub fn add_path_segment<U>(mut self, segment: U) -> Self
25    where
26        U: Into<Box<str>>,
27    {
28        self.path_segments.push(segment.into());
29        self
30    }
31
32    pub fn add_query_param<U, V>(mut self, key: U, value: V) -> Self
33    where
34        U: Into<Box<str>>,
35        V: Into<Box<str>>,
36    {
37        self.query_params.push((key.into(), value.into()));
38        self
39    }
40
41    pub fn build(self) -> Result<Url> {
42        let mut url = Url::parse(&self.base_url)?;
43
44        url.path_segments_mut()
45            .map_err(|_| ErrorCode::UrlCannotBeABase)?
46            .extend(self.path_segments);
47
48        if !self.query_params.is_empty() {
49            let query_string = self
50                .query_params
51                .into_iter()
52                .map(|(k, v)| format!("{}={}", k, v))
53                .collect::<Vec<String>>()
54                .join("&");
55            url.set_query(Some(&query_string));
56        }
57
58        Ok(url)
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::UrlBuilder;
65    use url::Url;
66
67    #[test]
68    fn test_valid_url_building() {
69        let url = UrlBuilder::new("http://example.com")
70            .add_path_segment("path")
71            .add_query_param("key", "value")
72            .build()
73            .unwrap();
74
75        assert_eq!(url.as_str(), "http://example.com/path?key=value");
76    }
77
78    #[test]
79    fn test_invalid_base_url() {
80        let result = UrlBuilder::new("invalid_url")
81            .add_path_segment("path")
82            .add_query_param("key", "value")
83            .build();
84
85        assert!(result.is_err());
86    }
87
88    #[test]
89    fn test_no_path_segments() {
90        let url = UrlBuilder::new("http://example.com")
91            .add_query_param("key", "value")
92            .build()
93            .unwrap();
94
95        assert_eq!(url.as_str(), "http://example.com/?key=value");
96    }
97
98    #[test]
99    fn test_multiple_path_segments() {
100        let url = UrlBuilder::new("http://example.com")
101            .add_path_segment("path1")
102            .add_path_segment("path2")
103            .build()
104            .unwrap();
105
106        assert_eq!(url.as_str(), "http://example.com/path1/path2");
107    }
108
109    #[test]
110    fn test_no_query_params() {
111        let url = UrlBuilder::new("http://example.com")
112            .add_path_segment("path")
113            .build()
114            .unwrap();
115
116        assert_eq!(url.as_str(), "http://example.com/path");
117    }
118
119    #[test]
120    fn test_multiple_query_params() {
121        let url = UrlBuilder::new("http://example.com")
122            .add_query_param("key1", "value1")
123            .add_query_param("key2", "value2")
124            .build()
125            .unwrap();
126
127        let expected_url = Url::parse("http://example.com/?key1=value1&key2=value2").unwrap();
128        assert_eq!(url, expected_url);
129    }
130
131    #[test]
132    fn test_empty_path_segment() {
133        let url = UrlBuilder::new("http://example.com")
134            .add_path_segment("")
135            .build()
136            .unwrap();
137
138        assert_eq!(url.as_str(), "http://example.com/");
139    }
140
141    #[test]
142    fn test_empty_query_param() {
143        let url = UrlBuilder::new("http://example.com")
144            .add_query_param("", "")
145            .build()
146            .unwrap();
147
148        assert_eq!(url.as_str(), "http://example.com/?=");
149    }
150}