# selderee

Build a decision tree from many CSS selectors to quickly find which ones match
an HTML element.

Given a set of `(selector, value)` pairs, `selderee` weaves the selectors into
a tree that shares common conditions. Testing one element against the tree takes
far fewer checks than testing each selector in turn. The naive approach is
`O(n * m)`. The tree checks each shared condition once.

The crate does not match against a DOM by itself. It produces an abstract tree.
A builder turns that tree into something callable. The built-in `treeify`
builder renders the tree as text. A DOM plugin builds a `Picker` whose matcher
walks the tree against a concrete element type.

## Installation

```toml
[dependencies]
selderee = "0.1"
```

## Usage

```rust
use selderee::{DecisionTree, DecisionTreeOptions, treeify};

let input = vec![
    ("foo".to_string(), "value 1".to_string()),
    ("bar".to_string(), "value 2".to_string()),
];
let tree = DecisionTree::new(input, DecisionTreeOptions::default())?;
let text = tree.build(treeify);
assert!(text.starts_with("▽\n"));
```

`new` returns a `Result`. An unsupported selector or a parse failure yields a
`SelectorError`, which carries the diagnostic message.

`build` accepts any builder. `treeify` is one. Any closure
`FnOnce(&[DecisionTreeNode<V>]) -> R` is another. A DOM plugin returns a
`Picker`.

## Supported selectors

- Combinators: child `>` and adjacent sibling `+`. Descendant, general
  sibling `~`, and column `||` are rejected at build time.
- Pseudo-classes: `:empty`, `:first-child`, `:last-child`, `:only-child`,
  `:any-link`. Others are rejected at build time.
- Attribute values are case-sensitive by default. List attribute names in
  `DecisionTreeOptions::attributes_with_normalized_values` to make those values
  case-insensitive by default.

## Picking matches

A `Picker<L, V>` wraps a matcher function over an element type `L`.

- `pick_all(el)` returns every matched value with its specificity, in
  tree-walk order.
- `pick1(el, prefer_first)` returns the value from the most specific match. On
  a specificity tie, `prefer_first` chooses the lowest input index, otherwise
  the highest.

## License

Licensed under the [MIT license](LICENSE).
