use core::fmt::{self, Display};
use core::net::IpAddr;
use core::ops::{Deref, DerefMut};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub use url::{Host, ParseError as UriParseError, Position, Url as Uri};
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RelativeRef(Uri);
impl RelativeRef {
const FAKE_SCHEME: &'static str = "base";
const FAKE_RELATIVE_HOST: &'static str = ".";
pub fn parse(input: &str) -> Result<Self, UriParseError> {
if input.starts_with('/') {
Uri::parse(&format!("{}://{}", Self::FAKE_SCHEME, input)).map(Self)
} else {
Uri::parse(&format!(
"{}://{}/{}",
Self::FAKE_SCHEME,
Self::FAKE_RELATIVE_HOST,
input,
))
.map(Self)
}
}
pub fn as_str(&self) -> &str {
match self.0.domain() {
None => &self.0[Position::BeforePath..],
Some(".") => self.0[Position::BeforePath..].trim_start_matches('/'),
_ => unreachable!(),
}
}
pub fn path(&self) -> &str {
match self.0.domain() {
None => self.0.path(),
Some(".") => self.0.path().trim_start_matches('/'),
_ => unreachable!(),
}
}
pub fn join(&self, input: &str) -> Result<Self, UriParseError> {
self.0.join(input).map(Self)
}
pub fn set_ip_host(&mut self, address: IpAddr) {
let _ = address;
}
pub fn set_host(&mut self, host: &str) {
let _ = host;
}
pub fn authority(&self) -> &str {
""
}
pub fn scheme(&self) -> &str {
""
}
pub fn host(&self) -> Option<Host<&str>> {
None
}
pub fn host_str(&self) -> Option<&str> {
None
}
pub fn has_host(&self) -> bool {
false
}
pub fn domain(&self) -> Option<&str> {
None
}
}
impl Deref for RelativeRef {
type Target = Uri;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for RelativeRef {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Display for RelativeRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
impl Serialize for RelativeRef {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.as_str().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for RelativeRef {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::parse(&s).map_err(D::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simple_reflexivity() {
let paths = [
"abc",
"/abc",
"abc/def",
"abc/def/",
"abc/def/*",
"abc?q=1#title",
];
for path in paths {
assert_eq!(path, RelativeRef::parse(path).unwrap().to_string());
}
}
}