use core::fmt;
use core::str::FromStr;
use serde::de::Error as _;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{Did, DidError, DidUrl};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Uri(String);
impl Uri {
pub fn parse(s: &str) -> Result<Self, DidError> {
validate_uri(s)?;
Ok(Self(s.to_owned()))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn scheme(&self) -> &str {
self.0.split(':').next().unwrap_or_default()
}
pub fn is_did_url(&self) -> bool {
self.scheme() == "did"
}
}
fn validate_uri(s: &str) -> Result<(), DidError> {
let bytes = s.as_bytes();
let colon = bytes
.iter()
.position(|&b| b == b':')
.ok_or(DidError::InvalidChar {
position: bytes.len(),
ch: ':',
})?;
if colon == 0 || !bytes[0].is_ascii_alphabetic() {
return Err(DidError::InvalidChar {
position: 0,
ch: s.chars().next().unwrap_or(':'),
});
}
if let Some(i) = bytes[..colon]
.iter()
.position(|&b| !(b.is_ascii_alphanumeric() || matches!(b, b'+' | b'-' | b'.')))
{
return Err(DidError::InvalidChar {
position: i,
ch: char::from(bytes[i]),
});
}
let mut hashes = 0;
let mut i = colon + 1;
while i < bytes.len() {
match bytes[i] {
b'%' => {
let ok = bytes.len() >= i + 3
&& bytes[i + 1].is_ascii_hexdigit()
&& bytes[i + 2].is_ascii_hexdigit();
if !ok {
return Err(DidError::InvalidPercentEncoding { position: i });
}
i += 3;
continue;
}
b'#' => {
hashes += 1;
if hashes > 1 {
return Err(DidError::InvalidChar {
position: i,
ch: '#',
});
}
}
b if is_uri_char(b) => {}
_ => {
let ch = s[i..].chars().next().unwrap_or('\0');
return Err(DidError::InvalidChar { position: i, ch });
}
}
i += 1;
}
Ok(())
}
fn is_uri_char(b: u8) -> bool {
b.is_ascii_alphanumeric()
|| matches!(
b,
b'-' | b'.'
| b'_'
| b'~'
| b':'
| b'/'
| b'?'
| b'['
| b']'
| b'@'
| b'!'
| b'$'
| b'&'
| b'\''
| b'('
| b')'
| b'*'
| b'+'
| b','
| b';'
| b'='
)
}
impl fmt::Display for Uri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl AsRef<str> for Uri {
fn as_ref(&self) -> &str {
&self.0
}
}
impl FromStr for Uri {
type Err = DidError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
impl TryFrom<&str> for Uri {
type Error = DidError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Self::parse(s)
}
}
impl TryFrom<String> for Uri {
type Error = DidError;
fn try_from(s: String) -> Result<Self, Self::Error> {
validate_uri(&s)?;
Ok(Self(s))
}
}
impl From<Did> for Uri {
fn from(did: Did) -> Self {
Self(did.as_str().to_owned())
}
}
impl From<DidUrl> for Uri {
fn from(url: DidUrl) -> Self {
Self(url.as_str().to_owned())
}
}
impl From<&Did> for Uri {
fn from(did: &Did) -> Self {
Self(did.as_str().to_owned())
}
}
impl From<&DidUrl> for Uri {
fn from(url: &DidUrl) -> Self {
Self(url.as_str().to_owned())
}
}
impl PartialEq<str> for Uri {
fn eq(&self, other: &str) -> bool {
self.0 == other
}
}
impl PartialEq<&str> for Uri {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}
impl Serialize for Uri {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0)
}
}
impl<'de> Deserialize<'de> for Uri {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
Self::try_from(s).map_err(D::Error::custom)
}
}