# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

`roxml` is a XML reader library for Rust focused on efficient XML parsing without DOM structures or intermediate representations. It supports both `std` and `no_std` environments (requires `alloc`).
Part of the dibbots ecosystem, hosted on Codeberg.

## Common Commands

```bash
# Build
cargo build
cargo build --release

# Test
cargo test                        # std tests
cargo test --all-features
cargo test --no-default-features  # test no_std mode

# Lint
cargo clippy
cargo fmt --check

# Doc
cargo doc --no-deps                            # generate docs

# Benchmarks
cargo bench
cargo make bench-baseline   # save criterion baseline "main"
cargo make bench-compare    # compare against baseline "main"

# Coverage (requires nightly + cargo-llvm-cov + cargo-make + llvm-tools-preview)
cargo make cov          # LCOV report → coverage/lcov.info
cargo make cov-html     # HTML report

# Single test
cargo test test_name

# Embedded compile test
./verify_embedded_compile.sh

# Run tests on embedded device
./run_embedded_tests.sh <board-identifier>
```

See [Makefile.toml](Makefile.toml) for the full task list and the install preconditions at the top of that file.

## Architecture

### Design principles

- **Zero-copy**: all parsing stores `Range<usize>` offsets into the original `&str`; no strings are duplicated.
- **Lazy / no DOM**: `XmlReader::parse` is `O(1)`.  Navigation (`first_child`, `next_sibling`, `children`) scans the source on demand.
- **No recursion in the hot path**: `find_close_tag` is iterative and depth-counts only the tag name being matched.

### Source layout

| File | Role |
|---|---|
| `src/error.rs` | `Error` enum (`NoRoot`, `MalformedXml`) |
| `src/xml_element.rs` | `XmlElement`, `XmlElementType`, `XmlAttribute`, `XmlAttributes`, `XmlChildren` and all low-level parsing helpers; in-module `#[cfg(test)]` unit tests cover internal helper branches not reachable via the public API |
| `src/xml_reader.rs` | `XmlReader` — entry point; skips PI / comments / DOCTYPE to find root element |
| `tests/xml_reader.rs` | integration tests for `XmlReader` (prologue skipping, error cases) |
| `tests/xml_element.rs` | integration tests for `XmlElement` navigation, attributes, mixed-content children |
| `tests/xml_files/` | XML fixtures used by integration tests |
| `benches/roxml.rs` | criterion benchmark (`parse`, `navigate`, `attributes` groups); enabled via `[[bench]] name = "roxml" harness = false` |
| `README.md` | included as crate-level docs via `#[doc = include_str!("../README.md")]` in `src/lib.rs`; the `Usage` code block runs as a doctest |

### Key types

**`XmlElement<'xml>`** — a parsed node.  Fields are private `Range<usize>` offsets:
- `open` — opening tag (or whole node for self-closing / leaf nodes)
- `close` — closing tag, or `open.end..open.end` when absent
- `content_end` — upper bound for sibling navigation (parent's close-tag start, or doc length for root)

**`XmlElementType`** — `Element | Comment | Text | PI`.  CDATA sections are surfaced as `Text`.

**`XmlAttribute<'xml>`** — a `name=value` pair; name and value are separate `Range<usize>` offsets.

### Parsing helpers (all `pub(crate)` in `xml_element.rs`)

| Function | Purpose |
|---|---|
| `skip_whitespace` | advance past ASCII whitespace |
| `find_bytes` | `memchr`-style needle search in a byte slice |
| `find_tag_end` | find unquoted `>` that closes a tag (quote-aware) |
| `extract_tag_name` | slice the tag name from a position after `<` |
| `parse_element_at` | parse one node at a given position; handles element, comment, PI, CDATA |
| `find_close_tag` | iterative depth-counted search for matching `</tag>` |

### Handled XML constructs

- Regular elements and self-closing elements (`<tag/>`)
- Attributes with `"` or `'` quoting; `>` inside attribute values is safe
- Nested same-named elements (depth counter in `find_close_tag`)
- Comments `<!-- -->`, processing instructions `<? ?>`, CDATA (surfaced as `Text`)
- XML declaration and DOCTYPE are silently skipped by `root_element`
- Whitespace-only text between tags is not surfaced by `first_child` / `next_sibling`

## Features / no_std

- `std` (default) — enables `thiserror/std`
- Disable default features for `no_std` usage (crate itself is `#![no_std]` and pulls in `alloc`)

```toml
roxml = { version = "...", default-features = false }
```

## Embedded Support

- [verify_embedded_compile.sh](verify_embedded_compile.sh) checks whether the library still compiles for embedded targets.
- [run_embedded_tests.sh](run_embedded_tests.sh) `<board-identifier>` runs tests on a specific board (which must be connected). The script uses the `laze` build system — see [laze-project.yml](laze-project.yml).

## Testing & coverage notes

- Line coverage is effectively 100% on merged instantiations.  `cargo llvm-cov test --all-features` may report ~99% in the text summary — that gap is a per-monomorphization reporting artifact (the same function compiled into several test binaries), not a real coverage hole; the HTML report and LCOV `DA:` entries both show no uncovered source line.
- Internal unit tests at the bottom of `src/xml_element.rs` construct `XmlElement`, `XmlChildren`, and `XmlAttributes` with raw field values to reach defensive branches that well-formed XML cannot produce (e.g., an unterminated attribute quote after a successful `find_tag_end`).
