aboutsummaryrefslogtreecommitdiff
path: root/src/attr.rs
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2023-08-16 09:45:18 +0200
committerMartin Fischer <martin@push-f.com>2023-08-19 13:41:55 +0200
commit65aca9cbf0318bd3a2f936641b4f5bc3729c98c2 (patch)
tree3bf6ae3ea03c1f377e9b4bd6fd7d6af99fbddc5a /src/attr.rs
parent0f460c2e77f450a2bac68eec97b2c62aa33c0495 (diff)
break!: introduce AttributeMap
This has a number of benefits: * it hides the implementation of the map * it hides the type used for the map values (which lets us e.g. change name_span to name_offset while still being able to provide a convenient `Attribute::name_span` method.) * it lets us provide convenience impls for the map such as `FromIterator<(String, String)>`
Diffstat (limited to 'src/attr.rs')
-rw-r--r--src/attr.rs127
1 files changed, 119 insertions, 8 deletions
diff --git a/src/attr.rs b/src/attr.rs
index d0d506e..9e4c984 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -1,14 +1,125 @@
-use std::ops::Range;
+//! Types for HTML attributes.
-/// A HTML attribute value (plus spans).
+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 struct Attribute<O> {
- /// The value of the attribute.
+pub(crate) struct AttrInternal<O> {
pub value: String,
-
- /// The source code span of the attribute name.
pub name_span: Range<O>,
-
- /// The source code span of the attribute value.
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(),
+ }
+ }
+}