Skip to main content

tl/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(missing_docs)]
3#![cfg_attr(not(feature = "std"), no_std)]
4#![cfg_attr(feature = "portable-simd", feature(portable_simd))]
5
6mod bytes;
7/// Errors that occur throughout the crate
8pub mod errors;
9/// Inline data structures
10pub mod inline;
11mod parser;
12/// Query selector API
13pub mod queryselector;
14mod stream;
15#[cfg(all(test, feature = "std"))]
16mod tests;
17mod util;
18mod vdom;
19
20#[doc(hidden)]
21#[cfg(feature = "__INTERNALS_DO_NOT_USE")]
22pub mod simd;
23#[cfg(not(feature = "__INTERNALS_DO_NOT_USE"))]
24mod simd;
25
26pub use bytes::Bytes;
27pub use errors::ParseError;
28pub use parser::*;
29use queryselector::Selector;
30pub use vdom::VDom;
31#[cfg(feature = "std")]
32pub use vdom::VDomGuard;
33
34#[cfg(feature = "std")]
35const STD_INLINE_CLASS_HANDLES: usize = 32;
36#[cfg(feature = "std")]
37const STD_INLINE_IDS: usize = 16;
38#[cfg(feature = "std")]
39const STD_INLINE_CLASSES: usize = 16;
40
41/// Parses the given input string
42///
43/// This is the "entry point" and function that is called to parse HTML.
44/// The input string must be kept alive, and must outlive `VDom`.
45/// If you need an "owned" version that takes an input string and can be kept around forever,
46/// consider using `parse_owned()`.
47///
48/// # Errors
49/// Throughout the parser it is assumed that spans never overflow a `u32`.
50/// To prevent this, this function will return an error if the input string length would overflow a `u32`.
51/// If the input string length fits in a `u32`, then it is safe to assume that none of the substrings can overflow a `u32`.
52///
53/// # Example
54/// ```
55/// # use tl::*;
56/// let dom = parse("<div>Hello, world!</div>", ParserOptions::default()).unwrap();
57/// assert_eq!(dom.query_selector("div").unwrap().count(), 1);
58/// ```
59#[cfg(feature = "std")]
60pub fn parse(
61    input: &str,
62    options: ParserOptions,
63) -> Result<
64    VDom<'_, STD_INLINE_CLASS_HANDLES, 0, 0, STD_INLINE_IDS, STD_INLINE_CLASSES, 0>,
65    ParseError,
66> {
67    let mut parser =
68        Parser::<STD_INLINE_CLASS_HANDLES, 0, 0, STD_INLINE_IDS, STD_INLINE_CLASSES, 0>::new(
69            input, options,
70        );
71    parser.parse()?;
72    Ok(VDom::from(parser))
73}
74
75/// Parses the given input string using bounded, allocation-free storage.
76///
77/// Capacity parameters bound the number of parsed nodes, parser stack entries,
78/// root nodes, tracked IDs, tracked classes, and query selector nodes.
79#[cfg(not(feature = "std"))]
80pub fn parse<
81    const MAX_NODES: usize,
82    const MAX_STACK: usize,
83    const MAX_ROOTS: usize,
84    const MAX_IDS: usize,
85    const MAX_CLASSES: usize,
86    const MAX_SELECTOR_NODES: usize,
87>(
88    input: &str,
89    options: ParserOptions,
90) -> Result<
91    VDom<'_, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>,
92    ParseError,
93> {
94    let mut parser = Parser::new(input, options);
95    parser.parse()?;
96    Ok(VDom::from(parser))
97}
98
99/// Parses a query selector
100///
101/// # Example
102/// ```
103/// # use tl::queryselector::selector::Selector;
104/// let selector = tl::parse_query_selector("div#test");
105///
106/// match selector {
107///     Some(Selector::And(left, right)) => {
108///         assert!(matches!(&*left, Selector::Tag(b"div")));
109///         assert!(matches!(&*right, Selector::Id(b"test")));
110///     },
111///     _ => unreachable!()
112/// }
113/// ```
114#[cfg(feature = "std")]
115pub fn parse_query_selector(input: &str) -> Option<Selector<'_>> {
116    let selector = queryselector::Parser::new(input.as_bytes()).selector()?;
117    Some(selector)
118}
119
120/// Parses a query selector using bounded, allocation-free storage.
121#[cfg(not(feature = "std"))]
122pub fn parse_query_selector<const MAX_SELECTOR_NODES: usize>(
123    input: &str,
124) -> Result<Selector<'_, MAX_SELECTOR_NODES>, ParseError> {
125    queryselector::Parser::new(input.as_bytes()).selector::<MAX_SELECTOR_NODES>()
126}
127
128/// Parses the given input string and returns an owned, RAII guarded DOM
129///
130/// # Errors
131/// See [parse]
132///
133/// # Safety
134/// This uses `unsafe` code to create a self-referential-like struct.
135/// The given input string is first leaked and turned into raw pointer, and its lifetime will be promoted to 'static.
136/// Once `VDomGuard` goes out of scope, the string will be freed.
137/// It should not be possible to cause UB in its current form and might become a safe function in the future.
138#[cfg(feature = "std")]
139pub unsafe fn parse_owned(input: String, options: ParserOptions) -> Result<VDomGuard, ParseError> {
140    VDomGuard::parse(input, options)
141}