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}