Skip to main content

tl/
lib.rs

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