use std::ffi::{c_void, CString};
use crate::client::Result;
use super::check_status;
pub struct Configuration {
handle: *mut c_void,
}
unsafe impl Send for Configuration {}
impl Configuration {
pub fn new() -> Result<Self> {
let mut handle = std::ptr::null_mut();
let status = unsafe { super::FXNConfigurationCreate(&mut handle) };
check_status(status, "Failed to create configuration")?;
Ok(Self { handle })
}
pub fn tag(&self) -> Result<Option<String>> {
let mut buffer = vec![0u8; 2048];
let status = unsafe {
super::FXNConfigurationGetTag(
self.handle,
buffer.as_mut_ptr() as *mut _,
buffer.len() as i32,
)
};
check_status(status, "Failed to get configuration tag")?;
let tag = unsafe { std::ffi::CStr::from_ptr(buffer.as_ptr() as *const _) }
.to_string_lossy()
.into_owned();
Ok(if tag.is_empty() { None } else { Some(tag) })
}
pub fn set_tag(&mut self, tag: &str) -> Result<()> {
let tag = CString::new(tag).map_err(|e| crate::client::MunaError::Native(e.to_string()))?;
let status = unsafe { super::FXNConfigurationSetTag(self.handle, tag.as_ptr()) };
check_status(status, "Failed to set configuration tag")
}
pub fn token(&self) -> Result<Option<String>> {
let mut buffer = vec![0u8; 2048];
let status = unsafe {
super::FXNConfigurationGetToken(
self.handle,
buffer.as_mut_ptr() as *mut _,
buffer.len() as i32,
)
};
check_status(status, "Failed to get configuration token")?;
let token = unsafe { std::ffi::CStr::from_ptr(buffer.as_ptr() as *const _) }
.to_string_lossy()
.into_owned();
Ok(if token.is_empty() { None } else { Some(token) })
}
pub fn set_token(&mut self, token: &str) -> Result<()> {
let token =
CString::new(token).map_err(|e| crate::client::MunaError::Native(e.to_string()))?;
let status = unsafe { super::FXNConfigurationSetToken(self.handle, token.as_ptr()) };
check_status(status, "Failed to set configuration token")
}
pub fn set_acceleration(&mut self, acceleration: i32) -> Result<()> {
let status = unsafe { super::FXNConfigurationSetAcceleration(self.handle, acceleration) };
check_status(status, "Failed to set configuration acceleration")
}
pub fn devices(&self) -> Result<Vec<*mut c_void>> {
let mut count = 0i32;
let status = unsafe {
super::FXNConfigurationGetDevices(self.handle, std::ptr::null_mut(), &mut count)
};
check_status(status, "Failed to get configuration device count")?;
let mut devices = vec![std::ptr::null_mut(); count as usize];
if count == 0 {
return Ok(devices);
}
let status = unsafe {
super::FXNConfigurationGetDevices(self.handle, devices.as_mut_ptr(), &mut count)
};
check_status(status, "Failed to get configuration devices")?;
devices.truncate(count as usize);
Ok(devices)
}
pub fn set_devices(&mut self, devices: &[*const c_void]) -> Result<()> {
let count = i32::try_from(devices.len()).map_err(|_| {
crate::client::MunaError::Native(
"Cannot set more than i32::MAX configuration devices".to_string(),
)
})?;
let ptr = if devices.is_empty() {
std::ptr::null()
} else {
devices.as_ptr()
};
let status = unsafe { super::FXNConfigurationSetDevices(self.handle, ptr, count) };
check_status(status, "Failed to set configuration devices")
}
pub fn add_resource(&mut self, resource_type: &str, path: &str) -> Result<()> {
let rtype = CString::new(resource_type)
.map_err(|e| crate::client::MunaError::Native(e.to_string()))?;
let rpath =
CString::new(path).map_err(|e| crate::client::MunaError::Native(e.to_string()))?;
let status = unsafe {
super::FXNConfigurationAddResource(self.handle, rtype.as_ptr(), rpath.as_ptr())
};
check_status(status, "Failed to add configuration resource")
}
pub fn metadata(&self, key: &str) -> Result<Option<String>> {
let key = CString::new(key).map_err(|e| crate::client::MunaError::Native(e.to_string()))?;
let mut buffer = vec![0u8; 2048];
let status = unsafe {
super::FXNConfigurationGetMetadata(
self.handle,
key.as_ptr(),
buffer.as_mut_ptr() as *mut _,
buffer.len() as i32,
)
};
if status == super::FXNStatus::ErrorInvalidArgument as i32 {
return Ok(None);
}
check_status(status, "Failed to get configuration metadata")?;
let value = unsafe { std::ffi::CStr::from_ptr(buffer.as_ptr() as *const _) }
.to_string_lossy()
.into_owned();
Ok(Some(value))
}
pub fn set_metadata(&mut self, key: &str, value: &str) -> Result<()> {
let key = CString::new(key).map_err(|e| crate::client::MunaError::Native(e.to_string()))?;
let value =
CString::new(value).map_err(|e| crate::client::MunaError::Native(e.to_string()))?;
let status = unsafe {
super::FXNConfigurationSetMetadata(self.handle, key.as_ptr(), value.as_ptr())
};
check_status(status, "Failed to set configuration metadata")
}
pub fn remove_metadata(&mut self, key: &str) -> Result<()> {
let key = CString::new(key).map_err(|e| crate::client::MunaError::Native(e.to_string()))?;
let status = unsafe {
super::FXNConfigurationSetMetadata(self.handle, key.as_ptr(), std::ptr::null())
};
check_status(status, "Failed to remove configuration metadata")
}
pub fn get_unique_id() -> Result<String> {
let mut buffer = vec![0u8; 2048];
let status = unsafe {
super::FXNConfigurationGetUniqueID(buffer.as_mut_ptr() as *mut _, buffer.len() as i32)
};
check_status(status, "Failed to get unique ID")?;
let id = unsafe { std::ffi::CStr::from_ptr(buffer.as_ptr() as *const _) }
.to_string_lossy()
.into_owned();
Ok(id)
}
pub fn get_client_id() -> Result<String> {
let mut buffer = vec![0u8; 64];
let status = unsafe {
super::FXNConfigurationGetClientID(buffer.as_mut_ptr() as *mut _, buffer.len() as i32)
};
check_status(status, "Failed to get client ID")?;
let id = unsafe { std::ffi::CStr::from_ptr(buffer.as_ptr() as *const _) }
.to_string_lossy()
.into_owned();
Ok(id)
}
pub(crate) fn handle(&self) -> *mut c_void {
self.handle
}
}
impl Drop for Configuration {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe { super::FXNConfigurationRelease(self.handle) };
self.handle = std::ptr::null_mut();
}
}
}
#[cfg(test)]
mod tests {
use super::Configuration;
use std::ffi::c_void;
#[test]
fn metadata_roundtrip_and_remove() {
let mut configuration = Configuration::new().unwrap();
assert_eq!(configuration.metadata("owner").unwrap(), None);
configuration.set_metadata("owner", "muna").unwrap();
assert_eq!(
configuration.metadata("owner").unwrap().as_deref(),
Some("muna")
);
configuration.remove_metadata("owner").unwrap();
assert_eq!(configuration.metadata("owner").unwrap(), None);
}
#[test]
fn devices_roundtrip_and_clear() {
let mut configuration = Configuration::new().unwrap();
assert!(configuration.devices().unwrap().is_empty());
let first = 0u8;
let second = 0u8;
let devices = [
std::ptr::from_ref(&first).cast::<c_void>(),
std::ptr::from_ref(&second).cast::<c_void>(),
];
configuration.set_devices(&devices).unwrap();
assert_eq!(
configuration.devices().unwrap(),
devices.map(|device| device.cast_mut()).to_vec()
);
configuration.set_devices(&[]).unwrap();
assert!(configuration.devices().unwrap().is_empty());
}
}