winereg 0.1.0

Rust library for parsing, writing, diffing, patching, and scripting Wine/Windows registry files.
Documentation
## winereg

Rust library for parsing, writing, diffing, patching, and scripting Wine registry files.

### Crate Overview
- Parse Wine `.reg` text/files into an in-memory tree.
- Serialize back to `.reg` (atomic file write support).
- Compare registries and export/parse text diffs.
- Apply diffs/patches with configurable options.
- Lightweight DSL for building or modifying registries.

Add to your code (crate name `winereg`):
```rust
use winereg::*;
```

### Core Types
- `KeyNode` / `RegistryKey`
  - `RegistryKey::create_root()`
  - `RegistryKey::create_subkey(&parent, name)` / `create_key_recursive(&parent, path)`
  - `RegistryKey::find_key(&root, path) -> Option<KeyNode>`
  - `set_value(name, RegistryValue)`; `try_delete_value(name) -> Result<()>`
  - `delete_subkey(parent, name, recursive) -> bool`; `try_delete_subkey(parent, name, recursive) -> Result<()>`
  - Snapshots to avoid borrow issues: `snapshot_subkeys(&KeyNode)`, `snapshot_values(&KeyNode)`
  - `get_full_path(&KeyNode)` returns the joined registry path.
- Values
  - `RegistryValue::new(name, RegistryValueData::*)`
  - Variants: `String`, `ExpandString`, `MultiString(Vec<String>)`, `Dword(u32)`, `Qword(u64)`, `Binary(Vec<u8>, u32)`
  - Common type constants: `REG_SZ`, `REG_EXPAND_SZ`, `REG_MULTI_SZ`, `REG_DWORD`, `REG_QWORD`, `REG_BINARY`

### Parsing & Writing (RegistryEditor)
- `RegistryEditor::load_from_file(path) -> Result<LoadResult, ParseError>`
- `RegistryEditor::load_from_text(text) -> Result<LoadResult, ParseError>`
- `RegistryEditor::write_to_file_with_options(key, path, EditorOptions) -> io::Result<()>`
- `RegistryEditor::write_to_file_default(key, path) -> io::Result<()>`
- `RegistryEditor::write_to_string_with_options(key, EditorOptions) -> String`
- `RegistryEditor::write_to_string_default(key) -> String`
- `EditorOptions { relative_base: String, architecture: Architecture }` (`Default`: empty base + `Unknown`)
- `Architecture`: `Unknown`, `Win32`, `Win64`

### Diff & Patch
- Compare: `RegistryComparator.compare_registries(left, right) -> DiffResult`
- Text diff export/parse:
  - `TextDiffExporter.export(&diff, from: Option<&str>, to: Option<&str>) -> String`
  - `TextDiffParser.parse(text) -> Result<DiffResult, String>`
- Apply patch:
  - `RegistryPatcher.apply_patch(target, &diff, PatchOptions) -> PatchResult`
  - `PatchOptions { ignore_failures, create_missing_keys, overwrite_existing_values, delete_empty_keys, validate_before_apply }` (`Default`: ignore_failures=false, create_missing_keys=true, overwrite_existing_values=true, delete_empty_keys=true, validate_before_apply=false)
  - `PatchResult { applied, failed, ignore_failures }`
    - `is_success()` respects `ignore_failures`
    - `applied_count()`, `failed_count()`, `total_count()`
- Convenience on `KeyNode` via `RegistryKeyExt`:
  - `apply_patch(&self, &DiffResult)`
  - `apply_patch_with(&self, &DiffResult, PatchOptions)`
  - `apply_text_patch(&self, text, options) -> Result<PatchResult, String>`
  - `compare_with(&self, other) -> DiffResult`
  - `export_diff_text(&self, other, from, to) -> String`

### DSL (optional)
- `registry(|ctx| { ... }) -> RegistryResult`
  - Set `ctx.relative_base`, `ctx.architecture`
  - `ctx.key("PATH", |k| { ... })`, `ctx.root(|k| { ... })`
- `RegistryKeyDsl` helpers: `value`, `dword`, `qword`, `binary`, `expand_string`, `multi_string`, `delete_value`, `delete_key(recursive)`, `replace_key`, `update_time`
- Mutating existing registry: `modify_registry(registry_result, |k| { ... })`

### Quick Examples
Load, tweak, save:
```rust
use rustregedit::*;

let loaded = RegistryEditor::load_from_file("user.reg")?;
let root = loaded.root_key.clone();
let key = RegistryKey::create_key_recursive(&root, "Software\\MyApp");
key.borrow_mut().set_value(
    "Version",
    RegistryValue::new("Version", RegistryValueData::String("2.0".into()))
);
RegistryEditor::write_to_file_default(&root, "user_out.reg")?;
```

Compare and patch:
```rust
let base = RegistryEditor::load_from_file("user.reg")?.root_key;
let desired = RegistryEditor::load_from_file("user_new.reg")?.root_key;
let diff = RegistryComparator.compare_registries(&base, &desired);
let result = RegistryPatcher.apply_patch(&base, &diff, PatchOptions::default());
assert!(result.is_success());
```

Text diff round-trip:
```rust
let diff = RegistryComparator.compare_registries(&RegistryKey::create_root(), &desired);
let text = TextDiffExporter.export(&diff, Some("old.reg"), Some("new.reg"));
let parsed = TextDiffParser.parse(&text)?;
let apply_res = RegistryPatcher.apply_patch(&RegistryKey::create_root(), &parsed, PatchOptions::default());
assert!(apply_res.is_success());
```

### Text Diff Format (Readable Patch)
The text diff is a compact, section-based format.

#### Anatomy
- Header lines:
  - `# Registry Patch File`
  - Optional: `# Generated: <timestamp>`
  - Optional: `# FROM: <file1>` / `# TO: <file2>`
- Sections:
  - `[ROOT]` for the virtual root (empty path)
  - `[HKEY_LOCAL_MACHINE]`, `[Software\Classes]`, etc.
- Inside each section, change lines:
  - Key add: `+key:<Name>`
  - Key delete: `-key:<Name>`
  - Key properties:
    - `~className:<old>-><new>` (values quoted when needed)
    - `~isSymlink:false->true`
    - `~isVolatile:false->true`
  - Value add/delete:
    - `+"Name"=<typed payload>`
    - `-"Name"=<typed payload>`
  - Value modify:
    - `~"Name"=<old typed payload>-><new typed payload>`
- Value payload encodings:
  - String: `string:"text"` (escapes: `\"`, `\\`, `\n`, `\r`, `\t`, `\0`)
  - Expand string: `expand_string:"text"`
  - Multi-string: `multi_string:["a","b","c"]`
  - Dword: `dword:00112233` (hex, 8 digits)
  - Qword: `qword:0011223344556677` (hex, 16 digits)
  - Binary:
    - `hex:01,02,ff` (REG_BINARY)
    - `hex(ffff1003):01,02` (explicit type in hex)

#### Example Patch
```text
# Registry Patch File
# FROM: old.reg
# TO: new.reg

[ROOT]
+key:HKEY_LOCAL_MACHINE

[HKEY_LOCAL_MACHINE]
~className:"OldClass"->"NewClass"

[HKEY_LOCAL_MACHINE\Software]
+"StringValue"=string:"hello\nworld"
+"DwordValue"=dword:0000002a
~"Mode"=string:"old"->string:"new"
-"Obsolete"=dword:00000001
```

#### Parsing & Applying
- Parse: `let diff = TextDiffParser.parse(text)?;`
- Apply: `RegistryPatcher.apply_patch(target, &diff, options);`
  - `PatchOptions` control behavior (`ignore_failures`, `create_missing_keys`, `overwrite_existing_values`, `delete_empty_keys`, `validate_before_apply`).