This makes it easier for tooling to find the Rust stuff. Rust/non-rust is not a useful distinction in this repo anymore anyway.
104 lines
3.1 KiB
Rust
104 lines
3.1 KiB
Rust
use anyhow::Result;
|
|
use nix_bindings_bindgen_raw as raw;
|
|
use std::sync::Mutex;
|
|
|
|
use crate::{
|
|
check_call, context, result_string_init,
|
|
string_return::{callback_get_result_string, callback_get_result_string_data},
|
|
};
|
|
|
|
// Global mutex to protect concurrent access to Nix settings
|
|
// See the documentation on `set()` for important thread safety information.
|
|
static SETTINGS_MUTEX: Mutex<()> = Mutex::new(());
|
|
|
|
/// Set a Nix setting.
|
|
///
|
|
/// # Thread Safety
|
|
///
|
|
/// This function uses a mutex to serialize access through the Rust API.
|
|
/// However, the underlying Nix settings system uses global mutable state
|
|
/// without internal synchronization.
|
|
///
|
|
/// The mutex provides protection between Rust callers but cannot prevent:
|
|
/// - C++ Nix code from modifying settings concurrently
|
|
/// - Other Nix operations from reading settings during modification
|
|
///
|
|
/// For multi-threaded applications, ensure that no other Nix operations
|
|
/// are running while changing settings. Settings are best modified during
|
|
/// single-threaded initialization.
|
|
pub fn set(key: &str, value: &str) -> Result<()> {
|
|
// Lock the mutex to ensure thread-safe access to global settings
|
|
let guard = SETTINGS_MUTEX.lock().unwrap();
|
|
|
|
let mut ctx = context::Context::new();
|
|
let key = std::ffi::CString::new(key)?;
|
|
let value = std::ffi::CString::new(value)?;
|
|
unsafe {
|
|
check_call!(raw::setting_set(&mut ctx, key.as_ptr(), value.as_ptr()))?;
|
|
}
|
|
drop(guard);
|
|
Ok(())
|
|
}
|
|
|
|
/// Get a Nix setting.
|
|
///
|
|
/// # Thread Safety
|
|
///
|
|
/// See the documentation on [`set()`] for important thread safety information.
|
|
pub fn get(key: &str) -> Result<String> {
|
|
// Lock the mutex to ensure thread-safe access to global settings
|
|
let guard = SETTINGS_MUTEX.lock().unwrap();
|
|
|
|
let mut ctx = context::Context::new();
|
|
let key = std::ffi::CString::new(key)?;
|
|
let mut r: Result<String> = result_string_init!();
|
|
unsafe {
|
|
check_call!(raw::setting_get(
|
|
&mut ctx,
|
|
key.as_ptr(),
|
|
Some(callback_get_result_string),
|
|
callback_get_result_string_data(&mut r)
|
|
))?;
|
|
}
|
|
drop(guard);
|
|
r
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::check_call;
|
|
|
|
use super::*;
|
|
|
|
#[ctor::ctor]
|
|
fn setup() {
|
|
let mut ctx = context::Context::new();
|
|
unsafe {
|
|
check_call!(raw::libstore_init(&mut ctx)).unwrap();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn set_get() {
|
|
// Something that shouldn't matter if it's a different value temporarily
|
|
let key = "user-agent-suffix";
|
|
|
|
// Save the old value, in case it's important. Probably not.
|
|
// If this doesn't work, pick a different setting to test with
|
|
let old_value = get(key).unwrap();
|
|
|
|
let new_value = "just a string that we're storing into some option for testing purposes";
|
|
|
|
let res_e = (|| {
|
|
set(key, new_value)?;
|
|
get(key)
|
|
})();
|
|
|
|
// Restore immediately; try not to affect other tests (if relevant).
|
|
set(key, old_value.as_str()).unwrap();
|
|
|
|
let res = res_e.unwrap();
|
|
|
|
assert_eq!(res, new_value);
|
|
}
|
|
}
|