A library for ATProtocol identities.
1//! URL construction utilities leveraging the `url` crate. 2//! 3//! Provides helpers for building URLs and appending query parameters 4//! without manual string concatenation. 5 6use url::{ParseError, Url}; 7 8/// Builds a URL from the provided components. 9/// Returns `Result<Url, ParseError>` to surface parsing errors. 10pub fn build_url<K, V, I>(host: &str, path: &str, params: I) -> Result<Url, ParseError> 11where 12 I: IntoIterator<Item = (K, V)>, 13 K: AsRef<str>, 14 V: AsRef<str>, 15{ 16 let mut base = if host.starts_with("http://") || host.starts_with("https://") { 17 Url::parse(host)? 18 } else { 19 Url::parse(&format!("https://{}", host))? 20 }; 21 22 if !base.path().ends_with('/') { 23 let mut new_path = base.path().to_string(); 24 if !new_path.ends_with('/') { 25 new_path.push('/'); 26 } 27 if new_path.is_empty() { 28 new_path.push('/'); 29 } 30 base.set_path(&new_path); 31 } 32 33 let mut url = base.join(path.trim_start_matches('/'))?; 34 { 35 let mut pairs = url.query_pairs_mut(); 36 for (key, value) in params { 37 pairs.append_pair(key.as_ref(), value.as_ref()); 38 } 39 } 40 Ok(url) 41} 42 43#[cfg(test)] 44mod tests { 45 use super::*; 46 47 #[test] 48 fn builds_url_with_params() { 49 let url = build_url( 50 "example.com/api", 51 "resource", 52 [("id", "123"), ("status", "active")], 53 ) 54 .expect("url build failed"); 55 56 assert_eq!( 57 url.as_str(), 58 "https://example.com/api/resource?id=123&status=active" 59 ); 60 } 61}