Skip to content

Commit 5289c5b

Browse files
add comments via cursor
1 parent 68a8238 commit 5289c5b

2 files changed

Lines changed: 59 additions & 1 deletion

File tree

src/backend/html_parser.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
1+
//! HTML parsing facade used across providers.
2+
//!
3+
//! This module defines:
4+
//! - `HtmlElement`: a small wrapper for raw HTML snippets that signals intent ("this is HTML"), not an arbitrary `String`.
5+
//! - `HtmlParser`: a trait that abstracts querying and extracting content from documents and fragments, implemented by
6+
//! backend-specific adapters (see `html_parser::scraper`).
7+
//! - `ParseHtml`: a helper trait for types that can be constructed directly from an `HtmlElement`.
8+
//!
9+
//! The goal is to keep scraping code decoupled from any particular HTML
10+
//! library. Providers operate only on this trait surface, which makes tests
11+
//! fast and the implementation swappable.
112
use std::error::Error;
213
use std::ops::Deref;
314

4-
/// Intended to represent html, not just a string
15+
/// Wrapper type that represents HTML, not just an arbitrary `String`.
16+
///
17+
/// This helps prevent accidental confusion between raw text and HTML
18+
/// fragments/documents at the type level.
519
#[derive(Debug, Clone, PartialEq, Eq)]
620
pub struct HtmlElement(String);
721

@@ -16,18 +30,22 @@ impl Deref for HtmlElement {
1630
pub mod scraper;
1731

1832
pub trait ParseHtml: Sized {
33+
/// Error type emitted during HTML parsing/construction.
1934
type ParseError: Error;
35+
/// Construct `Self` from an `HtmlElement`.
2036
fn parse_html(html: HtmlElement) -> Result<Self, Self::ParseError>;
2137
}
2238
impl HtmlElement {
2339
#[inline]
40+
/// Creates a new `HtmlElement` from any `Into<String>` value.
2441
pub fn new<T: Into<String>>(raw_str: T) -> Self {
2542
let s: String = raw_str.into();
2643
Self(s)
2744
}
2845
}
2946

3047
pub trait HtmlParser {
48+
/// Returns the direct children of `element` as separate `HtmlElement`s.
3149
fn get_element_children(&self, element: &HtmlElement) -> Vec<HtmlElement>;
3250
/// "Document-wide"
3351
/// Get the first matching element for the given selector / class
@@ -40,8 +58,13 @@ pub trait HtmlParser {
4058
fn get_matching_elements_from(&self, from: &HtmlElement, class: &str) -> Vec<HtmlElement>;
4159

4260
/// "Document-wide"
61+
/// Returns all elements matching the provided CSS selector.
4362
fn get_matching_elements(&self, selector: &str) -> Vec<HtmlElement>;
63+
/// Returns the inner HTML string of the provided element.
4464
fn get_inner_html(&self, document: &HtmlElement) -> String;
65+
/// Returns the inner text of the provided element (implementation-defined
66+
/// granularity; usually the first text node trimmed).
4567
fn get_inner_text(&self, document: &HtmlElement) -> String;
68+
/// Returns the value of an attribute on the provided element, if present.
4669
fn get_element_attr(&self, element: &HtmlElement, attr_to_find: &str) -> Option<String>;
4770
}

src/backend/html_parser/scraper.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
//! Minimal HTML scraping adapter built on top of the `scraper` crate.
2+
//!
3+
//! This module provides a concrete `HtmlParser` implementation (`Scraper`)
4+
//! used by providers to extract content from HTML strings via CSS selectors.
5+
//! It converts `HtmlElement` inputs into a parsed document/fragment and
6+
//! exposes small helper methods to query elements, attributes and text.
7+
//!
8+
//! Notes and caveats:
9+
//! - Fragment parsing: most methods parse a small `HtmlElement` fragment and then select using `*` to reach the first real node via
10+
//! `nth(1)`. This aligns with how `scraper` represents fragment roots.
11+
//! - Selector parsing: `AsSelector` unwraps on parse errors; only valid CSS selectors should be passed.
12+
//! - Text extraction: `get_inner_text` returns the first text node trimmed. For complex nodes with multiple text parts, call it on
13+
//! the most specific element.
114
use scraper::{Element, ElementRef, Selector, html, node};
215

316
use crate::backend::html_parser::{HtmlElement, HtmlParser};
@@ -8,6 +21,8 @@ pub struct Scraper {
821
}
922

1023
impl Scraper {
24+
/// Creates a new `Scraper` by parsing the provided HTML string as a full
25+
/// document.
1126
#[inline]
1227
pub fn new(document: HtmlElement) -> Self {
1328
let document = html::Html::parse_document(&document);
@@ -17,6 +32,9 @@ impl Scraper {
1732

1833
pub trait AsSelector {
1934
#![allow(clippy::wrong_self_convention)]
35+
/// Converts a CSS selector string into a parsed `Selector`.
36+
///
37+
/// Panics if the selector is invalid.
2038
fn as_selector(self) -> Selector;
2139
}
2240

@@ -28,6 +46,9 @@ impl AsSelector for &str {
2846
}
2947

3048
impl HtmlParser for Scraper {
49+
/// Returns the first text node inside the provided `HtmlElement` fragment,
50+
/// trimmed of surrounding whitespace. Returns an empty string if no text
51+
/// node is present.
3152
fn get_inner_text(&self, document: &super::HtmlElement) -> String {
3253
let el = html::Html::parse_fragment(document);
3354

@@ -41,6 +62,9 @@ impl HtmlParser for Scraper {
4162
}
4263

4364
#[inline]
65+
/// Returns the first element matching `class` within the `from` fragment.
66+
/// The result is returned as a new `HtmlElement` containing the matched
67+
/// element's HTML.
4468
fn get_element_from(&self, from: &HtmlElement, class: &str) -> Option<HtmlElement> {
4569
html::Html::parse_fragment(from)
4670
.select(&class.as_selector())
@@ -49,17 +73,23 @@ impl HtmlParser for Scraper {
4973
}
5074

5175
#[inline]
76+
/// Returns the inner HTML of the first child element within the provided
77+
/// fragment. If no child is found, returns an empty string.
5278
fn get_inner_html(&self, document: &super::HtmlElement) -> String {
5379
let el = html::Html::parse_fragment(document);
5480
el.select(&"*".as_selector()).nth(1).map(|el| el.inner_html()).unwrap_or_default()
5581
}
5682

5783
#[inline]
84+
/// Selects the first element in the full document that matches `class` and
85+
/// returns it as an `HtmlElement`.
5886
fn get_element(&self, class: &str) -> Option<super::HtmlElement> {
5987
self.document.select(&class.as_selector()).next().map(|el| HtmlElement::new(el.html()))
6088
}
6189

6290
#[inline]
91+
/// Returns the value of attribute `attr_to_find` on the first child element
92+
/// within the provided fragment, if present.
6393
fn get_element_attr(&self, element: &super::HtmlElement, attr_to_find: &str) -> Option<String> {
6494
let el = html::Html::parse_fragment(element);
6595

@@ -71,20 +101,25 @@ impl HtmlParser for Scraper {
71101
}
72102

73103
#[inline]
104+
/// Returns the children of the provided element fragment as individual
105+
/// `HtmlElement` nodes.
74106
fn get_element_children(&self, element: &super::HtmlElement) -> Vec<super::HtmlElement> {
75107
let doc = html::Html::parse_fragment(element);
76108

77109
doc.select(&"*".as_selector()).skip(2).map(|el| HtmlElement::new(el.html())).collect()
78110
}
79111

80112
#[inline]
113+
/// Returns all elements in the full document matching the CSS selector.
81114
fn get_matching_elements(&self, selector: &str) -> Vec<HtmlElement> {
82115
self.document
83116
.select(&selector.as_selector())
84117
.map(|el| HtmlElement::new(el.html()))
85118
.collect()
86119
}
87120

121+
/// Returns all elements matching the CSS selector inside the provided
122+
/// fragment.
88123
fn get_matching_elements_from(&self, from: &HtmlElement, class: &str) -> Vec<HtmlElement> {
89124
html::Html::parse_fragment(from)
90125
.select(&class.as_selector())

0 commit comments

Comments
 (0)