liblitho 0.2.0

cli tool to flash/clone the images to storage devices
Documentation
# Platform segregation plan

**Status:** In progress  
**Goal:** Clear module boundaries for Linux, Windows, and macOS behind shared traits. Portable flash/clone logic stays OS-agnostic.

---

## Problem

| Area | Today | Issue |
|------|--------|--------|
| Block I/O | `DeviceReader` / `DeviceWriter` + `PlatformDevice` | OK — keep and refine |
| Enum / size / removable | `devices.rs` + `windows_devices` | Linux in facade; Windows in platform |
| Path / system disk / mounts | `devices.rs` + heavy `cfg` | God module |
| Unmount / prep | Linux in `devices.rs`; Windows in `windows_access` | Asymmetric |
| Privilege | `tui/privilege/{linux,windows,unsupported}` | Good pattern — not shared with lib |
| Error hints | `lib.rs` Windows-only | Policy leak into core |

**Asymmetry:** Windows has `platform/windows*.rs`; Linux safety/enum largely lives in `devices.rs`.

---

## Non-goals (portable core)

These stay outside platform traits:

- `flash` / `clone` / stream-xz / SHA-256 verify
- Progress + cancel protocol
- `optimal_io_block_size_*` (pure table)
- CLI / TUI UX (except calling platform façades)

---

## Base traits

### 1. `DeviceReader` / `DeviceWriter` (existing)

Raw block I/O handles. Capability methods with defaults:

- `supports_incremental_sync`, `sync_written_range`
- `write_at`, inline verify (`supports_inline_verify`, `rewind_for_verify`, `read_for_verify`)
- `flush_and_sync` / `flush_and_sync_with_progress`

**Dispatch:** `Box<dyn …>` for open handles (or associated types on `Platform`).

### 2. `DeviceInventory`

| Method | Role |
|--------|------|
| `list_storage_devices()` | Enumerate flashable disks |
| `device_size_bytes(path)` | Capacity |
| `is_removable(path)` | Removable media flag |

### 3. `DevicePathOps`

| Method | Role |
|--------|------|
| `validate_whole_disk_path(path)` | Whole-disk only; reject partitions |
| `paths_equivalent(a, b)` | Path identity (Windows normalization) |

### 4. `DeviceSafety`

| Method | Role |
|--------|------|
| `refuse_system_disk(path)` | Protect boot/root disk |
| `list_mounts(path)` | Mounted volumes on target |
| `refuse_if_busy(path)` | Non-empty mounts → error |

### 5. `VolumeOps`

```rust
pub trait VolumeOps {
    type Guard: Send;
    fn prepare_for_io(path: &str, auto_unmount: bool) -> Result<Self::Guard, DeviceError>;
    fn unmount_volumes(path: &str) -> Result<Vec<String>, DeviceError>;
}
```

| Platform | Guard | Behavior |
|----------|-------|----------|
| Linux | unit / empty | `umount` partitions |
| Windows | session locks | lock + dismount; outlive writer |
| macOS | stub | `Unsupported` until implemented |

### 6. `PrivilegeOps` (optional, later)

Promote `tui/privilege` into `platform/*/privilege.rs`.

### 7. Small helpers

- `user_cache_dir()` for cancel flags
- `hint_for_io_error(&io::Error) -> Option<String>`

### Umbrella

```rust
pub trait Platform: DeviceInventory + DevicePathOps + DeviceSafety + VolumeOps {
    type Reader: DeviceReader;
    type BufferedReader: DeviceReader;
    type Writer: DeviceWriter;
    // open helpers…
}

pub type Active = /* linux | windows | macos | unsupported */;
```

`devices.rs` becomes DTOs + `DeviceError` + thin free-fn wrappers over `Active`.

---

## Target tree

```text
src/platform/
  mod.rs                 # Active alias + public façade free-fns
  traits/
    mod.rs
    io.rs
    inventory.rs
    path.rs
    safety.rs
    volume.rs
  linux/
    mod.rs
    io.rs
    inventory.rs
    path.rs
    safety.rs
    volume.rs
  windows/
    mod.rs
    io.rs
    inventory.rs
    path.rs
    safety.rs
    volume.rs
    ffi.rs               # shared wide-string / handle helpers (as needed)
  macos/
    mod.rs
    io.rs
    inventory.rs
    path.rs
    safety.rs
    volume.rs
  unsupported/
    mod.rs
```

**Rule:** no `cfg(target_os)` in `lib.rs` / `devices.rs` / `main.rs` except via `platform` façade.

---

## Dispatch

| Concern | Strategy |
|---------|----------|
| Inventory / path / safety / volume | Static (`Active::…`) |
| Open device handles | `dyn` or associated types |
| Privilege | Module façade or static trait |

---

## Migration order

1. Introduce traits + OS modules; wrap existing code.
2. Move **Linux** inventory / path / safety / volume out of `devices.rs`.
3. Reshape **Windows** `windows_devices` / `windows_access` into inventory/path/safety/volume.
4. Wire `VolumeOps::Guard` into writer open (Windows session lifetime).
5. **macOS** stubs for non-I/O traits.
6. (Later) privilege move from TUI.
7. Thin `devices.rs`; delete leftover cfg soup.
8. (Later) mock platform for flash unit tests.

---

## Design decisions (locked for this implementation)

1. **ZST platform types** with associated functions (`LinuxPlatform::list_…`) for umbrella `Platform`.
2. **Writer owns volume guard** where needed (Windows session field stays on writer).
3. **macOS:** stubs return `DeviceError::Unsupported` for inventory/safety/volume; keep existing I/O.
4. **Privilege:** leave in `tui/privilege` for this pass; re-export later if needed.
5. **Public API:** keep `liblitho::devices::*` free functions stable; they delegate to platform.

---

## Success criteria

- [x] Traits live under `platform/traits/`
- [x] Linux / Windows / macOS each have `io`, `inventory`, `path`, `safety`, `volume`
- [x] `devices.rs` is a thin façade over `Active` (+ portable block-size policy)
- [x] `cargo test` and `cargo clippy` clean
- [x] Real-io feature still builds

### Follow-up completed

- [x] Move privilege into `platform/*/privilege.rs` (`PrivilegeOps`); TUI re-exports
- [x] Windows writer holds `VolumeOps::Guard`; `preflight_for_io` vs exclusive `prepare_for_io`
- [x] Dropped top-level `windows_devices` / `windows_access` re-exports (use `windows::`)

### Deferred (later)

- [ ] Mock `Platform` for flash unit tests
- [ ] macOS inventory / safety / elevation beyond stubs