use core::fmt;
use core::str::FromStr;
use serde::de::Error as _;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::syntax::{check_len, scan_url_suffix};
use super::{DEFAULT_MAX_LEN, Did, DidError, DidUrl};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum DidUrlReference {
Absolute(DidUrl),
Relative(String),
}
impl DidUrlReference {
pub fn parse(s: &str) -> Result<Self, DidError> {
if s.starts_with(['/', '?', '#']) {
check_len(s, DEFAULT_MAX_LEN)?;
scan_url_suffix(s, 0)?;
Ok(Self::Relative(s.to_owned()))
} else {
DidUrl::parse(s).map(Self::Absolute)
}
}
pub fn is_relative(&self) -> bool {
matches!(self, Self::Relative(_))
}
pub fn as_str(&self) -> &str {
match self {
Self::Absolute(url) => url.as_str(),
Self::Relative(s) => s,
}
}
pub fn resolve(&self, base: &Did) -> Result<DidUrl, DidError> {
match self {
Self::Absolute(url) => Ok(url.clone()),
Self::Relative(s) => base.join(s),
}
}
pub fn refers_to(&self, url: &DidUrl, base: &Did) -> bool {
match self {
Self::Absolute(this) => this == url,
Self::Relative(_) => self.resolve(base).is_ok_and(|resolved| &resolved == url),
}
}
}
impl fmt::Display for DidUrlReference {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for DidUrlReference {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl FromStr for DidUrlReference {
type Err = DidError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
impl TryFrom<&str> for DidUrlReference {
type Error = DidError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Self::parse(s)
}
}
impl TryFrom<String> for DidUrlReference {
type Error = DidError;
fn try_from(s: String) -> Result<Self, Self::Error> {
Self::parse(&s)
}
}
impl From<DidUrl> for DidUrlReference {
fn from(url: DidUrl) -> Self {
Self::Absolute(url)
}
}
impl From<&DidUrl> for DidUrlReference {
fn from(url: &DidUrl) -> Self {
Self::Absolute(url.clone())
}
}
impl Serialize for DidUrlReference {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for DidUrlReference {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
Self::parse(&s).map_err(D::Error::custom)
}
}