use std::str::FromStr;
use dizzy::DstNewtype;
use thiserror::Error;
pub use language_tags::ParseError as LanguageTagParseError;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LanguageTag(language_tags::LanguageTag);
impl LanguageTag {
pub fn parse(s: &str) -> Result<Self, language_tags::ParseError> {
language_tags::LanguageTag::parse(s).map(LanguageTag)
}
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
#[inline]
pub fn primary_language(&self) -> &str {
self.0.primary_language()
}
}
impl FromStr for LanguageTag {
type Err = language_tags::ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
impl std::fmt::Display for LanguageTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum InvalidUidError {
#[error("expected at least one character")]
EmptyString,
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, DstNewtype)]
#[dizzy(invariant = Uid::str_is_uid, error = InvalidUidError)]
#[dizzy(constructor = pub new)]
#[dizzy(getter = pub const as_str)]
#[dizzy(derive(Debug, CloneBoxed, IntoBoxed))]
#[dizzy(owned = pub UidBuf(String))]
#[dizzy(derive_owned(Debug, IntoBoxed))]
#[repr(transparent)]
pub struct Uid(str);
impl std::fmt::Display for Uid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl Uid {
fn str_is_uid(s: &str) -> Result<(), InvalidUidError> {
if s.is_empty() {
return Err(InvalidUidError::EmptyString);
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum InvalidUriError {
#[error("expected at least one character")]
EmptyString,
#[error("missing colon after scheme")]
MissingColon,
#[error("scheme must start with a letter")]
SchemeStartsWithNonLetter,
#[error("invalid character in scheme: {c}")]
InvalidSchemeChar { index: usize, c: char },
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, DstNewtype)]
#[dizzy(invariant = Uri::str_is_uri, error = InvalidUriError)]
#[dizzy(constructor = pub new)]
#[dizzy(getter = pub const as_str)]
#[dizzy(derive(Debug, CloneBoxed, IntoBoxed))]
#[dizzy(owned = pub UriBuf(String))]
#[dizzy(derive_owned(Debug, IntoBoxed))]
#[repr(transparent)]
pub struct Uri(str);
impl std::fmt::Display for Uri {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl Uri {
fn str_is_uri(s: &str) -> Result<(), InvalidUriError> {
let (scheme, _rest) = s.split_once(':').ok_or(if s.is_empty() {
InvalidUriError::EmptyString
} else {
InvalidUriError::MissingColon
})?;
let mut chars = scheme.chars().enumerate();
match chars.next() {
None => return Err(InvalidUriError::MissingColon),
Some((_, c)) if !c.is_ascii_alphabetic() => {
return Err(InvalidUriError::SchemeStartsWithNonLetter);
}
Some(_) => {}
}
for (index, c) in chars {
if !c.is_ascii_alphanumeric() && c != '+' && c != '-' && c != '.' {
return Err(InvalidUriError::InvalidSchemeChar { index, c });
}
}
Ok(())
}
#[inline(always)]
pub fn scheme(&self) -> &str {
self.as_str()
.split_once(':')
.expect("a Uri must contain a colon")
.0
}
}