| Crates.io | winereg |
| lib.rs | winereg |
| version | 0.1.0 |
| created_at | 2025-12-07 23:49:14.745874+00 |
| updated_at | 2025-12-07 23:49:14.745874+00 |
| description | Rust library for parsing, writing, diffing, patching, and scripting Wine/Windows registry files. |
| homepage | |
| repository | https://github.com/asLody/winereg |
| max_upload_size | |
| id | 1972498 |
| size | 110,559 |
Rust library for parsing, writing, diffing, patching, and scripting Wine registry files.
.reg text/files into an in-memory tree..reg (atomic file write support).Add to your code (crate name winereg):
use winereg::*;
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<()>snapshot_subkeys(&KeyNode), snapshot_values(&KeyNode)get_full_path(&KeyNode) returns the joined registry path.RegistryValue::new(name, RegistryValueData::*)String, ExpandString, MultiString(Vec<String>), Dword(u32), Qword(u64), Binary(Vec<u8>, u32)REG_SZ, REG_EXPAND_SZ, REG_MULTI_SZ, REG_DWORD, REG_QWORD, REG_BINARYRegistryEditor::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, Win64RegistryComparator.compare_registries(left, right) -> DiffResultTextDiffExporter.export(&diff, from: Option<&str>, to: Option<&str>) -> StringTextDiffParser.parse(text) -> Result<DiffResult, String>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() respects ignore_failuresapplied_count(), failed_count(), total_count()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) -> DiffResultexport_diff_text(&self, other, from, to) -> Stringregistry(|ctx| { ... }) -> RegistryResult
ctx.relative_base, ctx.architecturectx.key("PATH", |k| { ... }), ctx.root(|k| { ... })RegistryKeyDsl helpers: value, dword, qword, binary, expand_string, multi_string, delete_value, delete_key(recursive), replace_key, update_timemodify_registry(registry_result, |k| { ... })Load, tweak, save:
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:
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:
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());
The text diff is a compact, section-based format.
# Registry Patch File# Generated: <timestamp># FROM: <file1> / # TO: <file2>[ROOT] for the virtual root (empty path)[HKEY_LOCAL_MACHINE], [Software\Classes], etc.+key:<Name>-key:<Name>~className:<old>-><new> (values quoted when needed)~isSymlink:false->true~isVolatile:false->true+"Name"=<typed payload>-"Name"=<typed payload>~"Name"=<old typed payload>-><new typed payload>string:"text" (escapes: \", \\, \n, \r, \t, \0)expand_string:"text"multi_string:["a","b","c"]dword:00112233 (hex, 8 digits)qword:0011223344556677 (hex, 16 digits)hex:01,02,ff (REG_BINARY)hex(ffff1003):01,02 (explicit type in hex)# 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
let diff = TextDiffParser.parse(text)?;RegistryPatcher.apply_patch(target, &diff, options);
PatchOptions control behavior (ignore_failures, create_missing_keys, overwrite_existing_values, delete_empty_keys, validate_before_apply).