use std::{borrow::Cow, fmt, ops::Deref};
use iri_rs::{Iri, IriBuf};
use crate::{RDF_DIR_LANG_STRING, RDF_LANG_STRING, RdfDisplay, XSD_STRING};
#[derive(Debug, thiserror::Error)]
pub enum ReservedDatatypeError {
#[error("rdf:langString cannot be a typed-literal datatype; use LiteralType::lang_string")]
LangString(IriBuf),
#[error("rdf:dirLangString cannot be a typed-literal datatype; use LiteralType::dir_lang_string")]
DirLangString(IriBuf),
}
impl ReservedDatatypeError {
pub const fn iri(&self) -> &IriBuf {
match self {
Self::LangString(i) | Self::DirLangString(i) => i,
}
}
pub fn into_iri(self) -> IriBuf {
match self {
Self::LangString(i) | Self::DirLangString(i) => i,
}
}
}
fn classify_reserved(iri: IriBuf) -> Result<IriBuf, ReservedDatatypeError> {
if iri.as_str() == RDF_LANG_STRING.as_str() {
Err(ReservedDatatypeError::LangString(iri))
} else if iri.as_str() == RDF_DIR_LANG_STRING.as_str() {
Err(ReservedDatatypeError::DirLangString(iri))
} else {
Ok(iri)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
pub struct Datatype(IriBuf);
impl Datatype {
pub fn new(iri: IriBuf) -> Result<Self, ReservedDatatypeError> {
classify_reserved(iri).map(Self)
}
pub fn xsd_string() -> Self {
Self(IriBuf::from(XSD_STRING))
}
pub fn into_iri(self) -> IriBuf {
self.0
}
pub fn as_iri(&self) -> Iri<&str> {
self.0.as_ref()
}
pub fn as_datatype_ref(&self) -> DatatypeRef<'_> {
DatatypeRef(self.0.as_ref())
}
pub const unsafe fn new_unchecked(iri: IriBuf) -> Self {
Self(iri)
}
}
impl Deref for Datatype {
type Target = IriBuf;
fn deref(&self) -> &IriBuf {
&self.0
}
}
impl AsRef<IriBuf> for Datatype {
fn as_ref(&self) -> &IriBuf {
&self.0
}
}
impl fmt::Display for Datatype {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl RdfDisplay for Datatype {
fn rdf_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.rdf_fmt(f)
}
}
impl TryFrom<IriBuf> for Datatype {
type Error = ReservedDatatypeError;
fn try_from(value: IriBuf) -> Result<Self, Self::Error> {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
pub struct DatatypeRef<'a>(Iri<&'a str>);
impl<'a> DatatypeRef<'a> {
pub fn new(iri: Iri<&'a str>) -> Result<Self, ReservedDatatypeError> {
if iri.as_str() == RDF_LANG_STRING.as_str() {
Err(ReservedDatatypeError::LangString(iri.into()))
} else if iri.as_str() == RDF_DIR_LANG_STRING.as_str() {
Err(ReservedDatatypeError::DirLangString(iri.into()))
} else {
Ok(Self(iri))
}
}
pub const fn as_iri(self) -> Iri<&'a str> {
self.0
}
pub fn to_owned(self) -> Datatype {
unsafe { Datatype::new_unchecked(self.0.into()) }
}
pub const unsafe fn new_unchecked(iri: Iri<&'a str>) -> Self {
Self(iri)
}
}
impl fmt::Display for DatatypeRef<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl RdfDisplay for DatatypeRef<'_> {
fn rdf_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.rdf_fmt(f)
}
}
pub type CowDatatype<'a> = Cow<'a, Datatype>;
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Datatype {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let iri = IriBuf::deserialize(deserializer)?;
Datatype::new(iri).map_err(|e| {
<D::Error as serde::de::Error>::invalid_value(
serde::de::Unexpected::Str(e.iri().as_str()),
&"a non-rdf:langString, non-rdf:dirLangString datatype IRI",
)
})
}
}