use std::ffi::{CStr, CString};
use crate::{
error::{Exception, Result},
utils::SUCCESS,
};
struct StringHandle {
raw: mlx_sys::mlx_string,
}
impl StringHandle {
fn new() -> Self {
Self {
raw: unsafe { mlx_sys::mlx_string_new() },
}
}
fn to_string(&self) -> Result<String> {
let data = unsafe { mlx_sys::mlx_string_data(self.raw) };
if data.is_null() {
return Err(Exception::custom("MLX returned an empty string handle"));
}
unsafe { CStr::from_ptr(data) }
.to_str()
.map(str::to_owned)
.map_err(|error| Exception::custom(error.to_string()))
}
}
impl Drop for StringHandle {
fn drop(&mut self) {
let _ = unsafe { mlx_sys::mlx_string_free(self.raw) };
}
}
fn install_error_handler() {
crate::error::ensure_mlx_error_handler();
}
fn status_result(status: i32, operation: &str) -> Result<()> {
if status == SUCCESS {
Ok(())
} else {
Err(crate::error::exception_from_status(status, operation))
}
}
pub fn metallib_path() -> Result<String> {
install_error_handler();
let mut path = StringHandle::new();
let status = unsafe { mlx_sys::mlx_metal_get_metallib_path(&mut path.raw) };
status_result(status, "reading the Metal library path")?;
path.to_string()
}
pub fn set_metallib_path(path: impl AsRef<str>) -> Result<()> {
install_error_handler();
let path = CString::new(path.as_ref()).map_err(|error| Exception::custom(error.to_string()))?;
let status = unsafe { mlx_sys::mlx_metal_set_metallib_path(path.as_ptr()) };
status_result(status, "setting the Metal library path")
}
#[cfg(test)]
mod tests {
use std::sync::Mutex;
use super::{metallib_path, set_metallib_path};
static METALLIB_PATH: Mutex<()> = Mutex::new(());
struct RestorePath(String);
impl Drop for RestorePath {
fn drop(&mut self) {
set_metallib_path(&self.0).expect("restore metallib path");
}
}
#[test]
fn metallib_path_is_non_empty() {
let _guard = METALLIB_PATH.lock().expect("lock metallib path");
let original = metallib_path().unwrap();
let _restore = RestorePath(original);
set_metallib_path("mlx-rs-metallib-path-test").unwrap();
assert!(!metallib_path().unwrap().is_empty());
}
#[test]
fn metallib_path_roundtrips() {
let _guard = METALLIB_PATH.lock().expect("lock metallib path");
let original = metallib_path().unwrap();
let _restore = RestorePath(original.clone());
let temporary = format!("{original}.mlx-rs-roundtrip");
set_metallib_path(&temporary).unwrap();
assert_eq!(metallib_path().unwrap(), temporary);
}
}