url_builder/
lib.rs

1//! # URLBuilder
2//!
3//! An easy-to-use crate to construct URLs for the Rust Programming language
4//!
5//! You can use this to build up context for a url over the course of execution and then
6//! call the `.build()` method to generate the final url.
7//!
8//! The mutating functions allow you to chain them to each other.
9//!
10//! ## Example
11//!
12//! The following code will create a url similar to `http://localhost:8000?first=1&second=2&third=3`
13//! The order of the query parameters is indeterminate as the parameters are internally stored in
14//! `std::collections::HashMap`.
15//!
16//! ```
17//! use url_builder::URLBuilder;
18//!
19//! let mut ub = URLBuilder::new();
20//!
21//! ub.set_protocol("http")
22//!     .set_host("localhost")
23//!     .set_port(8000)
24//!     .add_param("first", "1")
25//!     .add_param("second", "2")
26//!     .add_param("third", "3");
27//!
28//! println!("{}", ub.build());
29//! ```
30
31use std::collections::HashMap;
32
33#[derive(Debug)]
34pub struct URLBuilder {
35    protocol: String,
36    host: String,
37    port: u16,
38    params: HashMap<String, String>,
39    routes: Vec<String>,
40}
41
42impl Default for URLBuilder {
43    fn default() -> Self {
44        Self::new()
45    }
46}
47
48impl URLBuilder {
49    /// Creates a new URLBuilder instance
50    ///
51    /// # Example
52    ///
53    /// ```
54    /// use url_builder::URLBuilder;
55    ///
56    /// let mut ub = URLBuilder::new();
57    /// ```
58    pub fn new() -> URLBuilder {
59        URLBuilder {
60            protocol: String::new(),
61            host: String::new(),
62            port: 0,
63            params: HashMap::new(),
64            routes: Vec::new(),
65        }
66    }
67
68    /// Consumes the builder and returns a String, with the formatted
69    /// url.
70    ///
71    /// # Example
72    ///
73    /// ```
74    /// use url_builder::URLBuilder;
75    ///
76    /// let mut ub = URLBuilder::new();
77    /// ub.set_protocol("http")
78    ///     .set_host("localhost")
79    ///     .set_port(8000)
80    ///     .add_route("query")
81    ///     .add_param("first", "1")
82    ///     .add_param("second", "2")
83    ///     .add_param("third", "3");
84    ///
85    /// let built_url = ub.build();
86    /// ```
87    pub fn build(self) -> String {
88        let base = format!("{}://{}", self.protocol, self.host);
89
90        let mut url_params = String::new();
91        let mut routes = String::new();
92
93        for route in self.routes {
94            routes.push_str(format!("/{}", route).as_str());
95        }
96
97        if !self.params.is_empty() {
98            url_params.push('?');
99
100            for (param, value) in self.params.iter() {
101                url_params.push_str(format!("{}={}&", param, value).as_str());
102            }
103
104            // Remove the trailing `&`
105            url_params.pop();
106        }
107
108        match self.port {
109            0 => format!("{}{}{}", base, routes, url_params),
110            _ => format!("{}:{}{}{}", base, self.port, routes, url_params),
111        }
112    }
113
114    /// Adds a parameter to the URL.
115    pub fn add_param(&mut self, param: &str, value: &str) -> &mut Self {
116        self.params.insert(param.to_string(), value.to_string());
117
118        self
119    }
120
121    /// Sets the protocol that the URL builder will use.
122    pub fn set_protocol(&mut self, protocol: &str) -> &mut Self {
123        self.protocol = protocol.to_string();
124
125        self
126    }
127
128    /// Sets the protocol that the URL builder will use.
129    pub fn set_host(&mut self, host: &str) -> &mut Self {
130        self.host = host.to_string();
131
132        self
133    }
134
135    /// Sets the port that the URL builder will use.
136    pub fn set_port(&mut self, port: u16) -> &mut Self {
137        self.port = port;
138
139        self
140    }
141
142    /// Adds a route to the URL.
143    pub fn add_route(&mut self, route: &str) -> &mut Self {
144        self.routes.push(route.to_owned());
145
146        self
147    }
148
149    pub fn port(&self) -> u16 {
150        self.port
151    }
152
153    pub fn host(&self) -> &str {
154        &self.host
155    }
156
157    pub fn protocol(&self) -> &str {
158        &self.protocol
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn test_set_host() {
168        let mut ub = URLBuilder::new();
169        ub.set_host("localhost");
170        assert_eq!("localhost", ub.host());
171    }
172
173    #[test]
174    fn test_set_protocol() {
175        let mut ub = URLBuilder::new();
176        ub.set_protocol("https");
177        assert_eq!("https", ub.protocol());
178    }
179
180    #[test]
181    fn test_set_port() {
182        let mut ub = URLBuilder::new();
183        ub.set_port(8000);
184        assert_eq!(8000, ub.port());
185    }
186
187    #[test]
188    fn create_google_url() {
189        let mut ub = URLBuilder::new();
190        ub.set_protocol("http")
191            .set_host("www.google.com")
192            .set_port(80);
193        let url = ub.build();
194        assert_eq!("http://www.google.com:80", url);
195    }
196
197    #[test]
198    fn create_url_without_port() {
199        let mut ub = URLBuilder::new();
200        ub.set_protocol("http").set_host("google.com");
201        let url = ub.build();
202        assert_eq!("http://google.com", url)
203    }
204
205    #[test]
206    fn create_url_without_port_and_params() {
207        let mut ub = URLBuilder::new();
208        ub.set_protocol("http")
209            .set_host("google.com")
210            .add_param("gcookie", "0xcafe");
211        let url = ub.build();
212        assert_eq!("http://google.com?gcookie=0xcafe", url)
213    }
214
215    #[test]
216    fn create_url_without_port_and_multiple_params() {
217        let mut ub = URLBuilder::new();
218        ub.set_protocol("http")
219            .set_host("google.com")
220            .add_param("gcookie", "0xcafe")
221            .add_param("search", "rust")
222            .add_param("locale", "en-gb");
223        let url = ub.build();
224        assert!(url.contains("gcookie=0xcafe"));
225        assert!(url.contains("search=rust"));
226        assert!(url.contains("locale=en-gb"));
227    }
228
229    #[test]
230    fn create_url_with_routes() {
231        let mut ub = URLBuilder::new();
232        ub.set_protocol("http")
233            .set_host("google.com")
234            .add_route("mail");
235        let url = ub.build();
236        assert_eq!("http://google.com/mail", url)
237    }
238
239    #[test]
240    fn create_url_with_params() {
241        let mut ub = URLBuilder::new();
242        ub.set_protocol("http")
243            .set_host("localhost")
244            .set_port(8000)
245            .add_param("first", "1")
246            .add_param("second", "2")
247            .add_param("third", "3");
248
249        let url = ub.build();
250        assert!(url.contains("first=1"));
251        assert!(url.contains("second=2"));
252        assert!(url.contains("third=3"));
253    }
254
255    #[test]
256    fn create_url_with_ports_routes_and_params() {
257        let mut ub = URLBuilder::new();
258        ub.set_protocol("http")
259            .set_host("localhost")
260            .set_port(8000)
261            .add_route("query")
262            .add_route("chains")
263            .add_param("first", "1")
264            .add_param("second", "2")
265            .add_param("third", "3");
266
267        let url = ub.build();
268        assert!(url.contains("/query"));
269        assert!(url.contains("/chains"));
270        assert!(url.contains("/query/chains"));
271        assert!(url.contains("first=1"));
272        assert!(url.contains("second=2"));
273        assert!(url.contains("third=3"));
274    }
275}