Skip to content

Commit 2a8c7f3

Browse files
committed
switch to Cow
this allows, thanks to the ownable crate, deep copies while only referencing the original (and other stuff)
1 parent 972a6ac commit 2a8c7f3

13 files changed

Lines changed: 330 additions & 76 deletions

File tree

Cargo.lock

Lines changed: 70 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pest_derive = "2.5.7"
1616
thiserror = "1.0.40"
1717
serde = { version = "1.0.159", features = ["derive"] }
1818
serde_json = "1.0.95"
19+
ownable = "1.0.0"
1920

2021
[features]
2122
default = ["source-span"]

examples/get_all_href/main.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use html_parser::{Dom, Node, Result};
2+
use ownable::traits::ToBorrowed;
3+
use std::ops::Deref;
24

35
// This example illustrates how to use the library to get all of the anchor-hrefs from a document.
46

@@ -8,13 +10,13 @@ fn main() -> Result<()> {
810
let iter = dom.children.first().unwrap().into_iter();
911

1012
let hrefs = iter.filter_map(|item| match item {
11-
Node::Element(element) if element.name == "a" => element.attributes["href"].clone(),
13+
Node::Element(element) if element.name == "a" => element.attributes["href"].to_borrowed(),
1214
_ => None,
1315
});
1416

1517
println!("\nThe following links where found:");
1618
for (index, href) in hrefs.enumerate() {
17-
println!("{}: {}", index + 1, href)
19+
println!("{}: {}", index + 1, href.deref())
1820
}
1921

2022
Ok(())

src/dom/element.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use super::node::Node;
22
#[cfg(feature = "source-span")]
33
use super::span::SourceSpan;
4-
use crate::VecSet;
54
use crate::dom::vecmap::VecMap;
5+
use crate::{Attribute, VecSet};
6+
use ownable::{IntoOwned, ToBorrowed, ToOwned};
67
use serde::Serialize;
8+
use std::borrow::Cow;
79
use std::default::Default;
810

911
/// Normal: `<div></div>` or Void: `<meta/>`and `<meta>`
@@ -17,48 +19,49 @@ pub enum ElementVariant {
1719
Void,
1820
}
1921

20-
pub type Attributes = VecMap<String, Option<String>>;
22+
pub type Attributes<'a> = VecMap<Cow<'a, str>, Option<Attribute<'a>>>;
2123

2224
/// Most of the parsed html nodes are elements, except for text
23-
#[derive(Debug, Clone, Serialize, PartialEq)]
25+
#[derive(Debug, Serialize, PartialEq, IntoOwned, ToBorrowed, ToOwned)]
2426
#[serde(rename_all = "camelCase")]
25-
pub struct Element {
27+
pub struct Element<'a> {
2628
/// The id of the element
2729
#[serde(skip_serializing_if = "Option::is_none")]
28-
pub id: Option<String>,
30+
pub id: Option<Attribute<'a>>,
2931

3032
/// The name / tag of the element
31-
pub name: String,
33+
pub name: Cow<'a, str>,
3234

3335
/// The element variant, if it is of type void or not
36+
#[ownable(clone)]
3437
pub variant: ElementVariant,
3538

3639
/// All of the elements attributes, except id and class
3740
#[serde(skip_serializing_if = "VecMap::is_empty")]
38-
pub attributes: Attributes,
41+
pub attributes: Attributes<'a>,
3942

4043
/// All of the elements classes
4144
#[serde(skip_serializing_if = "VecSet::is_empty")]
42-
pub classes: VecSet<String>,
45+
pub classes: VecSet<Attribute<'a>>,
4346

4447
/// All of the elements child nodes
4548
#[serde(skip_serializing_if = "Vec::is_empty")]
46-
pub children: Vec<Node>,
49+
pub children: Vec<Node<'a>>,
4750

4851
#[cfg(feature = "source-span")]
4952
/// Span of the element in the parsed source
5053
#[serde(skip)]
51-
pub source_span: SourceSpan,
54+
pub source_span: SourceSpan<'a>,
5255
}
5356

54-
impl Default for Element {
57+
impl Default for Element<'static> {
5558
fn default() -> Self {
5659
Self {
5760
id: None,
58-
name: "".to_string(),
61+
name: "".into(),
5962
variant: ElementVariant::Void,
60-
classes: VecSet::default(),
61-
attributes: VecMap::default(),
63+
classes: Default::default(),
64+
attributes: Default::default(),
6265
children: vec![],
6366
#[cfg(feature = "source-span")]
6467
source_span: SourceSpan::default(),

src/dom/formatting.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use pest::error::Error as PestError;
55

66
/// This function abstracts the formatting of errors away from the core logic inside parser,
77
/// so that the file is easier to read.
8-
pub fn error_msg(error: PestError<Rule>) -> Result<super::Dom> {
8+
pub fn error_msg(error: PestError<Rule>) -> Result<super::Dom<'static>> {
99
let message = error.renamed_rules(|rule| match *rule {
1010
Rule::EOI => "end of input".to_string(),
1111
Rule::doctype => "doctype element".to_string(),

src/dom/html.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use ownable::{IntoOwned, ToBorrowed, ToOwned};
2+
use serde::Serialize;
3+
use std::borrow::{Borrow, Cow};
4+
use std::fmt::{Debug, Formatter};
5+
use std::ops::Deref;
6+
7+
#[derive(Default, Debug, Serialize, PartialEq, Eq, IntoOwned, ToBorrowed, ToOwned)]
8+
#[serde(transparent)]
9+
pub struct Attribute<'a>(pub Cow<'a, str>);
10+
11+
impl<'a> Attribute<'a> {
12+
pub fn as_str(&self) -> &str {
13+
self.0.borrow()
14+
}
15+
}
16+
17+
impl<S: Into<Cow<'static, str>>> From<S> for Attribute<'static> {
18+
fn from(value: S) -> Self {
19+
Self(value.into())
20+
}
21+
}
22+
23+
impl<'a> Deref for Attribute<'a> {
24+
type Target = str;
25+
26+
fn deref(&self) -> &Self::Target {
27+
self.as_str()
28+
}
29+
}
30+
31+
impl<'a> PartialEq<str> for Attribute<'a> {
32+
fn eq(&self, other: &str) -> bool {
33+
self.deref().eq(other)
34+
}
35+
}
36+
37+
impl<'a> PartialEq<&str> for Attribute<'a> {
38+
fn eq(&self, other: &&str) -> bool {
39+
self.deref().eq(*other)
40+
}
41+
}
42+
43+
impl<'a> PartialEq<str> for &Attribute<'a> {
44+
fn eq(&self, other: &str) -> bool {
45+
(*self).deref().eq(other)
46+
}
47+
}
48+
49+
#[derive(Default, Serialize, PartialEq, Eq, IntoOwned, ToBorrowed, ToOwned)]
50+
#[serde(transparent)]
51+
pub struct Text<'a>(pub Cow<'a, str>);
52+
53+
impl<'a> Text<'a> {
54+
pub fn as_str(&self) -> &str {
55+
self.0.borrow()
56+
}
57+
}
58+
59+
impl Debug for Text<'_> {
60+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
61+
self.as_str().fmt(f)
62+
}
63+
}
64+
65+
impl<S: Into<Cow<'static, str>>> From<S> for Text<'static> {
66+
fn from(value: S) -> Self {
67+
Self(value.into())
68+
}
69+
}
70+
71+
impl<'a> Deref for Text<'a> {
72+
type Target = str;
73+
74+
fn deref(&self) -> &Self::Target {
75+
self.as_str()
76+
}
77+
}
78+
79+
impl<'a> PartialEq<str> for Text<'a> {
80+
fn eq(&self, other: &str) -> bool {
81+
self.deref().eq(other)
82+
}
83+
}
84+
85+
impl<'a> PartialEq<&str> for Text<'a> {
86+
fn eq(&self, other: &&str) -> bool {
87+
self.deref().eq(*other)
88+
}
89+
}
90+
91+
impl<'a> PartialEq<str> for &Text<'a> {
92+
fn eq(&self, other: &str) -> bool {
93+
(*self).deref().eq(other)
94+
}
95+
}

0 commit comments

Comments
 (0)