I am new to Rust and specially new to FFI programming, and I am porting a macOS specific C library called librazermacos to Rust.
While writing some public APIs I came up with my first two methods which, incidentally, are the ones I needed to write to check my mouse battery and build some kind of proactive monitoring of when it's about to run out. That was my initial goal when porting and writing this code.
The extend goal is to gradually improve the exposed functionality to eventually get parity with the current C library and maybe even write a desktop app using Tauri that leverages the library.
Here's the relevant methods:
pub fn battery(&self) -> u8 {
let c_str = unsafe {
let devices = getAllRazerDevices();
let slice = slice::from_raw_parts(devices.devices, devices.size as usize);
let device = slice
.iter()
.find(|d| d.internalDeviceId == self.internal_device_id)
.unwrap();
let mut buf = [0i8; 4];
razer_attr_read_get_battery(device.usbDevice, buf.as_mut_ptr());
closeAllRazerDevices(devices);
CStr::from_ptr(buf.as_ptr())
};
let str = format!("{}", c_str.to_str().unwrap()).trim().to_string();
let current: u8 = str.parse().unwrap();
return ((current as f32 / 255.0) * 100.0) as u8;
}
pub fn is_charging(&self) -> bool {
let c_str = unsafe {
let devices = getAllRazerDevices();
let slice = slice::from_raw_parts(devices.devices, devices.size as usize);
let device = slice
.iter()
.find(|d| d.internalDeviceId == self.internal_device_id)
.unwrap();
let mut buf = [0i8; 4];
razer_attr_read_is_charging(device.usbDevice, buf.as_mut_ptr());
closeAllRazerDevices(devices);
CStr::from_ptr(buf.as_ptr())
};
format!("{}", c_str.to_str().unwrap()).starts_with("1")
}
The full code can be seen on GitHub project fcoury/razermacos-rs and the file in question is lib.rs
.
This code works perfectly as is, but it's far from ideal.
My feedback/review goals here are:
- Some guidance on how to evaluate and hopefully improve the safety of the
unsafe
blocks; - Figure out a way to reduce duplications on the two methods above, where I first get all devices, use some pointers to get information, and finally close the devices. From my experience with other languages I was tempted to extract both
razer_attr_read_get_battery
andrazer_attr_read_is_charging
into closures that would then be wrapped on a extracted function that opens, calls the closure and closes the devices. I was looking into Fn/FnOnce/etc. but not really sure which direction would be more idiomatic.