|
6 | 6 | #![cfg_attr(not(test), deny(clippy::todo))]
|
7 | 7 | #![cfg_attr(not(test), deny(clippy::unimplemented))]
|
8 | 8 |
|
| 9 | +use http::{uri::PathAndQuery, Uri}; |
9 | 10 | use hyper::{
|
10 | 11 | header::HeaderValue,
|
11 | 12 | http::uri::{self},
|
@@ -229,6 +230,40 @@ impl Endpoint {
|
229 | 230 | /// Default value for the timeout field in milliseconds.
|
230 | 231 | pub const DEFAULT_TIMEOUT: u64 = 3_000;
|
231 | 232 |
|
| 233 | + /// Add a path to the url of an `Endpoint`. |
| 234 | + /// |
| 235 | + /// The given path must start with a slash (e.g. "/v0.4/traces"). |
| 236 | + /// Returns an error if the path is not valid. |
| 237 | + pub fn add_path(&mut self, path: &str) -> anyhow::Result<()> { |
| 238 | + let mut parts = self.url.clone().into_parts(); |
| 239 | + parts.path_and_query = Some(match parts.path_and_query { |
| 240 | + Some(pq) => { |
| 241 | + let p = pq.path(); |
| 242 | + let mut p = p.strip_suffix('/').unwrap_or(p).to_owned(); |
| 243 | + p.push_str(path); |
| 244 | + if let Some(q) = pq.query() { |
| 245 | + p.push('?'); |
| 246 | + p.push_str(q); |
| 247 | + } |
| 248 | + PathAndQuery::from_str(p.as_str()) |
| 249 | + } |
| 250 | + None => PathAndQuery::from_str(path), |
| 251 | + }?); |
| 252 | + self.url = Uri::from_parts(parts)?; |
| 253 | + Ok(()) |
| 254 | + } |
| 255 | + |
| 256 | + /// Create a new `Endpoint` by extending the url with the given path. |
| 257 | + /// |
| 258 | + /// The given path must start with a slash (e.g. "/v0.4/traces"). |
| 259 | + /// Returns an error if the path is not valid. |
| 260 | + /// All the other fields are copied. |
| 261 | + pub fn try_clone_with_subpath(&self, path: &str) -> anyhow::Result<Self> { |
| 262 | + let mut endpoint = self.clone(); |
| 263 | + endpoint.add_path(path)?; |
| 264 | + Ok(endpoint) |
| 265 | + } |
| 266 | + |
232 | 267 | /// Return a request builder with the following headers:
|
233 | 268 | /// - User agent
|
234 | 269 | /// - Api key
|
@@ -286,3 +321,31 @@ impl Endpoint {
|
286 | 321 | }
|
287 | 322 | }
|
288 | 323 | }
|
| 324 | + |
| 325 | +#[cfg(test)] |
| 326 | +mod tests { |
| 327 | + use super::*; |
| 328 | + |
| 329 | + #[test] |
| 330 | + fn test_add_path() { |
| 331 | + let test_cases = [ |
| 332 | + ("http://test.com/", "/foo", "http://test.com/foo"), |
| 333 | + ("http://test.com/bar", "/foo", "http://test.com/bar/foo"), |
| 334 | + ( |
| 335 | + "http://test.com/bar", |
| 336 | + "/foo/baz", |
| 337 | + "http://test.com/bar/foo/baz", |
| 338 | + ), |
| 339 | + ( |
| 340 | + "http://test.com/bar?data=dog&product=apm", |
| 341 | + "/foo/baz", |
| 342 | + "http://test.com/bar/foo/baz?data=dog&product=apm", |
| 343 | + ), |
| 344 | + ]; |
| 345 | + for (url, path, expected) in test_cases { |
| 346 | + let mut endpoint = Endpoint::from_url(url.parse().unwrap()); |
| 347 | + endpoint.add_path(path).unwrap(); |
| 348 | + assert_eq!(endpoint.url, expected); |
| 349 | + } |
| 350 | + } |
| 351 | +} |
0 commit comments