use crate::error::{Error, Result};
use url::Url;
pub struct UrlBuilder {
base_url: Url,
}
impl UrlBuilder {
pub fn new(base_url: Url) -> Self {
Self { base_url }
}
pub fn build(&self, path: &str) -> Result<Url> {
let clean_path = path.trim_start_matches('/');
self.base_url.join(clean_path).map_err(|e| Error::ApiError {
code: 400,
message: format!("Invalid URL construction for path '{}': {}", path, e),
metadata: None,
})
}
pub fn build_with_query(&self, path: &str) -> Result<Url> {
self.build(path)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_url_builder_with_trailing_slash() {
let base_url = Url::parse("https://api.example.com/v1/").unwrap();
let builder = UrlBuilder::new(base_url);
let url = builder.build("activity").unwrap();
assert_eq!(url.as_str(), "https://api.example.com/v1/activity");
}
#[test]
fn test_url_builder_without_trailing_slash() {
let base_url = Url::parse("https://api.example.com/v1").unwrap();
let builder = UrlBuilder::new(base_url);
let url = builder.build("activity").unwrap();
assert_eq!(url.as_str(), "https://api.example.com/activity");
}
#[test]
fn test_url_builder_with_leading_slash() {
let base_url = Url::parse("https://api.example.com/v1/").unwrap();
let builder = UrlBuilder::new(base_url);
let url = builder.build("/activity").unwrap();
assert_eq!(url.as_str(), "https://api.example.com/v1/activity");
}
#[test]
fn test_url_builder_invalid_path() {
let base_url = Url::parse("https://api.example.com/v1/").unwrap();
let builder = UrlBuilder::new(base_url);
let result = builder.build("../../../etc/passwd");
assert!(result.is_ok());
}
}