use crate::iri_exclusion::IriExclusion;
use crate::language_exclusion::LanguageExclusion;
use crate::literal_exclusion::LiteralExclusion;
use prefixmap::IriRef;
use rudof_rdf::rdf_core::term::literal::Lang;
use serde::de::{MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserialize, Serialize, Serializer, de};
use std::str::FromStr;
use std::{fmt, result};
#[derive(Debug, PartialEq, Clone)]
pub enum Exclusion {
LiteralExclusion(LiteralExclusion),
LanguageExclusion(LanguageExclusion),
IriExclusion(IriExclusion),
Untyped(String),
}
#[derive(Debug)]
pub struct SomeNoLitExclusion {
pub exc: Exclusion,
}
#[derive(Debug)]
pub struct SomeNoIriExclusion {
pub exc: Exclusion,
}
#[derive(Debug)]
pub struct SomeNoLanguageExclusion {
pub exc: Exclusion,
}
impl Exclusion {
pub fn parse_literal_exclusions(excs: Vec<Exclusion>) -> Result<Vec<LiteralExclusion>, SomeNoLitExclusion> {
let mut lit_excs = Vec::new();
for e in excs {
match e {
Exclusion::LiteralExclusion(le) => lit_excs.push(le),
Exclusion::Untyped(s) => lit_excs.push(LiteralExclusion::Literal(s)),
other => return Err(SomeNoLitExclusion { exc: other }),
}
}
Ok(lit_excs)
}
pub fn parse_iri_exclusions(excs: Vec<Exclusion>) -> Result<Vec<IriExclusion>, SomeNoIriExclusion> {
let mut iri_excs = Vec::new();
for e in excs {
match &e {
Exclusion::IriExclusion(le) => iri_excs.push(le.clone()),
v @ Exclusion::Untyped(s) => {
let iri = FromStr::from_str(s.as_str()).map_err(|_e| SomeNoIriExclusion { exc: v.clone() })?;
iri_excs.push(IriExclusion::Iri(iri))
},
other => return Err(SomeNoIriExclusion { exc: other.clone() }),
}
}
Ok(iri_excs)
}
pub fn parse_language_exclusions(excs: Vec<Exclusion>) -> Result<Vec<LanguageExclusion>, SomeNoIriExclusion> {
let mut lang_excs = Vec::new();
for e in excs {
match e {
Exclusion::LanguageExclusion(le) => lang_excs.push(le),
Exclusion::Untyped(s) => {
let lang = Lang::new(&s).map_err(|_e| SomeNoIriExclusion {
exc: Exclusion::Untyped(s.clone()),
})?;
lang_excs.push(LanguageExclusion::Language(lang))
},
other => return Err(SomeNoIriExclusion { exc: other }),
}
}
Ok(lang_excs)
}
}
impl Serialize for Exclusion {
fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Exclusion::IriExclusion(_iri) => todo!(),
Exclusion::LiteralExclusion(LiteralExclusion::Literal(_lit)) => {
todo!()
},
Exclusion::LiteralExclusion(LiteralExclusion::LiteralStem(stem)) => {
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", "LiteralStem")?;
map.serialize_entry("stem", stem)?;
map.end()
},
Exclusion::LanguageExclusion(stem) => {
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", "LanguageStem")?;
map.serialize_entry("stem", stem)?;
map.end()
},
Exclusion::Untyped(str) => serializer.serialize_str(str),
}
}
}
impl<'de> Deserialize<'de> for Exclusion {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
enum Field {
Type,
Stem,
}
impl<'de> Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
where
D: serde::Deserializer<'de>,
{
struct FieldVisitor;
impl Visitor<'_> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("field of exclusion: `type` or `stem`")
}
fn visit_str<E>(self, value: &str) -> Result<Field, E>
where
E: de::Error,
{
match value {
"type" => Ok(Field::Type),
"stem" => Ok(Field::Stem),
_ => Err(de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct ExclusionVisitor;
const FIELDS: &[&str] = &["type", "stem"];
impl<'de> Visitor<'de> for ExclusionVisitor {
type Value = Exclusion;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Exclusion value")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Exclusion::Untyped(s.to_string()))
}
fn visit_map<V>(self, mut map: V) -> Result<Exclusion, V::Error>
where
V: MapAccess<'de>,
{
let mut type_: Option<ExclusionType> = None;
let mut stem: Option<StemValue> = None;
while let Some(key) = map.next_key()? {
match key {
Field::Type => {
if type_.is_some() {
return Err(de::Error::duplicate_field("type"));
}
let value: String = map.next_value()?;
let parsed_type_ = ExclusionType::parse(value.as_str()).map_err(|e| {
de::Error::custom(format!("Error parsing Exclusion type, found: {value}. Error: {e:?}"))
})?;
type_ = Some(parsed_type_);
},
Field::Stem => {
if stem.is_some() {
return Err(de::Error::duplicate_field("stem"));
}
stem = Some(map.next_value()?);
},
}
}
match type_ {
Some(ExclusionType::LiteralStem) => match stem {
Some(StemValue::Literal(lit)) => {
Ok(Exclusion::LiteralExclusion(LiteralExclusion::LiteralStem(lit)))
},
Some(_) => Err(de::Error::custom(format!("Stem {stem:?} must be a literal"))),
None => Err(de::Error::missing_field("stem")),
},
Some(ExclusionType::LanguageStem) => match stem {
Some(StemValue::Language(lang)) => {
Ok(Exclusion::LanguageExclusion(LanguageExclusion::LanguageStem(lang)))
},
Some(StemValue::Literal(l)) => {
let lang = Lang::new(&l)
.map_err(|e| de::Error::custom(format!("Invalid language tag {l} in stem: {e}")))?;
Ok(Exclusion::LanguageExclusion(LanguageExclusion::LanguageStem(lang)))
},
Some(_) => Err(de::Error::custom(format!("Stem {stem:?} must be a language"))),
None => Err(de::Error::missing_field("stem")),
},
Some(ExclusionType::IriStem) => match stem {
Some(StemValue::Iri(iri_ref)) => Ok(Exclusion::IriExclusion(IriExclusion::IriStem(iri_ref))),
Some(_) => Err(de::Error::custom(format!("Stem {stem:?} must be an IRI"))),
None => Err(de::Error::missing_field("stem")),
},
None => Err(de::Error::custom("No value of exclusion type")),
}
}
}
deserializer.deserialize_any(ExclusionVisitor)
}
}
#[derive(Debug, PartialEq)]
#[allow(clippy::enum_variant_names)]
enum ExclusionType {
IriStem,
LiteralStem,
LanguageStem,
}
#[derive(Debug, PartialEq, Deserialize)]
#[serde(untagged)]
enum StemValue {
Iri(IriRef),
Literal(String),
Language(Lang),
}
#[derive(Debug)]
#[allow(dead_code)]
struct ExclusionTypeError {
value: String,
}
impl ExclusionType {
fn parse(s: &str) -> Result<ExclusionType, ExclusionTypeError> {
match s {
"IriStem" => Ok(ExclusionType::IriStem),
"LanguageStem" => Ok(ExclusionType::LanguageStem),
"LiteralStem" => Ok(ExclusionType::LiteralStem),
_ => Err(ExclusionTypeError { value: s.to_string() }),
}
}
}