sophia_api/prefix/
_prefix_map.rs

1use super::{AsPrefix, IsPrefix, Prefix};
2/// Define the [`PrefixMap`] trait with default implementation.
3use mownstr::MownStr;
4use sophia_iri::{AsIri, Iri, IsIri};
5
6/// A prefix map associates prefixes to namespaces.
7pub trait PrefixMap {
8    /// Return the IRI associated to this prefix, if any.
9    fn get_namespace<'s>(&'s self, prefix: &str) -> Option<Iri<&'s str>>;
10    /// Return a prefix-suffix pair describing the given IRI, if any.
11    fn get_prefixed_pair<'s, T: IsIri + 's>(
12        &'s self,
13        iri: T,
14    ) -> Option<(Prefix<&'s str>, MownStr<'s>)> {
15        self.get_checked_prefixed_pair(iri, |_| true)
16    }
17    /// Return a prefix-suffix pair describing the given IRI, if any,
18    /// guaranteeing that the suffix will satisfy the given predicate.
19    fn get_checked_prefixed_pair<'s, T, F>(
20        &'s self,
21        iri: T,
22        suffix_check: F,
23    ) -> Option<(Prefix<&'s str>, MownStr<'s>)>
24    where
25        T: IsIri + 's,
26        F: Fn(&str) -> bool;
27    /// Iterate over (prefix, IRI) pairs.
28    fn iter<'s>(&'s self) -> Box<dyn Iterator<Item = (Prefix<&'s str>, Iri<&'s str>)> + 's>;
29    /// Copies this prefix map as a self-sufficient vector
30    #[allow(clippy::type_complexity)]
31    fn to_vec(&self) -> Vec<PrefixMapPair> {
32        self.iter()
33            .map(|(prefix, ns)| (prefix.map_unchecked(Box::from), ns.map_unchecked(Box::from)))
34            .collect()
35    }
36}
37
38/// Type alias for the return type of [`PrefixMap::to_vec`].
39pub type PrefixMapPair = (Prefix<Box<str>>, Iri<Box<str>>);
40
41impl<P, N> PrefixMap for [(P, N)]
42where
43    P: IsPrefix,
44    N: IsIri,
45{
46    fn get_namespace<'s>(&'s self, prefix: &str) -> Option<Iri<&'s str>> {
47        for (p, n) in self {
48            if p.borrow() == prefix {
49                return Some(n.as_iri());
50            }
51        }
52        None
53    }
54    fn get_checked_prefixed_pair<'s, T, F>(
55        &'s self,
56        iri: T,
57        suffix_check: F,
58    ) -> Option<(Prefix<&'s str>, MownStr<'s>)>
59    where
60        T: IsIri + 's,
61        F: Fn(&str) -> bool,
62    {
63        let iri_str = iri.borrow();
64        let mut matched = 0;
65        let mut found = None;
66        for (p, n) in self {
67            let n_str = n.borrow();
68            if iri_str.starts_with(n_str) && n_str.len() > matched {
69                let maybe_matched = n_str.len();
70                let suffix = &iri_str[maybe_matched..];
71                if suffix_check(suffix) {
72                    matched = maybe_matched;
73                    found = Some((p.as_prefix(), suffix));
74                }
75            }
76        }
77        found.map(|(p, s)| (p, s.to_owned().into()))
78    }
79    fn iter<'s>(&'s self) -> Box<dyn Iterator<Item = (Prefix<&'s str>, Iri<&'s str>)> + 's> {
80        Box::new(<[(P, N)]>::iter(self).map(|(prefix, ns)| (prefix.as_prefix(), ns.as_iri())))
81    }
82}
83
84/*
85#[cfg(test)]
86#[allow(clippy::bool_assert_comparison, clippy::unused_unit)] // test_case! generated warnings
87mod test {
88    use super::super::Prefix;
89    use super::*;
90    use crate::term::SimpleIri;
91    use sophia_iri::Iri;
92    use test_case::test_case;
93
94    fn make_map() -> Vec<(Prefix<'static>, Iri<'static>)> {
95        vec![
96            (
97                Prefix::new_unchecked("s"),
98                Iri::new_unchecked("http://schema.org/"),
99            ),
100            (
101                Prefix::new_unchecked("a"),
102                Iri::new_unchecked("http://example.org/a/"),
103            ),
104            (
105                Prefix::new_unchecked("ab"),
106                Iri::new_unchecked("http://example.org/a/b#"),
107            ),
108            (
109                Prefix::new_unchecked(""),
110                Iri::new_unchecked("http://example.org/"),
111            ),
112        ]
113    }
114
115    #[test_case("s", Some("http://schema.org/"); "s")]
116    #[test_case("a", Some("http://example.org/a/"); "a")]
117    #[test_case("ab", Some("http://example.org/a/b#"); "ab")]
118    #[test_case("", Some("http://example.org/"); "empty")]
119    #[test_case("sa", None; "sa")]
120    fn get_namespace(prefix: &str, expected: Option<&str>) {
121        let expected = expected.map(Iri::new_unchecked);
122        let map = make_map();
123        let got = map.get_namespace(prefix);
124        assert_eq!(got, expected);
125    }
126
127    #[test_case("http://something.else.com/", None, None; "something else")]
128    #[test_case("http://schema.org/Person", None, Some(("s", "Person")); "s:Person")]
129    #[test_case("http://example.org/", None, Some(("", "")); "single colon")]
130    #[test_case("http://example.org/", Some("a/c"), Some(("a", "c")); "a:c")]
131    #[test_case("http://example.org/", Some("a/b#c"), Some(("ab", "c")); "b:c")]
132    #[test_case("http://example.org/a#", Some("c"), Some(("", "a#c")); ":a#c")]
133    fn get_prefixed_pair(ns: &str, sf: Option<&str>, expected: Option<(&str, &str)>) {
134        let expected = expected.map(|(pf, sf)| (Prefix::new_unchecked(pf), MownStr::from(sf)));
135        let map = make_map();
136        let iri = SimpleIri::new_unchecked(ns, sf);
137        let got = map.get_prefixed_pair(&iri);
138        assert_eq!(got, expected);
139    }
140
141    #[test_case("http://something.else.com/", None, None; "something else")]
142    #[test_case("http://schema.org/Person", None, Some(("s", "Person")); "s:Person")]
143    #[test_case("http://example.org/", None, Some(("", "")); "single colon")]
144    #[test_case("http://example.org/", Some("a/c"), Some(("a", "c")); "a:c")]
145    #[test_case("http://example.org/", Some("a/b#c"), Some(("ab", "c")); "b:c")]
146    #[test_case("http://example.org/a#", Some("c"), None; ":a#c")]
147    fn get_checked_prefixed_pair(ns: &str, sf: Option<&str>, expected: Option<(&str, &str)>) {
148        let expected = expected.map(|(pf, sf)| (Prefix::new_unchecked(pf), MownStr::from(sf)));
149        let map = make_map();
150        let iri = SimpleIri::new_unchecked(ns, sf);
151        let suffix_check = |txt: &str| txt.chars().all(|c| c.is_alphabetic());
152        let got = map.get_checked_prefixed_pair(&iri, suffix_check);
153        assert_eq!(got, expected);
154    }
155}
156 */