Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8c5ae36
Add append_path() for Url
antkmsft Oct 9, 2025
809f118
Fix unit test and format
antkmsft Oct 9, 2025
c1c26b1
Don't append simgle slash
antkmsft Oct 9, 2025
fd2ba26
UrlExt
antkmsft Oct 10, 2025
b1ffb45
Update sdk/typespec/typespec_client_core/src/http/mod.rs
antkmsft Oct 10, 2025
e0655f5
Update sdk/typespec/typespec_client_core/src/http/mod.rs
antkmsft Oct 10, 2025
e64f1ce
Move tests
antkmsft Oct 10, 2025
96ec4f3
Update sdk/typespec/typespec_client_core/src/http/mod.rs
antkmsft Oct 13, 2025
306619f
Update sdk/typespec/typespec_client_core/src/http/mod.rs
antkmsft Oct 13, 2025
2d71a79
Update sdk/typespec/typespec_client_core/src/http/mod.rs
antkmsft Oct 13, 2025
ccbe945
Update sdk/typespec/typespec_client_core/src/http/mod.rs
antkmsft Oct 13, 2025
d89d516
Update sdk/typespec/typespec_client_core/src/http/mod.rs
antkmsft Oct 13, 2025
a2d6a2c
Apply most of the feedback
antkmsft Oct 14, 2025
5f8e5a5
Format instead of +
antkmsft Oct 14, 2025
a891e06
Concatenate insted of format
antkmsft Oct 14, 2025
35056f7
append_path("/...") sets new path
antkmsft Oct 20, 2025
97aad03
Revert "append_path("/...") sets new path"
antkmsft Oct 20, 2025
8fc2c67
Merge remote-tracking branch 'upstream/main' into url-append-path
antkmsft Oct 20, 2025
2fbe7ad
Add inisignificant change to re-trigger " license/cla" pipeline that …
antkmsft Oct 20, 2025
4fedcc2
pub use from core and update changelogs
antkmsft Oct 21, 2025
0734709
Merge branch 'url-append-path' of https://github.com/antkmsft/azure-s…
antkmsft Oct 21, 2025
0717002
Update sdk/core/typespec_client_core/src/http/mod.rs
antkmsft Oct 22, 2025
366ac4f
Clippy
antkmsft Oct 22, 2025
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
2 changes: 2 additions & 0 deletions sdk/core/azure_core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Features Added

- Added `UrlExt::append_path()`.

### Breaking Changes

- Moved deserializers and serializers for optional base64-encoded bytes to `base64::option` module. `base64` module now deserializes or serializes non-optional fields congruent with the `time` module.
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/azure_core/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub use response::{AsyncResponse, BufResponse, RawResponse, Response};
pub use typespec_client_core::http::response;
pub use typespec_client_core::http::{
new_http_client, AppendToUrlQuery, Context, DeserializeWith, Format, HttpClient, JsonFormat,
Method, NoFormat, StatusCode, Url,
Method, NoFormat, StatusCode, Url, UrlExt,
};

pub use crate::error::check_success;
Expand Down
2 changes: 2 additions & 0 deletions sdk/core/typespec_client_core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Features Added

- Added `UrlExt::append_path()`.

### Breaking Changes

- Moved deserializers and serializers for optional base64-encoded bytes to `base64::option` module. `base64` module now deserializes or serializes non-optional fields congruent with the `time` module.
Expand Down
150 changes: 150 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,153 @@ 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>) {
let path = p.as_ref().trim_start_matches('/');
if self.path() == "/" {
self.set_path(path);
return;
}
if path.is_empty() {
return;
}
let needs_separator = !self.path().ends_with('/');
let mut new_len = self.path().len() + path.len();
if needs_separator {
new_len += 1;
}
let mut new_path = String::with_capacity(new_len);
debug_assert_eq!(new_path.capacity(), new_len);
new_path.push_str(self.path());
if needs_separator {
new_path.push('/');
}
new_path.push_str(path);
debug_assert_eq!(new_path.capacity(), new_len);

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 {}
}