# Selectors: Multi-IDs and Ranges

## The Problem

Users often need to act on batches of items ("Delete the last 3 notes", "Pin items 1 and 5").
Typing `padz delete 1 2 3` is okay, but `padz delete 1-3` is better.
However, range logic is tricky when different index types (pinned vs regular vs deleted) are involved. Mixing types (`p1-3`) is ambiguous.

## The Solution: Typed Selectors

Padz implements a strict grammar for selecting items. A "Selector" is the user's intent string, which must be parsed and validated before it touches the business logic.

### 1. The Grammar

-   **Regular Index**: `N` (e.g., `1`, `42`)
-   **Pinned Index**: `pX` (e.g., `p1`, `p2`)
-   **Deleted Index**: `dX` (e.g., `d1`, `d5`)
-   **Ranges**: `Start-End` (e.g., `1-5`, `p1-p3`, `d1-d3`)
    -   **Constraint**: Ranges must be homogeneous. `1-3` is valid. `p1-p3` is valid. `1-p3` is **invalid**.
    -   **Constraint**: Start must be ≤ end. `3-5` is valid. `5-3` is **invalid**.

### 2. Processing Pipeline

**Phase 1: Parsing (The "What")**
-   **Location**: `src/padz/index.rs` — function `parse_index_or_range`
-   **Role**: Expands string inputs into a list of `DisplayIndex` enums.
-   **Example**: Input `"1-3"` → Output `vec![Regular(1), Regular(2), Regular(3)]`.

**Phase 2: Resolution (The "Who")**
-   **Location**: `src/padz/api.rs` — function `parse_selectors`
-   **Role**: Converts `DisplayIndex` values to `PadSelector` enums.
-   **Logic**:
    1.  Attempts to parse all inputs as indexes/ranges.
    2.  Deduplicates indexes while preserving order.
    3.  If any input fails to parse as an index, treats the entire input as a **search term**.
    4.  Returns `Vec<PadSelector>` (either all `Index` variants or a single `Title` variant).

**Phase 3: Execution**
-   Command modules receive `Vec<PadSelector>` and resolve to UUIDs using `index_pads`.
-   Out-of-bounds indexes return errors at this stage.

### 3. Search Fallback

If parsing fails (string is not a valid index/range syntax), the entire input is treated as a **search term**:
-   `padz view meeting` → "meeting" is not an index → Treated as title search.
-   All input tokens are joined with spaces and become a single search query.

### 4. Special: Restore and Purge

For commands operating on deleted pads (`restore`, `purge`), bare numbers are auto-prefixed with `d`:
-   `padz restore 3` → Internally becomes `d3`
-   `padz restore 1-3` → Internally becomes `d1-d3`

This is handled in `src/padz/api.rs` — function `parse_selectors_for_deleted`.

### Developer Note

This layered approach means the `commands` module never deals with parsing logic. It receives `Vec<PadSelector>` and works with that abstraction, keeping input handling contained in the API layer.
