aboutsummaryrefslogtreecommitdiff
path: root/src/attr.rs
blob: 9e4c984413c20fa25d208f61d8bae4eb36c32998 (plain)
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//! Types for HTML attributes.

use std::collections::{btree_map, BTreeMap};
use std::iter::FromIterator;
use std::ops::{Index, Range};

use crate::offset::Offset;

/// A map of HTML attributes.
///
/// Does not preserve the order of attributes.
/// Iterating always yields attributes in order by name.
///
/// # Example
///
/// ```
/// # use html5tokenizer::attr::AttributeMap;
/// let attrs: AttributeMap<()> = vec![("href".into(), "http://example.com".into())]
///     .into_iter()
///     .collect();
/// assert_eq!(&attrs["href"], "http://example.com");
/// ```
#[derive(Debug, Default, PartialEq, Eq)]
pub struct AttributeMap<O> {
    pub(crate) inner: BTreeMap<String, AttrInternal<O>>,
}

/// The value type internally used by the [`AttributeMap`].
/// Not part of the public API.
#[derive(Debug, Eq, PartialEq)]
pub(crate) struct AttrInternal<O> {
    pub value: String,
    pub name_span: Range<O>,
    pub value_span: Range<O>,
}

/// An HTML attribute borrowed from an [`AttributeMap`].
#[derive(Debug, Eq, PartialEq)]
pub struct Attribute<'a, O> {
    name: &'a str,
    map_val: &'a AttrInternal<O>,
}

impl<O> AttributeMap<O> {
    /// Returns the attribute with the given name.
    pub fn get(&self, name: &str) -> Option<Attribute<O>> {
        self.inner
            .get_key_value(name)
            .map(|(name, map_val)| Attribute { name, map_val })
    }
}

impl<'a, O: Offset> Attribute<'a, O> {
    /// Returns the attribute name.
    pub fn name(&self) -> &'a str {
        self.name
    }

    /// Returns the attribute value.
    pub fn value(&self) -> &'a str {
        &self.map_val.value
    }

    /// Returns the span of the attribute name.
    pub fn name_span(&self) -> Range<O> {
        self.map_val.name_span.clone()
    }

    /// Returns the span of the attribute value.
    pub fn value_span(&self) -> Range<O> {
        self.map_val.value_span.clone()
    }
}

// We cannot impl Index<Output=Attribute> because Index::index returns a reference of
// the Output type (and you cannot return a value referencing a temporary value).
impl<O> Index<&str> for AttributeMap<O> {
    type Output = str;

    fn index(&self, name: &str) -> &Self::Output {
        &self.inner[name].value
    }
}

impl<'a, O> IntoIterator for &'a AttributeMap<O> {
    type Item = Attribute<'a, O>;

    type IntoIter = AttrIter<'a, O>;

    fn into_iter(self) -> Self::IntoIter {
        AttrIter(self.inner.iter())
    }
}

/// A borrowed iterator over the attributes of an [`AttributeMap`].
pub struct AttrIter<'a, S>(btree_map::Iter<'a, String, AttrInternal<S>>);

impl<'a, S> Iterator for AttrIter<'a, S> {
    type Item = Attribute<'a, S>;

    fn next(&mut self) -> Option<Self::Item> {
        let (name, map_val) = self.0.next()?;
        Some(Attribute { name, map_val })
    }
}

impl<O: Default> FromIterator<(String, String)> for AttributeMap<O> {
    fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
        Self {
            inner: iter
                .into_iter()
                .map(|(name, value)| {
                    (
                        name,
                        AttrInternal {
                            value,
                            name_span: O::default()..O::default(),
                            value_span: O::default()..O::default(),
                        },
                    )
                })
                .collect(),
        }
    }
}