use super::Scheme;
use iri_string::{
format::ToDedicatedString,
types::{IriAbsoluteStr, UriAbsoluteStr, UriAbsoluteString},
};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AbsoluteUri(pub(super) UriAbsoluteString);
impl AbsoluteUri {
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn scheme(&self) -> Scheme {
Scheme(self.0.scheme_str())
}
}
impl From<&UriAbsoluteStr> for AbsoluteUri {
fn from(value: &UriAbsoluteStr) -> Self {
Self(value.to_owned())
}
}
impl TryFrom<&str> for AbsoluteUri {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let absolute_iri = IriAbsoluteStr::new(value)
.or(Err(format!("String '{value}' is not a valid Absolute IRI.")).to_owned())?;
Ok(Self(absolute_iri.encode_to_uri().to_dedicated_string()))
}
}
impl TryFrom<&Path> for AbsoluteUri {
type Error = String;
fn try_from(value: &Path) -> Result<Self, Self::Error> {
fn path_to_absolute(
prefix: &str,
value: &Path,
lstrip_value: usize,
) -> Result<AbsoluteUri, String> {
if let Some(path) = value.to_str() {
let path = path.replace(std::path::MAIN_SEPARATOR, "/");
format!("{}{}", prefix, &path[lstrip_value..])
.as_str()
.try_into()
} else {
Err(format!(
"Path '{value:?}' contains invalid characters that can't be represented by UTF-8."
))
}
}
if value.is_relative() {
return Err(format!(
"Cannot create Absolute URI from relative path '{value:?}'."
));
}
let mut components = value.components();
match components.next() {
Some(std::path::Component::Prefix(component)) => {
if let std::path::Prefix::Disk(_) = component.kind() {
path_to_absolute("file:///", value, 0)
} else if let std::path::Prefix::VerbatimDisk(_) = component.kind() {
path_to_absolute("file:///", value, 4)
} else {
Err(format!("Only absolute paths beginning with a drive letter can be converted to Absolute URIs but not {value:?}"))
}
}
Some(std::path::Component::RootDir) => path_to_absolute("file://", value, 0),
_ => Err(format!(
"Provided path ({value:?}) doesn't start with a prefix or a root directory."
)),
}
}
}
impl TryFrom<&AbsoluteUri> for PathBuf {
type Error = String;
fn try_from(value: &AbsoluteUri) -> Result<Self, Self::Error> {
if value.scheme() != Scheme::FILE {
return Err(format!(
"Absolute URI {value:?} does not have a file scheme"
));
}
#[cfg(unix)]
let path = Path::new(value.0.path_str());
#[cfg(windows)]
let path = Path::new(&value.0.path_str()[1..]);
if path.is_relative() {
Err(format!("Absolute URI {value:?} contains relative path"))
} else {
Ok(path.to_owned())
}
}
}