Skip to content

Implement RegistryManager #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ features = [
"Win32_UI_WindowsAndMessaging",
"Win32_UI_Shell",
]

[dependencies.windows-registry]
version = "0.5.1"
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
# next-winebridge
The source code of the Next version of WineBridge

## Testing
Tests for winebridge are meant to be run inside wine. Generate the test executables using the following command:
```bash
cargo test --no-run --lib
```

This will generate the test executables in the `target/$TARGET/debug/deps` which can be run using wine as follows:
```bash
wine target/$TARGET/debug/deps/next-winebridge-*.exe --test-threads=1
```

The `--test-threads=1` flag is required becase parallel operations on Wine registry can interfere with each other and cause tests to fail. This issue is not specific to this project.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod processes;
mod registry;

use bottles_core::proto::{self, wine_bridge_server::WineBridge};
use processes::{manager::ProcessManager, process::ProcessIdentifier};
Expand Down
205 changes: 205 additions & 0 deletions src/registry/manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
use std::path::Path;
use windows_registry::*;

#[derive(Debug, Eq, PartialEq)]
pub enum Data {
DWord(u32),
QWord(u64),
String(String),
ExpandString(String),
MultiString(Vec<String>),
Bytes(Vec<u8>),
Other,
}

#[derive(Debug, Copy, Clone)]
pub enum Hive {
ClassesRoot,
CurrentConfig,
CurrentUser,
LocalMachine,
Users,
}

impl Hive {
pub fn inner(&self) -> &Key {
match self {
Hive::ClassesRoot => CLASSES_ROOT,
Hive::CurrentConfig => CURRENT_CONFIG,
Hive::CurrentUser => CURRENT_USER,
Hive::LocalMachine => LOCAL_MACHINE,
Hive::Users => USERS,
}
}
}

trait KeyExtension {
fn get(hive: Hive, subkey: &Path) -> Result<Key> {
hive.inner().open(subkey.display().to_string())
}

fn new(hive: Hive, subkey: &Path) -> Result<Key> {
hive.inner().create(subkey.display().to_string())
}

fn delete(hive: Hive, subkey: &Path) -> Result<()> {
hive.inner().remove_tree(subkey.display().to_string())
}

fn value(&self, name: &str) -> Result<Value>;
fn values(&self) -> Result<Vec<(String, Value)>>;
fn create_value(&self, name: &str, data: Data) -> Result<()>;
fn rename_value(&self, old_name: &str, new_name: &str) -> Result<()>;
}

impl KeyExtension for windows_registry::Key {
fn value(&self, name: &str) -> Result<Value> {
let value = self.get_value(name)?;
Ok(value)
}

fn values(&self) -> Result<Vec<(String, Value)>> {
let mut values = Vec::new();
for value in self.values()? {
values.push(value);
}
Ok(values)
}

fn create_value(&self, name: &str, data: Data) -> Result<()> {
match data {
Data::Bytes(val) => self.set_bytes(name, Type::Bytes, &val),
Data::DWord(val) => self.set_u32(name, val),
Data::QWord(val) => self.set_u64(name, val),
Data::String(val) => self.set_string(name, &val),
Data::ExpandString(val) => self.set_expand_string(name, &val),
Data::MultiString(val) => {
let d: Vec<&str> = val.iter().map(|s| s.as_str()).collect();
self.set_multi_string(name, &d)
}
_ => unreachable!(),
}
}

fn rename_value(&self, old_name: &str, new_name: &str) -> Result<()> {
let value = self.get_value(old_name)?;
self.remove_value(old_name)?;
self.set_value(new_name, &value)
}
}

pub struct RegistryManager;

impl RegistryManager {
pub fn value(&self, hive: &Hive, subkey: &str, name: &str) -> Result<Value> {
let key = hive.inner().open(subkey)?;

key.value(name)
}

pub fn key(&self, hive: &Hive, subkey: &str) -> Result<Key> {
hive.inner().open(subkey)
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;

fn test_subkey() -> PathBuf {
PathBuf::from("Software\\WineBridgeTest")
}

#[test]
fn test_create_key() {
let hive = Hive::CurrentUser;
let subkey = test_subkey();
assert!(Key::new(hive, &subkey).is_ok(), "Failed to create key");

// Check if the key exists
let key = hive.inner().open(&subkey.display().to_string());
assert!(key.is_ok(), "Failed to open key");

// Clean up
hive.inner()
.remove_tree(&subkey.display().to_string())
.expect("Failed to delete test key");
}

#[test]
fn test_get_key() {
let hive = Hive::CurrentUser;
let subkey = test_subkey();
Key::new(hive, &subkey).expect("Failed to create key");

// Get the key
let key = Key::get(hive, &subkey);
assert!(key.is_ok(), "Failed to open key");

// Clean up
hive.inner()
.remove_tree(&subkey.display().to_string())
.expect("Failed to delete test key");
}

#[test]
fn test_delete_key() {
let hive = Hive::CurrentUser;
let subkey = test_subkey();
Key::new(hive, &subkey).expect("Failed to create key");

// Delete the key
Key::delete(hive, &subkey).expect("Failed to delete key");

// Check if the key is deleted
let key = hive.inner().open(&subkey.display().to_string());
assert!(key.is_err(), "Key still exists after deletion");
}

#[test]
fn test_create_value() {
let hive = Hive::CurrentUser;
let subkey = test_subkey();

let key = Key::new(hive, &subkey).expect("Failed to create key");

// Set values
key.create_value("TestDWord", Data::DWord(42))
.expect("Failed to set DWord");
key.create_value("TestString", Data::String("hello".to_string()))
.expect("Failed to set String");

// Get values
let dword = key.get_u32("TestDWord").expect("Failed to get DWord");
assert_eq!(dword, 42);

let string = key.get_string("TestString").expect("Failed to get String");
assert_eq!(string, "hello");

key.remove_value("TestDWord")
.expect("Failed to remove DWord");
key.remove_value("TestString")
.expect("Failed to remove String");
}

#[test]
fn test_rename_value() {
let hive = Hive::CurrentUser;
let subkey = test_subkey();
let key = Key::new(hive, &subkey).expect("Failed to open key");

key.create_value("FromDWord", Data::DWord(42))
.expect("Failed to set DWord");

// Rename value
key.rename_value("FromDWord", "ToDWord")
.expect("Failed to rename value");
let renamed = key.get_u32("ToDWord").expect("Failed to get renamed value");
assert_eq!(renamed, 42);

// Delete value
key.remove_value("ToDWord").expect("Failed to delete value");
assert!(key.value("ToDWord").is_err());
}
}
1 change: 1 addition & 0 deletions src/registry/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod manager;