Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions sdk/core/typespec_client_core/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,148 @@ where
}
}
}

/// Extension trait for [`Url`] to provide additional URL manipulation methods.
pub trait UrlExt: crate::private::Sealed {
/// Appends a path segment to the URL's path, handling slashes appropriately and preserving query parameters.
///
/// This always assumes the existing URL terminates with a directory, and the `path` you pass in is a separate directory or file segment.
///
/// # Examples
///
/// ```
/// use typespec_client_core::http::{Url, UrlExt as _};
///
/// let mut url: Url = "https://contoso.com/foo?a=1".parse().unwrap();
/// url.append_path("bar");
/// assert_eq!(url.as_str(), "https://contoso.com/foo/bar?a=1");
/// ```
fn append_path(&mut self, path: impl AsRef<str>);
}

impl UrlExt for Url {
fn append_path(&mut self, p: impl AsRef<str>) {
if self.path() == "/" {
self.set_path(p.as_ref());
} else if !p.as_ref().is_empty() && p.as_ref() != "/" {
let mut combinator = if self.path().ends_with('/') { 1 } else { 0 };
combinator += if p.as_ref().starts_with('/') { 1 } else { 0 };

let mut new_path = if combinator < 2 {
self.path().to_owned()
} else {
self.path()[..self.path().len() - 1].to_owned()
};
if combinator == 0 {
new_path.push('/');
}
new_path.push_str(p.as_ref());

self.set_path(&new_path);
}
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn url_append_path() {
let mut url = Url::parse("https://www.microsoft.com?q=q").unwrap();
url.append_path("foo");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo?q=q");

url = Url::parse("https://www.microsoft.com/?q=q").unwrap();
url.append_path("foo");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo?q=q");

url = Url::parse("https://www.microsoft.com?q=q").unwrap();
url.append_path("/foo");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo?q=q");

url = Url::parse("https://www.microsoft.com/?q=q").unwrap();
url.append_path("/foo");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo?q=q");

url = Url::parse("https://www.microsoft.com?q=q").unwrap();
url.append_path("foo/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/?q=q");

url = Url::parse("https://www.microsoft.com/?q=q").unwrap();
url.append_path("foo/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/?q=q");

url = Url::parse("https://www.microsoft.com?q=q").unwrap();
url.append_path("/foo/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/?q=q");

url = Url::parse("https://www.microsoft.com/?q=q").unwrap();
url.append_path("/foo/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/?q=q");

url = Url::parse("https://www.microsoft.com/foo?q=q").unwrap();
url.append_path("bar");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/bar?q=q");

url = Url::parse("https://www.microsoft.com/foo/?q=q").unwrap();
url.append_path("bar");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/bar?q=q");

url = Url::parse("https://www.microsoft.com/foo?q=q").unwrap();
url.append_path("/bar");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/bar?q=q");

url = Url::parse("https://www.microsoft.com/foo/?q=q").unwrap();
url.append_path("/bar");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/bar?q=q");

url = Url::parse("https://www.microsoft.com/foo?q=q").unwrap();
url.append_path("bar/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/bar/?q=q");

url = Url::parse("https://www.microsoft.com/foo/?q=q").unwrap();
url.append_path("bar/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/bar/?q=q");

url = Url::parse("https://www.microsoft.com/foo?q=q").unwrap();
url.append_path("/bar/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/bar/?q=q");

url = Url::parse("https://www.microsoft.com/foo/?q=q").unwrap();
url.append_path("/bar/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/bar/?q=q");

url = Url::parse("https://www.microsoft.com?q=q").unwrap();
url.append_path("/");
assert_eq!(url.as_str(), "https://www.microsoft.com/?q=q");

url = Url::parse("https://www.microsoft.com/?q=q").unwrap();
url.append_path("/");
assert_eq!(url.as_str(), "https://www.microsoft.com/?q=q");

url = Url::parse("https://www.microsoft.com?q=q").unwrap();
url.append_path("");
assert_eq!(url.as_str(), "https://www.microsoft.com/?q=q");

url = Url::parse("https://www.microsoft.com?q=q").unwrap();
url.append_path("");
assert_eq!(url.as_str(), "https://www.microsoft.com/?q=q");

url = Url::parse("https://www.microsoft.com/foo?q=q").unwrap();
url.append_path("/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo?q=q");

url = Url::parse("https://www.microsoft.com/foo/?q=q").unwrap();
url.append_path("/");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/?q=q");

url = Url::parse("https://www.microsoft.com/foo?q=q").unwrap();
url.append_path("");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo?q=q");

url = Url::parse("https://www.microsoft.com/foo/?q=q").unwrap();
url.append_path("");
assert_eq!(url.as_str(), "https://www.microsoft.com/foo/?q=q");
}
}
5 changes: 5 additions & 0 deletions sdk/core/typespec_client_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ pub use typespec::Bytes;
pub use uuid::Uuid;

pub use sleep::sleep;

mod private {
pub trait Sealed {}
impl Sealed for crate::http::Url {}
}