use std::fmt;
use prefixmap::{Deref, DerefError, IriRef};
use serde::{
de::{self, MapAccess, Visitor},
Deserialize, Serialize, Serializer,
};
use srdf::numeric_literal::NumericLiteral;
use super::ValueSetValue;
use crate::{NodeKind, NumericFacet, Pattern, StringFacet, XsFacet};
use serde::ser::SerializeMap;
#[derive(Debug, Default, PartialEq, Clone)]
pub struct NodeConstraint {
node_kind: Option<NodeKind>,
datatype: Option<IriRef>,
xs_facet: Option<Vec<XsFacet>>,
values: Option<Vec<ValueSetValue>>,
}
impl NodeConstraint {
pub fn new() -> Self {
Self::default()
}
pub fn with_datatype(mut self, datatype: IriRef) -> Self {
self.datatype = Some(datatype);
self
}
pub fn add_datatype(&mut self, datatype: IriRef) {
self.datatype = Some(datatype);
}
pub fn add_values(&mut self, values: Vec<ValueSetValue>) {
self.values = Some(values);
}
pub fn datatype(&self) -> Option<IriRef> {
self.datatype.clone()
}
pub fn with_node_kind(mut self, node_kind: NodeKind) -> Self {
self.node_kind = Some(node_kind);
self
}
pub fn node_kind(&self) -> Option<NodeKind> {
self.node_kind.clone()
}
pub fn with_xsfacets(mut self, facets: Vec<XsFacet>) -> Self {
self.xs_facet = if facets.is_empty() {
None
} else {
Some(facets)
};
self
}
pub fn with_pattern(mut self, pat: &str) -> Self {
match self.xs_facet {
Some(ref mut facets) => facets.push(XsFacet::pattern(pat)),
None => self.xs_facet = Some(vec![XsFacet::pattern(pat)]),
}
self
}
pub fn with_pattern_flags(mut self, pat: &str, flags: &str) -> Self {
match self.xs_facet {
Some(ref mut facets) => facets.push(XsFacet::pattern_flags(pat, flags)),
None => self.xs_facet = Some(vec![XsFacet::pattern_flags(pat, flags)]),
}
self
}
pub fn xs_facet(&self) -> Option<Vec<XsFacet>> {
self.xs_facet.clone()
}
pub fn with_length(self, len: usize) -> Self {
self.add_facet(XsFacet::length(len))
}
pub fn with_minlength(self, len: usize) -> Self {
self.add_facet(XsFacet::min_length(len))
}
pub fn with_maxlength(self, len: usize) -> Self {
self.add_facet(XsFacet::max_length(len))
}
pub fn with_min_inclusive(self, n: NumericLiteral) -> Self {
self.add_facet(XsFacet::min_inclusive(n))
}
pub fn with_max_inclusive(self, n: NumericLiteral) -> Self {
self.add_facet(XsFacet::max_inclusive(n))
}
pub fn add_facet(mut self, f: XsFacet) -> Self {
match self.xs_facet {
Some(ref mut facets) => facets.push(f),
None => self.xs_facet = Some(vec![f]),
}
self
}
pub fn with_min_exclusive(self, n: NumericLiteral) -> Self {
self.add_facet(XsFacet::min_exclusive(n))
}
pub fn with_max_exclusive(self, n: NumericLiteral) -> Self {
self.add_facet(XsFacet::max_exclusive(n))
}
pub fn with_totaldigits(self, n: usize) -> Self {
self.add_facet(XsFacet::totaldigits(n))
}
pub fn with_fractiondigits(self, n: usize) -> Self {
self.add_facet(XsFacet::fractiondigits(n))
}
pub fn with_values(mut self, values: Vec<ValueSetValue>) -> Self {
self.values = Some(values);
self
}
pub fn values(&self) -> Option<Vec<ValueSetValue>> {
match &self.values {
None => None,
Some(vs) => {
let mut r = Vec::new();
for v in vs {
r.push((*v).clone())
}
Some(r)
}
}
}
}
impl Deref for NodeConstraint {
fn deref(
&self,
base: &Option<iri_s::IriS>,
prefixmap: &Option<prefixmap::PrefixMap>,
) -> Result<Self, DerefError>
where
Self: Sized,
{
let datatype = <IriRef as Deref>::deref_opt(&self.datatype, base, prefixmap)?;
let values = <ValueSetValue as Deref>::deref_opt_vec(&self.values, base, prefixmap)?;
Ok(NodeConstraint {
node_kind: self.node_kind.clone(),
datatype,
xs_facet: self.xs_facet.clone(),
values,
})
}
}
impl Serialize for NodeConstraint {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let NodeConstraint {
node_kind,
datatype,
xs_facet,
values,
} = self;
let mut map = serializer.serialize_map(None)?;
match node_kind {
None => (),
Some(nk) => {
map.serialize_entry("nodeKind", &format!("{nk}").to_lowercase())?;
}
}
match datatype {
None => (),
Some(dt) => {
map.serialize_entry("datatype", &format!("{dt}"))?;
}
}
match values {
None => (),
Some(values) => {
map.serialize_entry("values", &values)?;
}
}
match xs_facet {
None => (),
Some(facets) => {
for f in facets {
match f {
XsFacet::StringFacet(sf) => match sf {
StringFacet::Length(len) => map.serialize_entry("length", len)?,
StringFacet::MinLength(ml) => map.serialize_entry("minlength", ml)?,
StringFacet::MaxLength(ml) => map.serialize_entry("maxlength", ml)?,
StringFacet::Pattern(Pattern { str, flags: None }) => {
map.serialize_entry("pattern", str)?;
}
StringFacet::Pattern(Pattern {
str,
flags: Some(fs),
}) => {
map.serialize_entry("pattern", str)?;
map.serialize_entry("flags", fs)?;
}
},
XsFacet::NumericFacet(nf) => match nf {
NumericFacet::FractionDigits(fd) => {
map.serialize_entry("fractiondigits", fd)?
}
NumericFacet::TotalDigits(td) => {
map.serialize_entry("totaldigits", td)?
}
NumericFacet::MaxExclusive(me) => {
map.serialize_entry("maxexclusive", me)?
}
NumericFacet::MaxInclusive(mi) => {
map.serialize_entry("maxinclusive", mi)?
}
NumericFacet::MinInclusive(mi) => {
map.serialize_entry("mininclusive", mi)?
}
NumericFacet::MinExclusive(me) => {
map.serialize_entry("minexclusive", me)?
}
},
}
}
}
}
map.end()
}
}
impl<'de> Deserialize<'de> for NodeConstraint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
enum Field {
Type,
NodeKind,
Datatype,
Values,
Length,
MinLength,
MaxLength,
Pattern,
Flags,
MinInclusive,
MaxInclusive,
MinExclusive,
MaxExclusive,
TotalDigits,
FractionDigits,
}
impl<'de> Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
where
D: serde::Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(
"`type` or `nodeKind` or `datatype` or some xsfacet or `values` ",
)
}
fn visit_str<E>(self, value: &str) -> Result<Field, E>
where
E: de::Error,
{
match value {
"nodeKind" => Ok(Field::NodeKind),
"datatype" => Ok(Field::Datatype),
"type" => Ok(Field::Type),
"pattern" => Ok(Field::Pattern),
"flags" => Ok(Field::Flags),
"length" => Ok(Field::Length),
"minlength" => Ok(Field::MinLength),
"maxlength" => Ok(Field::MaxLength),
"mininclusive" => Ok(Field::MinInclusive),
"maxinclusive" => Ok(Field::MaxInclusive),
"minexclusive" => Ok(Field::MinExclusive),
"maxexclusive" => Ok(Field::MaxExclusive),
"totaldigits" => Ok(Field::TotalDigits),
"fractiondigits" => Ok(Field::FractionDigits),
"values" => Ok(Field::Values),
_ => Err(de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct NodeConstraintVisitor;
impl<'de> Visitor<'de> for NodeConstraintVisitor {
type Value = NodeConstraint;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct NodeConstraint")
}
fn visit_map<V>(self, mut map: V) -> Result<NodeConstraint, V::Error>
where
V: MapAccess<'de>,
{
let mut type_: Option<String> = None;
let mut node_kind: Option<NodeKind> = None;
let mut datatype: Option<IriRef> = None;
let mut pattern: Option<String> = None;
let mut length: Option<usize> = None;
let mut minlength: Option<usize> = None;
let mut maxlength: Option<usize> = None;
let mut mininclusive: Option<NumericLiteral> = None;
let mut maxinclusive: Option<NumericLiteral> = None;
let mut minexclusive: Option<NumericLiteral> = None;
let mut maxexclusive: Option<NumericLiteral> = None;
let mut totaldigits: Option<usize> = None;
let mut fractiondigits: Option<usize> = None;
let mut flags: Option<String> = None;
let mut values: Option<Vec<ValueSetValue>> = None;
while let Some(key) = map.next_key()? {
match key {
Field::NodeKind => {
if node_kind.is_some() {
return Err(de::Error::duplicate_field("nodeKind"));
}
let value = map.next_value()?;
node_kind = match value {
"iri" => Some(NodeKind::Iri),
"bnode" => Some(NodeKind::BNode),
"literal" => Some(NodeKind::Literal),
"nonliteral" => Some(NodeKind::NonLiteral),
_ => {
return Err(de::Error::custom(format!(
"Unexpected value for `nodeKind`: {value}"
)))
}
}
}
Field::Datatype => {
if datatype.is_some() {
return Err(de::Error::duplicate_field("datatype"));
}
let iri: IriRef = map.next_value()?;
datatype = Some(iri);
}
Field::Values => {
if values.is_some() {
return Err(de::Error::duplicate_field("values"));
}
let vs: Vec<ValueSetValue> = map.next_value()?;
values = Some(vs)
}
Field::Pattern => {
if pattern.is_some() {
return Err(de::Error::duplicate_field("pattern"));
}
pattern = Some(map.next_value()?);
}
Field::Length => {
if length.is_some() {
return Err(de::Error::duplicate_field("length"));
}
length = Some(map.next_value()?);
}
Field::MinLength => {
if minlength.is_some() {
return Err(de::Error::duplicate_field("minlength"));
}
minlength = Some(map.next_value()?);
}
Field::MaxLength => {
if maxlength.is_some() {
return Err(de::Error::duplicate_field("maxlength"));
}
maxlength = Some(map.next_value()?);
}
Field::MinInclusive => {
if mininclusive.is_some() {
return Err(de::Error::duplicate_field("mininclusive"));
}
mininclusive = Some(map.next_value()?);
}
Field::MaxInclusive => {
if maxinclusive.is_some() {
return Err(de::Error::duplicate_field("maxinclusive"));
}
maxinclusive = Some(map.next_value()?);
}
Field::MinExclusive => {
if minexclusive.is_some() {
return Err(de::Error::duplicate_field("minexclusive"));
}
minexclusive = Some(map.next_value()?);
}
Field::MaxExclusive => {
if maxexclusive.is_some() {
return Err(de::Error::duplicate_field("maxexclusive"));
}
maxexclusive = Some(map.next_value()?);
}
Field::TotalDigits => {
if totaldigits.is_some() {
return Err(de::Error::duplicate_field("totaldigits"));
}
totaldigits = Some(map.next_value()?);
}
Field::FractionDigits => {
if fractiondigits.is_some() {
return Err(de::Error::duplicate_field("fractiondigits"));
}
fractiondigits = Some(map.next_value()?);
}
Field::Type => {
if type_.is_some() {
return Err(de::Error::duplicate_field("type"));
}
let value: String = map.next_value()?;
if value != "NodeConstraint" {
return Err(de::Error::custom(format!(
"Expected NodeConstraint, found: {value}"
)));
}
type_ = Some("NodeConstraint".to_string());
}
Field::Flags => {
if flags.is_some() {
return Err(de::Error::duplicate_field("flags"));
}
flags = Some(map.next_value()?);
}
}
}
let mut nc = NodeConstraint::new();
if let Some(nk) = node_kind {
nc = nc.with_node_kind(nk)
}
if let Some(pat) = pattern {
if let Some(flags) = flags {
nc = nc.with_pattern_flags(&pat, &flags)
} else {
nc = nc.with_pattern(&pat)
}
}
if let Some(length) = length {
nc = nc.with_length(length)
}
if let Some(datatype) = datatype {
nc = nc.with_datatype(datatype)
}
if let Some(vs) = values {
nc = nc.with_values(vs)
}
if let Some(minlength) = minlength {
nc = nc.with_minlength(minlength)
}
if let Some(maxlength) = maxlength {
nc = nc.with_maxlength(maxlength)
}
if let Some(mininclusive) = mininclusive {
nc = nc.with_min_inclusive(mininclusive)
}
if let Some(maxinclusive) = maxinclusive {
nc = nc.with_max_inclusive(maxinclusive)
}
if let Some(minexclusive) = minexclusive {
nc = nc.with_min_exclusive(minexclusive)
}
if let Some(maxexclusive) = maxexclusive {
nc = nc.with_max_exclusive(maxexclusive)
}
if let Some(totaldigits) = totaldigits {
nc = nc.with_totaldigits(totaldigits)
}
if let Some(fractiondigits) = fractiondigits {
nc = nc.with_fractiondigits(fractiondigits)
}
Ok(nc)
}
}
const FIELDS: &[&str] = &[
"type",
"nodeKind",
"datatype",
"values",
"pattern",
"flags",
"length",
"minlength",
"maxlength",
"mininclusive",
"maxinclusive",
"minexclusive",
"maxexclusive",
"totaldigits",
"fractiondigits",
];
deserializer.deserialize_struct("NodeConstraint", FIELDS, NodeConstraintVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_serialize_node_kind_iri() {
let nc = NodeConstraint::new().with_node_kind(NodeKind::Iri);
let json_nc = serde_json::to_string(&nc).unwrap();
assert_eq!(json_nc, "{\"nodeKind\":\"iri\"}");
}
#[test]
fn test_deserialize_node_kind_iri() {
let str = r#"{ "type":"NodeConstraint","nodeKind": "iri"}"#;
let deserialized: NodeConstraint = serde_json::from_str(str).unwrap();
let expected = NodeConstraint::new().with_node_kind(NodeKind::Iri);
assert_eq!(deserialized, expected);
}
#[test]
fn test_serialize_pattern() {
let nc = NodeConstraint::new().with_pattern("o*");
let json_nc = serde_json::to_string(&nc).unwrap();
let expected = r#"{"pattern":"o*"}"#;
assert_eq!(json_nc, expected);
}
#[test]
fn test_deserialize_pattern() {
let str = r#"{ "type":"NodeConstraint","pattern": "o*"}"#;
let deserialized: NodeConstraint = serde_json::from_str(str).unwrap();
let expected = NodeConstraint::new().with_pattern("o*");
assert_eq!(deserialized, expected);
}
#[test]
fn test_serialize_pattern_flags() {
let nc = NodeConstraint::new().with_pattern_flags("o*", "i");
let json_nc = serde_json::to_string(&nc).unwrap();
let expected = r#"{"pattern":"o*","flags":"i"}"#;
assert_eq!(json_nc, expected);
}
#[test]
fn test_deserialize_pattern_flags() {
let str = r#"{ "type":"NodeConstraint","pattern": "o*", "flags": "i"}"#;
let deserialized: NodeConstraint = serde_json::from_str(str).unwrap();
let expected = NodeConstraint::new().with_pattern_flags("o*", "i");
assert_eq!(deserialized, expected);
}
}