winereg
Rust library for parsing, writing, diffing, patching, and scripting Wine registry files.
Crate Overview
- Parse Wine
.regtext/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):
use *;
Core Types
KeyNode/RegistryKeyRegistryKey::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) -> StringRegistryEditor::write_to_string_default(key) -> StringEditorOptions { 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>) -> StringTextDiffParser.parse(text) -> Result<DiffResult, String>
- Apply patch:
RegistryPatcher.apply_patch(target, &diff, PatchOptions) -> PatchResultPatchOptions { 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()respectsignore_failuresapplied_count(),failed_count(),total_count()
- Convenience on
KeyNodeviaRegistryKeyExt:apply_patch(&self, &DiffResult)apply_patch_with(&self, &DiffResult, PatchOptions)apply_text_patch(&self, text, options) -> Result<PatchResult, String>compare_with(&self, other) -> DiffResultexport_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| { ... })
- Set
RegistryKeyDslhelpers: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:
use *;
let loaded = load_from_file?;
let root = loaded.root_key.clone;
let key = create_key_recursive;
key.borrow_mut.set_value;
write_to_file_default?;
Compare and patch:
let base = load_from_file?.root_key;
let desired = load_from_file?.root_key;
let diff = RegistryComparator.compare_registries;
let result = RegistryPatcher.apply_patch;
assert!;
Text diff round-trip:
let diff = RegistryComparator.compare_registries;
let text = TextDiffExporter.export;
let parsed = TextDiffParser.parse?;
let apply_res = RegistryPatcher.apply_patch;
assert!;
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>
- Key add:
- 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)
- String:
Example Patch
# 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);PatchOptionscontrol behavior (ignore_failures,create_missing_keys,overwrite_existing_values,delete_empty_keys,validate_before_apply).