use super::{AbsoluteUri, Authority, Fragment, Query, Uri};
use iri_string::{
format::ToDedicatedString,
types::{IriRelativeStr, UriRelativeStr, UriRelativeString},
};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RelativeReference(pub(super) UriRelativeString);
impl RelativeReference {
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn empty() -> Self {
Self("".try_into().unwrap())
}
pub fn resolve(&self, absolute: &AbsoluteUri) -> Uri {
Uri(self.0.resolve_against(&absolute.0).and_normalize().into())
}
pub fn authority(&self) -> Option<Authority> {
self.0.authority_str().map(Authority)
}
pub fn query(&self) -> Option<Query> {
self.0.query().map(Query::from)
}
#[expect(unused_variables)]
pub fn set_query(&mut self, query: Option<&Query>) {
todo!()
}
pub fn fragment(&self) -> Option<Fragment> {
self.0.fragment().map(Fragment::from)
}
pub fn set_fragment(&mut self, fragment: Option<&Fragment>) {
self.0.set_fragment(fragment.map(|f| f.0.as_slice()));
}
}
impl From<&UriRelativeStr> for RelativeReference {
fn from(value: &UriRelativeStr) -> Self {
Self(value.to_owned())
}
}
impl TryFrom<&Path> for RelativeReference {
type Error = String;
fn try_from(value: &Path) -> Result<Self, Self::Error> {
if value.is_absolute() {
Err(format!(
"Cannot convert an absolute path {value:?} into a Relative Reference."
))
} else {
let path_string = value
.to_str()
.ok_or(format!(
"Provided path {value:?} cannot be converted into a Relative Reference."
))?
.replace(std::path::MAIN_SEPARATOR, "/");
let relative_reference = IriRelativeStr::new(&path_string).or(Err(format!(
"Provided path {value:?} cannot be converted into a Relative Reference."
)))?;
Ok(Self(
relative_reference.encode_to_uri().to_dedicated_string(),
))
}
}
}
impl TryFrom<&RelativeReference> for PathBuf {
type Error = String;
fn try_from(value: &RelativeReference) -> Result<Self, Self::Error> {
if value.fragment().is_some() {
Err(format!(
"Can't convert Relative Reference with a fragment {value:?} into a path."
))
} else if value.query().is_some() {
Err(format!(
"Can't convert Relative Reference with a query {value:?} into a path."
))
} else if !value.authority().unwrap_or(Authority::EMPTY).is_empty() {
Err(format!(
"Can't convert Relative Reference with a non-empty authority {value:?} into a path."
))
} else {
Ok(value.as_str().into())
}
}
}