tl

repository·master·Indexed 19 days ago

https://github.com/y21/tl

A high-performance HTML parser written in pure Rust designed for speed and ease of use. It provides a simple DOM API, CSS-like query selectors, and supports SIMD-accelerated parsing via a nightly Rust compiler. The library allows for both immutable and mutable DOM manipulation, including attribute modification and node traversal, and offers configurable tracking for IDs and classes to optimize element lookups.

Tokens
6.9K
Snippets
31
Records
36
Agent score
64%

What's inside tl

  1. Enable SIMD-accelerated parsing

    master

    The tl parser includes optimized utility functions that use SIMD (Single Instruction, Multiple Data) to process bytes more efficiently.

    To use these, you must:

    1. Enable the simd feature flag in your Cargo.toml.
    2. Use a nightly Rust compiler, as the feature relies on the unstable portable_simd nightly feature.

    If the simd feature is not enabled, the crate falls back to highly optimized stable alternatives using manual loop unrolling.

  2. Run fuzz tests with cargo-fuzz

    master

    The tl-fuzz package contains fuzz tests designed to find bugs such as crashes or infinite loops. You can execute these tests using cargo-fuzz. To run a specific test, identify the target name from the files located in the fuzz_targets folder and use the cargo fuzz run command.

    cargo fuzz run <fuzz_test>
  3. Install tl

    master

    Add tl to your Cargo.toml dependencies. You can use the default configuration or enable the simd feature for accelerated parsing (note that the simd feature requires a nightly Rust compiler).

    [dependencies]
    tl = "0.7.8"
    # or, with explicit SIMD support (requires a nightly compiler!)
    tl = { version = "0.7.8", features = ["simd"] }
  4. Access and manipulate HTML tag children

    master

    The HTMLTag struct provides two ways to access its children via wrapper types:

    1. Children (Immutable):

      • top(): Returns the direct, topmost children (including whitespace nodes).
      • all(parser): Returns a slice containing all subnodes, including deeply nested children.
      • boundaries(parser): Returns the start and end boundaries of the children.
    2. ChildrenMut (Mutable):

      • top_mut(): Returns a mutable slice of the direct, topmost children.

    Important Distinction: top() only returns the immediate children of the tag. all() returns the entire subtree of nodes contained within that tag.

    // Example: Accessing all subnodes
    let a = dom.get_element_by_id("a").unwrap().get(parser).unwrap().as_tag().unwrap();
    assert_eq!(a.children().all(parser).len(), 7);
  5. How to use the tl parser

    master

    Users should not construct the Parser struct directly. Instead, use the high-level tl::parse() function, which returns a VDom (Virtual DOM) containing the parsed tree.

    Note: The Parser struct itself is an internal implementation detail used during the parsing phase.

  6. Configure the HTML Parser with ParserOptions

    master

    The ParserOptions struct allows you to configure the behavior of the HTML parser. By default, ParserOptions::default() is optimized for raw parsing speed.

    If your use case requires efficient lookups of elements by ID or class name, you should enable tracking. Enabling tracking causes the parser to cache HTML nodes in lookup tables on the fly as they are encountered in the source code.

    // Default behavior (optimized for raw parsing)
    let options = ParserOptions::default();
    
    // Optimized for ID/Class lookups
    let options = ParserOptions::new()
        .track_ids()
        .track_classes();
  7. Use VDom to interact with a parsed HTML document

    master

    The VDom struct represents the parsed Document Object Model (DOM). It acts as a wrapper around the internal Parser. Most operations that require resolving NodeHandles to actual Node objects require a reference to the underlying Parser, which can be accessed via VDom::parser() or VDom::parser_mut().

    let html = r#"<div><p href="/about" id="find-me">Hello world</p></div>"#;
    let mut dom = tl::parse(html, Default::default()).unwrap();
    
    // Accessing the parser to resolve handles
    let parser = dom.parser();
    // ... use parser with handles ...
  8. Use VDomGuard for owned HTML parsing

    master

    When you need to parse an owned String rather than a borrowed &str, use VDomGuard. It uses RAII to ensure the input string is freed once the guard goes out of scope. You can access the underlying VDom via get_ref() or get_mut_ref().

    // Note: VDomGuard::parse is internal/pub(crate) in this snippet,
    // typically accessed via a public wrapper like tl::parse_owned()
    let guard = tl::parse_owned(input_string, options).unwrap();
    let dom = guard.get_ref();
  9. Find elements using Query Selectors

    master

    You can use the query_selector method on a Dom instance to find elements matching a CSS selector. This returns an iterator over the matching nodes.

    let dom = tl::parse(r#"<div><img src=\"cool-image.png\" /></div>"#, tl::ParserOptions::default()).unwrap();
    let img = dom.query_selector("img[src]").unwrap().next();
        
    assert!(img.is_some());
  10. Mutate HTML attributes

    master

    To modify the DOM, you must use the mutable versions of the parser and node accessors:

    1. Obtain a mutable parser via dom.parser_mut().
    2. Resolve the node using anchor.get_mut(parser_mut).
    3. Cast the node to a tag using as_tag_mut().
    4. Access attributes via attributes_mut() and use get_mut("key") to modify them.
    let input = r#"<div><a href=\"/about\">About</a></div>"#;
    let mut dom = tl::parse(input, tl::ParserOptions::default())
      .expect("HTML string too long");
      
    let anchor = dom.query_selector("a[href]")
      .expect("Failed to parse query selector")
      .next()
      .expect("Failed to find anchor tag");
    
    let parser_mut = dom.parser_mut();
    
    let anchor = anchor.get_mut(parser_mut)
      .expect("Failed to resolve node")
      .as_tag_mut()
      .expect("Failed to cast Node to HTMLTag");
    
    let attributes = anchor.attributes_mut();
    
    attributes.get_mut("href")
      .flatten()
      .expect("Attribute not found or malformed")
      .set("http://localhost/about");
    
    assert_eq!(attributes.get("href").flatten(), Some(&"http://localhost/about".into()));
  11. Iterate over HTML nodes

    master

    To traverse the document structure, use the nodes() method on the Dom instance to get an iterator over all nodes in the document.

    let dom = tl::parse(r#"<div><img src=\"cool-image.png\" /></div>"#, tl::ParserOptions::default()).unwrap();
    let img = dom.nodes()
      .iter()
      .find(|node| {
        node.as_tag().map_or(false, |tag| tag.name() == "img")
      });
        
    assert!(img.is_some());
  12. Parse HTML with tl::parse()

    master

    Use tl::parse() to convert an HTML source string into a DOM tree. It accepts a string slice and a tl::ParserOptions object.

    Note: tl may silently ignore invalid tags (similar to browser behavior), which can result in large sections of the document being omitted from the resulting tree if the HTML is malformed.

    let dom = tl::parse(r#"<p id=\"text\">Hello</p>"#, tl::ParserOptions::default()).unwrap();