rust-switcher 1.0.8

Windows keyboard layout switcher and text conversion utility
Documentation
# AGENTS.md


This file provides guidance to WARP (warp.dev) when working with code in this repository.

## Overview


Rust Switcher is a Windows 11 utility for converting text between Russian (ЙЦУКЕН) and English (QWERTY) keyboard layouts. It's a native Win32 GUI application using the `windows` crate directly (no framework).

## Build Commands


This project requires **Rust nightly** (specified in `rust-toolchain.toml`) and **MSVC toolchain**.

```powershell
# Development build with debug tracing

cargo +nightly build --features debug-tracing

# Release build

cargo +nightly build --release --locked

# Run tests

cargo +nightly test --locked

# Format check

cargo +nightly fmt --check

# Clippy (mirrors CI)

cargo +nightly clippy --all-targets --all-features -- -D warnings
```

### Bacon (recommended for development)


The project uses `bacon` for a fast development loop. Run `bacon` and use keybindings:
- `d` — dev-long: fmt check, clippy, debug build, run app
- `r` — release-long: fmt check, clippy, release build, run app
- `t` — test-long: fmt check, clippy, run tests
- `p` — dushnota: maximum strictness clippy (includes `clippy::pedantic`)

### Enable logging


```powershell
$env:RUST_LOG="trace"
cargo +nightly run -F debug-tracing
```

## Code Style Requirements


### Strict Clippy Lints


The codebase enforces strict linting. These are **denied** (will fail CI):
- `clippy::unwrap_used` — use `ok()`, `map()`, `?` operator, or explicit error handling
- `clippy::expect_used` — same as above
- `clippy::todo`, `clippy::unimplemented` — no TODO/unimplemented macros in committed code
- `clippy::dbg_macro` — no debug macros

Exception: Tests (in `src/tests/`) allow `unwrap_used` and `expect_used`.

### Formatting


Configured in `rustfmt.toml`:
- `imports_granularity = "Crate"` — group imports by crate
- `group_imports = "StdExternalCrate"` — std first, then external, then local

## Architecture


### Module Structure


```
src/
├── main.rs           # Entry point, single-instance guard
├── app.rs            # AppState struct (all UI state, control handles)
├── config/           # Config load/save, validation, hotkey definitions
├── domain/text/      # Core conversion logic
│   ├── mapping.rs    # RU↔EN character mapping (convert_ru_en_bidirectional)
│   ├── convert.rs    # Selection conversion, layout switching
│   └── last_word.rs  # Last-word conversion logic
├── conversion/       # High-level conversion API
│   ├── clipboard.rs  # Clipboard operations
│   └── input.rs      # Input simulation
├── input/            # Input handling
│   ├── hotkeys.rs    # Win32 hotkey registration (RegisterHotKey)
│   └── ring_buffer.rs# Keystroke buffer for last-word tracking
├── platform/
│   ├── win/          # Windows implementation
│   │   ├── window.rs # Window creation, message loop
│   │   ├── keyboard/ # Keyboard hooks (WH_KEYBOARD_LL)
│   │   ├── tray.rs   # System tray icon
│   │   └── ...
│   └── ui/           # UI components
│       ├── themes.rs # Light/dark theme support
│       └── ...
└── utils/            # Helpers, tracing setup
```

### Key Patterns


**AppState**: Central state struct stored in window user data (`GWLP_USERDATA`). Access via `with_state_mut(hwnd, |state| ...)`.

**Error handling**: Uses `ui_call!` and `ui_try!` macros to push errors to a notification queue rather than panicking. Errors are shown to users via balloon notifications.

**Hotkey system**: Two modes:
1. Traditional hotkeys via `RegisterHotKey` (single chord)
2. Chord sequences (e.g., double-tap Shift) tracked via `HotkeySequence` and runtime state

**Config**: JSON at `%APPDATA%\RustSwitcher\config.json`. Loaded via `confy` crate. Always validate with `cfg.validate_hotkey_sequences()` before saving.

### Win32 Specifics


- Window class: `RustSwitcherMainWindow`
- Uses `WH_KEYBOARD_LL` hook for keystroke capture (in `keyboard/capture.rs`)
- Uses `WH_MOUSE_LL` hook for mouse events
- Tray icon via `Shell_NotifyIconW`
- Dark mode via `DwmSetWindowAttribute` and custom `WM_CTLCOLOR*` handling

## Testing


Tests are in `src/tests/`. Run a single test:

```powershell
cargo +nightly test test_name -- --nocapture
```

Test modules cover: config I/O, config validation, hotkey formatting, keyboard sequences, character mapping invariants, ring buffer.

## Agent Workflow Requirements


When making changes in this repository, always:

- Use isolated task workspaces: parallel execution in a single working directory is forbidden.
- Mandatory model: **1 task = 1 branch = 1 worktree = 1 pull request**.
- Create a dedicated `git worktree` for each task before editing files.
- Run edits, checks, commits, and pushes only from the assigned task worktree.
- Keep the primary checkout for sync and integration only (`fetch`, `pull`, `merge`, worktree create/remove).
- Remove task worktrees after merge and delete task branches when appropriate.
- Run formatting, linting, build, and tests before reporting results:
  - `cargo +nightly fmt --check`
  - `cargo +nightly clippy --all-targets --all-features -- -D warnings`
  - `cargo +nightly build --features debug-tracing`
  - `cargo +nightly test --locked`
- Address and fix any findings from these checks before finalizing work.

## Rust Build Isolation


When multiple task worktrees run concurrently, keep Rust build outputs isolated per worktree:

```powershell
$env:CARGO_TARGET_DIR = ".target"
```

Do not share a common `CARGO_TARGET_DIR` across concurrent task worktrees.

## Git Workflow


- Do not push directly to `dev` or `main`.
- Always create a feature branch (recommended prefix: `codex/`).
- `git push` must target the current feature branch only (never `dev`/`main`).
- Open a Pull Request to `dev` (or `main` only for explicit release flow).
- Before `git push`/`gh pr create`, run local checks:
  - `cargo +nightly fmt --check`
  - `cargo +nightly clippy --all-targets --all-features -- -D warnings`
  - `cargo +nightly build --features debug-tracing`
  - `cargo +nightly test --locked`
  - `actionlint -color`
  - `zizmor --min-severity medium .`
- After creating/updating a PR, monitor checks and follow up:
  - `gh pr checks <PR_NUMBER> --watch`
  - then re-check after delay: `Start-Sleep -Seconds 300; gh pr checks <PR_NUMBER>`
- Merge only through PR after required checks pass.
- Use `gh` CLI for PR creation and branch-protection operations when available.