mod error;
mod http;
#[cfg(feature = "transport-policy")]
#[cfg_attr(docsrs, doc(cfg(feature = "transport-policy")))]
pub mod policy;
#[cfg(not(target_arch = "wasm32"))]
pub mod reqwest;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
pub mod fetch;
pub use error::TransportError;
pub use http::{HttpTransport, TransportResponse};
#[cfg(not(target_arch = "wasm32"))]
pub use self::reqwest::{ReqwestTransport, ReqwestTransportConfig, classify_reqwest_error};
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
pub use self::fetch::{FetchTransport, FetchTransportConfig};
pub use crate::validation::TransportErrorClass;
const CUSTOM_OVERRIDE_ROUTE_IDENTITY: &str = "<custom override>";
#[must_use]
#[allow(
clippy::option_if_let_else,
reason = "the Ok arm binds an intermediate origin and carries a nested conditional; the combinator form would collapse that multi-statement body into a closure and obscure the two-branch parallel structure"
)]
pub fn sanitize_public_base_url(base_url: &str) -> String {
match url::Url::parse(base_url) {
Ok(url) => {
let origin = url.origin().ascii_serialization();
if origin == "null" {
CUSTOM_OVERRIDE_ROUTE_IDENTITY.to_owned()
} else {
origin.trim_end_matches('/').to_owned()
}
}
Err(_) => CUSTOM_OVERRIDE_ROUTE_IDENTITY.to_owned(),
}
}
#[cfg(feature = "tracing")]
#[must_use]
pub fn span_endpoint(path: &str) -> &str {
let has_authority = path.contains("://");
if has_authority && sanitize_public_base_url(path) == CUSTOM_OVERRIDE_ROUTE_IDENTITY {
return "/";
}
let endpoint = path.find("://").map_or(path, |scheme_end| {
let after_authority = &path[scheme_end + 3..];
after_authority
.find('/')
.map_or("/", |path_start| &after_authority[path_start..])
});
let end = endpoint.find(['?', '#']).unwrap_or(endpoint.len());
let endpoint = &endpoint[..end];
if endpoint.is_empty() && has_authority {
"/"
} else {
endpoint
}
}
#[must_use]
pub fn join_request_url(base_url: &str, path: &str) -> String {
if path.starts_with("http://") || path.starts_with("https://") || base_url.is_empty() {
path.to_owned()
} else if path.starts_with('/') {
format!("{base_url}{path}")
} else {
format!("{base_url}/{path}")
}
}
#[cfg(test)]
mod join_request_url_tests {
use super::join_request_url;
#[test]
fn absolute_http_path_returns_path_verbatim() {
assert_eq!(
join_request_url("https://api.example.com", "http://other.example/orders"),
"http://other.example/orders"
);
}
#[test]
fn absolute_https_path_returns_path_verbatim() {
assert_eq!(
join_request_url("https://api.example.com", "https://other.example/orders"),
"https://other.example/orders"
);
}
#[test]
fn empty_base_returns_path_verbatim() {
assert_eq!(join_request_url("", "/api/v1/orders"), "/api/v1/orders");
}
#[test]
fn leading_slash_path_concatenates_without_inserted_slash() {
assert_eq!(
join_request_url("https://api.example.com", "/api/v1/orders"),
"https://api.example.com/api/v1/orders"
);
}
#[test]
fn non_leading_slash_path_concatenates_with_inserted_slash() {
assert_eq!(
join_request_url("https://api.example.com", "api/v1/orders"),
"https://api.example.com/api/v1/orders"
);
}
}