use crate::Protocol;
#[cfg(feature = "std")]
use crate::address::HostWithPort;
use crate::address::{Domain, Host, HostWithOptPort};
#[cfg(feature = "std")]
use crate::client::ConnectorTarget;
#[cfg(feature = "http")]
use crate::http::Version;
use crate::transport::TransportProtocol;
use crate::uri::{PathRef, Uri};
#[cfg(feature = "std")]
use rama_core::extensions::ExtensionsRef;
pub trait UriInputExt {
fn uri(&self) -> &Uri;
}
impl<T: UriInputExt + ?Sized> UriInputExt for &T {
fn uri(&self) -> &Uri {
(**self).uri()
}
}
impl UriInputExt for Uri {
fn uri(&self) -> &Uri {
self
}
}
pub trait PathInputExt {
fn path_ref(&self) -> PathRef<'_>;
}
impl<T: PathInputExt + ?Sized> PathInputExt for &T {
fn path_ref(&self) -> PathRef<'_> {
(**self).path_ref()
}
}
impl PathInputExt for Uri {
fn path_ref(&self) -> PathRef<'_> {
self.path_ref_or_root()
}
}
pub trait AuthorityInputExt {
fn authority(&self) -> Option<HostWithOptPort>;
fn host(&self) -> Option<Host> {
self.authority().map(|a| a.host)
}
fn host_as_domain(&self) -> Option<Domain> {
self.authority().and_then(|a| a.host.try_into_domain().ok())
}
fn port(&self) -> Option<u16> {
self.authority().and_then(|a| a.port_u16())
}
}
impl<T: AuthorityInputExt + ?Sized> AuthorityInputExt for &T {
fn authority(&self) -> Option<HostWithOptPort> {
(**self).authority()
}
}
pub trait ProtocolInputExt {
fn protocol(&self) -> Option<&Protocol>;
fn protocol_default_port(&self) -> Option<u16> {
self.protocol().and_then(|p| p.default_port())
}
}
impl<T: ProtocolInputExt + ?Sized> ProtocolInputExt for &T {
fn protocol(&self) -> Option<&Protocol> {
(**self).protocol()
}
}
#[cfg(feature = "http")]
pub trait HttpVersionInputExt {
fn http_version(&self) -> Option<Version>;
}
#[cfg(feature = "http")]
impl<T: HttpVersionInputExt + ?Sized> HttpVersionInputExt for &T {
fn http_version(&self) -> Option<Version> {
(**self).http_version()
}
}
pub trait TransportProtocolInputExt {
fn transport_protocol(&self) -> Option<TransportProtocol>;
}
impl<T: TransportProtocolInputExt + ?Sized> TransportProtocolInputExt for &T {
fn transport_protocol(&self) -> Option<TransportProtocol> {
(**self).transport_protocol()
}
}
#[cfg(feature = "std")]
mod private {
use super::{AuthorityInputExt, ProtocolInputExt};
pub trait Sealed {}
impl<T: AuthorityInputExt + ProtocolInputExt + ?Sized> Sealed for T {}
}
#[cfg(feature = "std")]
pub trait ConnectorTargetInputExt:
AuthorityInputExt + ProtocolInputExt + ExtensionsRef + private::Sealed
{
fn connector_target(&self) -> Option<HostWithPort> {
if let Some(ConnectorTarget(target)) = self.extensions().get_ref() {
return Some(target.clone());
}
self.authority()
.and_then(|a| a.into_host_with_port(self.protocol_default_port()))
}
fn connector_target_with_default_port(&self, default_port: u16) -> Option<HostWithPort> {
if let Some(ConnectorTarget(target)) = self.extensions().get_ref() {
return Some(target.clone());
}
self.authority()
.map(|a| a.into_host_with_port_or(self.protocol_default_port().unwrap_or(default_port)))
}
}
#[cfg(feature = "std")]
impl<T: AuthorityInputExt + ProtocolInputExt + ExtensionsRef + ?Sized> ConnectorTargetInputExt
for T
{
}
#[cfg(test)]
mod tests {
use super::{PathInputExt, UriInputExt};
use crate::uri::{PathPattern, Uri};
#[test]
fn uri_input_ref_forwards_to_inner_uri() {
let uri: Uri = "https://example.com/a%2Fb?q=1".parse().unwrap();
let uri_ref = &uri;
let forwarded = <&Uri as UriInputExt>::uri(&uri_ref);
assert_eq!(forwarded.path_ref_or_root(), "/a%2Fb");
assert_ne!(forwarded.path_ref_or_root(), "/a/b");
}
#[test]
fn path_input_ref_forwards_to_inner_path() {
let uri: Uri = "https://example.com/a%2Fb?q=1".parse().unwrap();
let uri_ref = &uri;
let forwarded = <&Uri as PathInputExt>::path_ref(&uri_ref);
assert_eq!(uri.path_ref(), "/a%2Fb");
assert_eq!(forwarded, "/a%2Fb");
assert_ne!(forwarded, "/a/b");
}
#[test]
fn path_input_for_uri_uses_root_fallback() {
let uri: Uri = "https://example.com".parse().unwrap();
assert_eq!(uri.path_ref(), "/");
}
#[test]
fn uri_pattern_helpers_route_through_typed_path() {
let uri: Uri = "https://example.com/api/acme/widgets".parse().unwrap();
let pattern = PathPattern::new("/api/{tenant}/widgets");
let miss = PathPattern::new("/api/{tenant}/orders");
assert!(uri.is_pattern_match(&pattern));
assert!(!uri.is_pattern_match(&miss));
let captures = uri.pattern_captures(&pattern).unwrap();
assert_eq!(captures.get("tenant"), Some("acme"));
assert!(uri.pattern_captures(&miss).is_none());
}
}