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
//! CSS funciri values.

use cssparser::Parser;

use crate::document::NodeId;
use crate::error::*;
use crate::parsers::Parse;

/// Used where style properties take a funciri or "none"
///
/// This is not to be used for values which don't come from properties.
/// For example, the `xlink:href` attribute in the `<image>` element
/// does not take a funciri value (which looks like `url(...)`), but rather
/// it takes a plain URL.
#[derive(Debug, Clone, PartialEq)]
pub enum Iri {
    None,
    Resource(Box<NodeId>),
}

impl Iri {
    /// Returns the contents of an `IRI::Resource`, or `None`
    pub fn get(&self) -> Option<&NodeId> {
        match *self {
            Iri::None => None,
            Iri::Resource(ref f) => Some(f),
        }
    }
}

impl Parse for Iri {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Iri, ParseError<'i>> {
        if parser
            .try_parse(|i| i.expect_ident_matching("none"))
            .is_ok()
        {
            Ok(Iri::None)
        } else {
            let loc = parser.current_source_location();
            let url = parser.expect_url()?;
            let node_id =
                NodeId::parse(&url).map_err(|e| loc.new_custom_error(ValueErrorKind::from(e)))?;

            Ok(Iri::Resource(Box::new(node_id)))
        }
    }
}

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

    #[test]
    fn parses_none() {
        assert_eq!(Iri::parse_str("none").unwrap(), Iri::None);
    }

    #[test]
    fn parses_url() {
        assert_eq!(
            Iri::parse_str("url(#bar)").unwrap(),
            Iri::Resource(Box::new(NodeId::Internal("bar".to_string())))
        );

        assert_eq!(
            Iri::parse_str("url(foo#bar)").unwrap(),
            Iri::Resource(Box::new(NodeId::External(
                "foo".to_string(),
                "bar".to_string()
            )))
        );

        // be permissive if the closing ) is missing
        assert_eq!(
            Iri::parse_str("url(#bar").unwrap(),
            Iri::Resource(Box::new(NodeId::Internal("bar".to_string())))
        );
        assert_eq!(
            Iri::parse_str("url(foo#bar").unwrap(),
            Iri::Resource(Box::new(NodeId::External(
                "foo".to_string(),
                "bar".to_string()
            )))
        );

        assert!(Iri::parse_str("").is_err());
        assert!(Iri::parse_str("foo").is_err());
        assert!(Iri::parse_str("url(foo)bar").is_err());
    }
}