abs_data/builders/
url_builder.rs1use 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}