shex_ast/ast/
exclusion.rs

1use std::str::FromStr;
2use std::{fmt, result};
3
4use serde::de::{MapAccess, Visitor};
5use serde::ser::SerializeMap;
6use serde::{de, Deserialize, Serialize, Serializer};
7use srdf::lang::Lang;
8
9use prefixmap::IriRef;
10
11#[derive(Debug, PartialEq, Clone, Deserialize)]
12pub enum LiteralExclusion {
13    Literal(String),
14    LiteralStem(String),
15}
16
17impl Serialize for LiteralExclusion {
18    fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
19    where
20        S: Serializer,
21    {
22        match self {
23            LiteralExclusion::Literal(lit) => serializer.serialize_str(lit.as_str()),
24            LiteralExclusion::LiteralStem(stem) => {
25                let mut map = serializer.serialize_map(Some(2))?;
26                map.serialize_entry("type", "LiteralStem")?;
27                map.serialize_entry("stem", stem)?;
28                map.end()
29            }
30        }
31    }
32}
33
34#[derive(Debug, PartialEq, Clone, Deserialize)]
35pub enum IriExclusion {
36    Iri(IriRef),
37    IriStem(IriRef),
38}
39
40impl Serialize for IriExclusion {
41    fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
42    where
43        S: Serializer,
44    {
45        match self {
46            IriExclusion::Iri(iri) => serializer.serialize_str(iri.to_string().as_str()),
47            IriExclusion::IriStem(stem) => {
48                let mut map = serializer.serialize_map(Some(2))?;
49                map.serialize_entry("type", "IriStem")?;
50                map.serialize_entry("stem", stem)?;
51                map.end()
52            }
53        }
54    }
55}
56
57#[derive(Debug, PartialEq, Clone)]
58pub enum LanguageExclusion {
59    Language(Lang),
60    LanguageStem(Lang),
61}
62
63impl Serialize for LanguageExclusion {
64    fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
65    where
66        S: Serializer,
67    {
68        match self {
69            LanguageExclusion::Language(lang) => serializer.serialize_str(&lang.to_string()),
70            LanguageExclusion::LanguageStem(stem) => {
71                let mut map = serializer.serialize_map(Some(2))?;
72                map.serialize_entry("type", "LanguageStem")?;
73                map.serialize_entry("stem", stem)?;
74                map.end()
75            }
76        }
77    }
78}
79
80#[derive(Debug, PartialEq, Clone)]
81pub enum Exclusion {
82    LiteralExclusion(LiteralExclusion),
83    LanguageExclusion(LanguageExclusion),
84    IriExclusion(IriExclusion),
85    Untyped(String),
86}
87
88#[derive(Debug)]
89pub struct SomeNoLitExclusion {
90    pub exc: Exclusion,
91}
92
93#[derive(Debug)]
94pub struct SomeNoIriExclusion {
95    pub exc: Exclusion,
96}
97
98#[derive(Debug)]
99pub struct SomeNoLanguageExclusion {
100    pub exc: Exclusion,
101}
102
103impl Exclusion {
104    pub fn parse_literal_exclusions(
105        excs: Vec<Exclusion>,
106    ) -> Result<Vec<LiteralExclusion>, SomeNoLitExclusion> {
107        let mut lit_excs = Vec::new();
108        for e in excs {
109            match e {
110                Exclusion::LiteralExclusion(le) => lit_excs.push(le),
111                Exclusion::Untyped(s) => lit_excs.push(LiteralExclusion::Literal(s)),
112                other => return Err(SomeNoLitExclusion { exc: other }),
113            }
114        }
115        Ok(lit_excs)
116    }
117
118    pub fn parse_iri_exclusions(
119        excs: Vec<Exclusion>,
120    ) -> Result<Vec<IriExclusion>, SomeNoIriExclusion> {
121        let mut iri_excs = Vec::new();
122        for e in excs {
123            match &e {
124                Exclusion::IriExclusion(le) => iri_excs.push(le.clone()),
125                v @ Exclusion::Untyped(s) => {
126                    let iri = FromStr::from_str(s.as_str())
127                        .map_err(|_e| SomeNoIriExclusion { exc: v.clone() })?;
128                    iri_excs.push(IriExclusion::Iri(iri))
129                }
130                other => return Err(SomeNoIriExclusion { exc: other.clone() }),
131            }
132        }
133        Ok(iri_excs)
134    }
135
136    pub fn parse_language_exclusions(
137        excs: Vec<Exclusion>,
138    ) -> Result<Vec<LanguageExclusion>, SomeNoIriExclusion> {
139        let mut lang_excs = Vec::new();
140        for e in excs {
141            match e {
142                Exclusion::LanguageExclusion(le) => lang_excs.push(le),
143                Exclusion::Untyped(s) => {
144                    lang_excs.push(LanguageExclusion::Language(Lang::new_unchecked(s)))
145                }
146                other => return Err(SomeNoIriExclusion { exc: other }),
147            }
148        }
149        Ok(lang_excs)
150    }
151}
152
153impl Serialize for Exclusion {
154    fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
155    where
156        S: Serializer,
157    {
158        match self {
159            Exclusion::IriExclusion(_iri) => todo!(),
160            Exclusion::LiteralExclusion(LiteralExclusion::Literal(_lit)) => {
161                todo!()
162            }
163            Exclusion::LiteralExclusion(LiteralExclusion::LiteralStem(stem)) => {
164                let mut map = serializer.serialize_map(Some(2))?;
165                map.serialize_entry("type", "LiteralStem")?;
166                map.serialize_entry("stem", stem)?;
167                map.end()
168            }
169            Exclusion::LanguageExclusion(stem) => {
170                let mut map = serializer.serialize_map(Some(2))?;
171                map.serialize_entry("type", "LanguageStem")?;
172                map.serialize_entry("stem", stem)?;
173                map.end()
174            }
175            Exclusion::Untyped(str) => serializer.serialize_str(str),
176        }
177    }
178}
179
180impl<'de> Deserialize<'de> for Exclusion {
181    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182    where
183        D: serde::Deserializer<'de>,
184    {
185        enum Field {
186            Type,
187            Stem,
188        }
189
190        impl<'de> Deserialize<'de> for Field {
191            fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
192            where
193                D: serde::Deserializer<'de>,
194            {
195                struct FieldVisitor;
196
197                impl Visitor<'_> for FieldVisitor {
198                    type Value = Field;
199
200                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
201                        formatter.write_str("field of exclusion: `type` or `stem`")
202                    }
203
204                    fn visit_str<E>(self, value: &str) -> Result<Field, E>
205                    where
206                        E: de::Error,
207                    {
208                        match value {
209                            "type" => Ok(Field::Type),
210                            "stem" => Ok(Field::Stem),
211                            _ => Err(de::Error::unknown_field(value, FIELDS)),
212                        }
213                    }
214                }
215
216                deserializer.deserialize_identifier(FieldVisitor)
217            }
218        }
219
220        struct ExclusionVisitor;
221
222        const FIELDS: &[&str] = &["type", "stem"];
223
224        impl<'de> Visitor<'de> for ExclusionVisitor {
225            type Value = Exclusion;
226
227            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
228                formatter.write_str("Exclusion value")
229            }
230
231            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
232            where
233                E: de::Error,
234            {
235                Ok(Exclusion::Untyped(s.to_string()))
236            }
237
238            fn visit_map<V>(self, mut map: V) -> Result<Exclusion, V::Error>
239            where
240                V: MapAccess<'de>,
241            {
242                let mut type_: Option<ExclusionType> = None;
243                let mut stem: Option<StemValue> = None;
244                while let Some(key) = map.next_key()? {
245                    match key {
246                        Field::Type => {
247                            if type_.is_some() {
248                                return Err(de::Error::duplicate_field("type"));
249                            }
250                            let value: String = map.next_value()?;
251
252                            let parsed_type_ =
253                                ExclusionType::parse(value.as_str()).map_err(|e| {
254                                    de::Error::custom(format!(
255                                        "Error parsing Exclusion type, found: {value}. Error: {e:?}"
256                                    ))
257                                })?;
258                            type_ = Some(parsed_type_);
259                        }
260                        Field::Stem => {
261                            if stem.is_some() {
262                                return Err(de::Error::duplicate_field("stem"));
263                            }
264                            stem = Some(map.next_value()?);
265                        }
266                    }
267                }
268                match type_ {
269                    Some(ExclusionType::LiteralStem) => match stem {
270                        Some(StemValue::Literal(lit)) => Ok(Exclusion::LiteralExclusion(
271                            LiteralExclusion::LiteralStem(lit),
272                        )),
273                        Some(_) => Err(de::Error::custom(format!(
274                            "Stem {stem:?} must be a literal"
275                        ))),
276                        None => Err(de::Error::missing_field("stem")),
277                    },
278                    Some(ExclusionType::LanguageStem) => match stem {
279                        Some(StemValue::Language(lang)) => Ok(Exclusion::LanguageExclusion(
280                            LanguageExclusion::LanguageStem(lang),
281                        )),
282                        Some(StemValue::Literal(l)) => Ok(Exclusion::LanguageExclusion(
283                            LanguageExclusion::LanguageStem(Lang::new_unchecked(l)),
284                        )),
285                        Some(_) => Err(de::Error::custom(format!(
286                            "Stem {stem:?} must be a language"
287                        ))),
288                        None => Err(de::Error::missing_field("stem")),
289                    },
290                    Some(ExclusionType::IriStem) => match stem {
291                        Some(StemValue::Iri(iri)) => {
292                            Ok(Exclusion::IriExclusion(IriExclusion::IriStem(iri)))
293                        }
294                        Some(_) => Err(de::Error::custom(format!("Stem {stem:?} must be an IRI"))),
295                        None => Err(de::Error::missing_field("stem")),
296                    },
297                    None => Err(de::Error::custom("No value of exclusion type")),
298                }
299            }
300        }
301
302        deserializer.deserialize_any(ExclusionVisitor)
303    }
304}
305
306#[derive(Debug, PartialEq)]
307#[allow(clippy::enum_variant_names)]
308enum ExclusionType {
309    IriStem,
310    LiteralStem,
311    LanguageStem,
312}
313
314#[derive(Debug, PartialEq, Deserialize)]
315#[serde(untagged)]
316enum StemValue {
317    Iri(IriRef),
318    Literal(String),
319    Language(Lang),
320}
321
322#[derive(Debug)]
323#[allow(dead_code)]
324struct ExclusionTypeError {
325    value: String,
326}
327
328impl ExclusionType {
329    fn parse(s: &str) -> Result<ExclusionType, ExclusionTypeError> {
330        match s {
331            "IriStem" => Ok(ExclusionType::IriStem),
332            "LanguageStem" => Ok(ExclusionType::LanguageStem),
333            "LiteralStem" => Ok(ExclusionType::LiteralStem),
334            _ => Err(ExclusionTypeError {
335                value: s.to_string(),
336            }),
337        }
338    }
339}