1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use super::*;
use serde::{
    de::{Error, Unexpected},
    Deserialize, Serialize,
};
use std::borrow::Borrow;

impl<'a, T: Borrow<str> + Deserialize<'a>> Deserialize<'a> for Iri<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'a>,
    {
        let inner: T = T::deserialize(deserializer)?;
        Iri::new(inner)
            .map_err(|err| D::Error::invalid_value(Unexpected::Str(&err.0), &"valid IRI"))
    }
}

impl<T: Borrow<str>> Serialize for Iri<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.as_str().serialize(serializer)
    }
}

impl<'a, T: Borrow<str> + Deserialize<'a>> Deserialize<'a> for IriRef<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'a>,
    {
        let inner: T = T::deserialize(deserializer)?;
        IriRef::new(inner)
            .map_err(|err| D::Error::invalid_value(Unexpected::Str(&err.0), &"valid IRI reference"))
    }
}

impl<T: Borrow<str>> Serialize for IriRef<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.as_str().serialize(serializer)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[derive(Serialize, Deserialize)]
    struct MyTable {
        iri: Option<Iri<String>>,
        iriref: Option<IriRef<String>>,
    }

    #[derive(Serialize, Deserialize)]
    struct MyUncheckedTable {
        iri: Option<String>,
        iriref: Option<String>,
    }

    #[test]
    fn valid_iri() {
        let data = MyUncheckedTable {
            iri: Some("http://example.org/".into()),
            iriref: None,
        };
        let toml_str = toml::to_string(&data).unwrap();
        let data2 = toml::from_str::<MyTable>(&toml_str).unwrap();
        assert_eq!(data.iri.unwrap(), data2.iri.unwrap().unwrap(),);
    }

    #[test]
    fn invalid_iri() {
        let data = MyUncheckedTable {
            iri: Some("#foo".into()),
            iriref: None,
        };
        let toml_str = toml::to_string(&data).unwrap();
        let data2 = toml::from_str::<MyTable>(&toml_str);
        assert!(data2.is_err());
    }

    #[test]
    fn valid_iriref() {
        let data = MyUncheckedTable {
            iriref: Some("#foo".into()),
            iri: None,
        };
        let toml_str = toml::to_string(&data).unwrap();
        let data2 = toml::from_str::<MyTable>(&toml_str).unwrap();
        assert_eq!(data.iriref.unwrap(), data2.iriref.unwrap().unwrap(),);
    }

    #[test]
    fn invalid_iriref() {
        let data = MyUncheckedTable {
            iriref: Some("a b".into()),
            iri: None,
        };
        let toml_str = toml::to_string(&data).unwrap();
        let data2 = toml::from_str::<MyTable>(&toml_str);
        assert!(data2.is_err());
    }
}