use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_long};
use wxdragon_sys as ffi;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ConfigStyle(c_long);
impl ConfigStyle {
pub const fn empty() -> Self {
ConfigStyle(0)
}
pub const USE_LOCAL_FILE: ConfigStyle = ConfigStyle(ffi::wxd_ConfigStyle_WXD_CONFIG_USE_LOCAL_FILE as c_long);
pub const USE_GLOBAL_FILE: ConfigStyle = ConfigStyle(ffi::wxd_ConfigStyle_WXD_CONFIG_USE_GLOBAL_FILE as c_long);
pub const USE_RELATIVE_PATH: ConfigStyle = ConfigStyle(ffi::wxd_ConfigStyle_WXD_CONFIG_USE_RELATIVE_PATH as c_long);
pub const USE_NO_ESCAPE_CHARACTERS: ConfigStyle =
ConfigStyle(ffi::wxd_ConfigStyle_WXD_CONFIG_USE_NO_ESCAPE_CHARACTERS as c_long);
pub const USE_SUBDIR: ConfigStyle = ConfigStyle(ffi::wxd_ConfigStyle_WXD_CONFIG_USE_SUBDIR as c_long);
pub fn to_raw(self) -> c_long {
self.0
}
}
impl std::ops::BitOr for ConfigStyle {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
ConfigStyle(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for ConfigStyle {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConfigEntryType {
Unknown,
String,
Boolean,
Integer,
Float,
}
impl From<i32> for ConfigEntryType {
fn from(value: i32) -> Self {
let v = value as i64;
if v == ffi::wxd_ConfigEntryType_WXD_CONFIG_TYPE_STRING as i64 {
ConfigEntryType::String
} else if v == ffi::wxd_ConfigEntryType_WXD_CONFIG_TYPE_BOOLEAN as i64 {
ConfigEntryType::Boolean
} else if v == ffi::wxd_ConfigEntryType_WXD_CONFIG_TYPE_INTEGER as i64 {
ConfigEntryType::Integer
} else if v == ffi::wxd_ConfigEntryType_WXD_CONFIG_TYPE_FLOAT as i64 {
ConfigEntryType::Float
} else {
ConfigEntryType::Unknown
}
}
}
pub struct Config {
ptr: *mut ffi::wxd_ConfigBase_t,
owned: bool,
}
impl Config {
pub fn new(
app_name: &str,
vendor_name: Option<&str>,
local_filename: Option<&str>,
global_filename: Option<&str>,
style: ConfigStyle,
) -> Self {
let c_app = CString::new(app_name).unwrap_or_default();
let c_vendor = vendor_name.map(|s| CString::new(s).unwrap_or_default());
let c_local = local_filename.map(|s| CString::new(s).unwrap_or_default());
let c_global = global_filename.map(|s| CString::new(s).unwrap_or_default());
let ptr = unsafe {
ffi::wxd_Config_Create(
c_app.as_ptr(),
c_vendor.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
c_local.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
c_global.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
style.to_raw(),
)
};
Self { ptr, owned: true }
}
pub fn get(create_on_demand: bool) -> Option<Self> {
let ptr = unsafe { ffi::wxd_Config_Get(create_on_demand) };
if ptr.is_null() {
None
} else {
Some(Self { ptr, owned: false })
}
}
pub fn set(config: Option<Config>) -> Option<Config> {
let ptr = config.map_or(std::ptr::null_mut(), |c| {
let p = c.ptr;
std::mem::forget(c); p
});
let prev = unsafe { ffi::wxd_Config_Set(ptr) };
if prev.is_null() {
None
} else {
Some(Self { ptr: prev, owned: true })
}
}
pub fn is_ok(&self) -> bool {
!self.ptr.is_null()
}
pub fn get_path(&self) -> String {
if self.ptr.is_null() {
return String::new();
}
let len = unsafe { ffi::wxd_Config_GetPath(self.ptr, std::ptr::null_mut(), 0) };
if len < 0 {
return String::new();
}
let mut buf: Vec<c_char> = vec![0; len as usize + 1];
unsafe { ffi::wxd_Config_GetPath(self.ptr, buf.as_mut_ptr(), buf.len()) };
unsafe { CStr::from_ptr(buf.as_ptr()).to_string_lossy().to_string() }
}
pub fn set_path(&self, path: &str) {
if self.ptr.is_null() {
return;
}
let c_path = match CString::new(path) {
Ok(s) => s,
Err(_) => return,
};
unsafe { ffi::wxd_Config_SetPath(self.ptr, c_path.as_ptr()) };
}
pub fn read_string(&self, key: &str, default: &str) -> String {
if self.ptr.is_null() {
return default.to_string();
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return default.to_string(),
};
let c_default = CString::new(default).unwrap_or_default();
let len = unsafe { ffi::wxd_Config_ReadString(self.ptr, c_key.as_ptr(), std::ptr::null_mut(), 0, c_default.as_ptr()) };
if len < 0 {
return default.to_string();
}
let mut buf: Vec<c_char> = vec![0; len as usize + 1];
unsafe { ffi::wxd_Config_ReadString(self.ptr, c_key.as_ptr(), buf.as_mut_ptr(), buf.len(), c_default.as_ptr()) };
unsafe { CStr::from_ptr(buf.as_ptr()).to_string_lossy().to_string() }
}
pub fn read_long(&self, key: &str, default: i64) -> i64 {
if self.ptr.is_null() {
return default;
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return default,
};
let mut value: c_long = default as c_long;
unsafe { ffi::wxd_Config_ReadLong(self.ptr, c_key.as_ptr(), &mut value, default as c_long) };
value as i64
}
pub fn read_double(&self, key: &str, default: f64) -> f64 {
if self.ptr.is_null() {
return default;
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return default,
};
let mut value: f64 = default;
unsafe { ffi::wxd_Config_ReadDouble(self.ptr, c_key.as_ptr(), &mut value, default) };
value
}
pub fn read_bool(&self, key: &str, default: bool) -> bool {
if self.ptr.is_null() {
return default;
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return default,
};
let mut value: bool = default;
unsafe { ffi::wxd_Config_ReadBool(self.ptr, c_key.as_ptr(), &mut value, default) };
value
}
pub fn write_string(&self, key: &str, value: &str) -> bool {
if self.ptr.is_null() {
return false;
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return false,
};
let c_value = match CString::new(value) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_WriteString(self.ptr, c_key.as_ptr(), c_value.as_ptr()) }
}
pub fn write_long(&self, key: &str, value: i64) -> bool {
if self.ptr.is_null() {
return false;
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_WriteLong(self.ptr, c_key.as_ptr(), value as c_long) }
}
pub fn write_double(&self, key: &str, value: f64) -> bool {
if self.ptr.is_null() {
return false;
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_WriteDouble(self.ptr, c_key.as_ptr(), value) }
}
pub fn write_bool(&self, key: &str, value: bool) -> bool {
if self.ptr.is_null() {
return false;
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_WriteBool(self.ptr, c_key.as_ptr(), value) }
}
pub fn exists(&self, name: &str) -> bool {
if self.ptr.is_null() {
return false;
}
let c_name = match CString::new(name) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_Exists(self.ptr, c_name.as_ptr()) }
}
pub fn has_entry(&self, name: &str) -> bool {
if self.ptr.is_null() {
return false;
}
let c_name = match CString::new(name) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_HasEntry(self.ptr, c_name.as_ptr()) }
}
pub fn has_group(&self, name: &str) -> bool {
if self.ptr.is_null() {
return false;
}
let c_name = match CString::new(name) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_HasGroup(self.ptr, c_name.as_ptr()) }
}
pub fn get_entry_type(&self, name: &str) -> ConfigEntryType {
if self.ptr.is_null() {
return ConfigEntryType::Unknown;
}
let c_name = match CString::new(name) {
Ok(s) => s,
Err(_) => return ConfigEntryType::Unknown,
};
let entry_type = unsafe { ffi::wxd_Config_GetEntryType(self.ptr, c_name.as_ptr()) };
ConfigEntryType::from(entry_type)
}
pub fn delete_entry(&self, key: &str, delete_group_if_empty: bool) -> bool {
if self.ptr.is_null() {
return false;
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_DeleteEntry(self.ptr, c_key.as_ptr(), delete_group_if_empty) }
}
pub fn delete_group(&self, key: &str) -> bool {
if self.ptr.is_null() {
return false;
}
let c_key = match CString::new(key) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_DeleteGroup(self.ptr, c_key.as_ptr()) }
}
pub fn delete_all(&self) -> bool {
if self.ptr.is_null() {
return false;
}
unsafe { ffi::wxd_Config_DeleteAll(self.ptr) }
}
pub fn get_entries(&self) -> Vec<String> {
if self.ptr.is_null() {
return Vec::new();
}
let mut entries = Vec::new();
let mut index: c_long = 0;
let mut buf: Vec<c_char> = vec![0; 256];
let mut has_more = unsafe { ffi::wxd_Config_GetFirstEntry(self.ptr, buf.as_mut_ptr(), buf.len(), &mut index) };
while has_more {
let name = unsafe { CStr::from_ptr(buf.as_ptr()).to_string_lossy().to_string() };
entries.push(name);
buf.fill(0);
has_more = unsafe { ffi::wxd_Config_GetNextEntry(self.ptr, buf.as_mut_ptr(), buf.len(), &mut index) };
}
entries
}
pub fn get_groups(&self) -> Vec<String> {
if self.ptr.is_null() {
return Vec::new();
}
let mut groups = Vec::new();
let mut index: c_long = 0;
let mut buf: Vec<c_char> = vec![0; 256];
let mut has_more = unsafe { ffi::wxd_Config_GetFirstGroup(self.ptr, buf.as_mut_ptr(), buf.len(), &mut index) };
while has_more {
let name = unsafe { CStr::from_ptr(buf.as_ptr()).to_string_lossy().to_string() };
groups.push(name);
buf.fill(0);
has_more = unsafe { ffi::wxd_Config_GetNextGroup(self.ptr, buf.as_mut_ptr(), buf.len(), &mut index) };
}
groups
}
pub fn get_number_of_entries(&self, recursive: bool) -> usize {
if self.ptr.is_null() {
return 0;
}
unsafe { ffi::wxd_Config_GetNumberOfEntries(self.ptr, recursive) }
}
pub fn get_number_of_groups(&self, recursive: bool) -> usize {
if self.ptr.is_null() {
return 0;
}
unsafe { ffi::wxd_Config_GetNumberOfGroups(self.ptr, recursive) }
}
pub fn rename_entry(&self, old_name: &str, new_name: &str) -> bool {
if self.ptr.is_null() {
return false;
}
let c_old = match CString::new(old_name) {
Ok(s) => s,
Err(_) => return false,
};
let c_new = match CString::new(new_name) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_RenameEntry(self.ptr, c_old.as_ptr(), c_new.as_ptr()) }
}
pub fn rename_group(&self, old_name: &str, new_name: &str) -> bool {
if self.ptr.is_null() {
return false;
}
let c_old = match CString::new(old_name) {
Ok(s) => s,
Err(_) => return false,
};
let c_new = match CString::new(new_name) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::wxd_Config_RenameGroup(self.ptr, c_old.as_ptr(), c_new.as_ptr()) }
}
pub fn flush(&self, current_only: bool) -> bool {
if self.ptr.is_null() {
return false;
}
unsafe { ffi::wxd_Config_Flush(self.ptr, current_only) }
}
pub fn get_app_name(&self) -> String {
if self.ptr.is_null() {
return String::new();
}
let len = unsafe { ffi::wxd_Config_GetAppName(self.ptr, std::ptr::null_mut(), 0) };
if len < 0 {
return String::new();
}
let mut buf: Vec<c_char> = vec![0; len as usize + 1];
unsafe { ffi::wxd_Config_GetAppName(self.ptr, buf.as_mut_ptr(), buf.len()) };
unsafe { CStr::from_ptr(buf.as_ptr()).to_string_lossy().to_string() }
}
pub fn get_vendor_name(&self) -> String {
if self.ptr.is_null() {
return String::new();
}
let len = unsafe { ffi::wxd_Config_GetVendorName(self.ptr, std::ptr::null_mut(), 0) };
if len < 0 {
return String::new();
}
let mut buf: Vec<c_char> = vec![0; len as usize + 1];
unsafe { ffi::wxd_Config_GetVendorName(self.ptr, buf.as_mut_ptr(), buf.len()) };
unsafe { CStr::from_ptr(buf.as_ptr()).to_string_lossy().to_string() }
}
pub fn is_expanding_env_vars(&self) -> bool {
if self.ptr.is_null() {
return false;
}
unsafe { ffi::wxd_Config_IsExpandingEnvVars(self.ptr) }
}
pub fn set_expand_env_vars(&self, expand: bool) {
if self.ptr.is_null() {
return;
}
unsafe { ffi::wxd_Config_SetExpandEnvVars(self.ptr, expand) };
}
pub fn is_recording_defaults(&self) -> bool {
if self.ptr.is_null() {
return false;
}
unsafe { ffi::wxd_Config_IsRecordingDefaults(self.ptr) }
}
pub fn set_record_defaults(&self, record: bool) {
if self.ptr.is_null() {
return;
}
unsafe { ffi::wxd_Config_SetRecordDefaults(self.ptr, record) };
}
}
impl Drop for Config {
fn drop(&mut self) {
if self.owned && !self.ptr.is_null() {
unsafe { ffi::wxd_Config_Destroy(self.ptr) };
}
}
}
pub struct ConfigPathGuard<'a> {
config: &'a Config,
old_path: String,
}
impl<'a> ConfigPathGuard<'a> {
pub fn new(config: &'a Config, new_path: &str) -> Self {
let old_path = config.get_path();
config.set_path(new_path);
Self { config, old_path }
}
}
impl<'a> Drop for ConfigPathGuard<'a> {
fn drop(&mut self) {
self.config.set_path(&self.old_path);
}
}