use url::Url;
pub fn build_api_url(
base_url: &str,
path: &str,
query: &[(String, String)],
) -> anyhow::Result<String> {
let base = if base_url.ends_with('/') {
base_url.to_string()
} else {
format!("{base_url}/")
};
let base = Url::parse(&base)?;
let mut url = base.join(path.trim_start_matches('/'))?;
if !query.is_empty() {
let mut pairs = url.query_pairs_mut();
for (key, value) in query {
pairs.append_pair(key, value);
}
}
Ok(url.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn joins_a_base_url_and_path_without_double_slashes() {
let url = build_api_url("https://api.example.com", "/widgets", &[]).unwrap();
assert_eq!(url, "https://api.example.com/widgets");
}
#[test]
fn handles_a_base_url_without_a_trailing_slash() {
let url = build_api_url("https://api.example.com/v1", "widgets", &[]).unwrap();
assert_eq!(url, "https://api.example.com/v1/widgets");
}
#[test]
fn appends_query_parameters() {
let url = build_api_url(
"https://api.example.com",
"/widgets",
&[("limit".to_string(), "10".to_string())],
)
.unwrap();
assert_eq!(url, "https://api.example.com/widgets?limit=10");
}
}