use std::ffi::c_char;
use std::ptr;
use audio_cpp_sys::*;
use crate::error::Error;
use crate::ffi;
use crate::model::Model;
use crate::types::{Device, LoaderInfo, ModelFamily, ModelInspection};
pub struct Registry {
raw: *mut audiocpp_registry,
}
unsafe impl Send for Registry {}
impl Registry {
pub fn new() -> Result<Self, Error> {
let raw = unsafe { audiocpp_registry_default() };
if raw.is_null() {
return Err(Error::NullHandle(ffi::last_error()));
}
Ok(Self { raw })
}
pub fn families(&self) -> Result<Vec<String>, Error> {
let mut out: *mut c_char = ptr::null_mut();
ffi::check_rc(unsafe { audiocpp_registry_families_json(self.raw, &mut out) })?;
let json = unsafe { ffi::take_string(out)? };
serde_json::from_str(&json).map_err(Error::from)
}
pub fn loaders(&self) -> Result<Vec<LoaderInfo>, Error> {
let mut out: *mut c_char = ptr::null_mut();
ffi::check_rc(unsafe { audiocpp_registry_loaders_json(self.raw, &mut out) })?;
let json = unsafe { ffi::take_string(out)? };
let root: serde_json::Value = serde_json::from_str(&json)?;
let loaders = root.get("loaders").ok_or_else(|| {
Error::Other(format!(
"注册表 loaders JSON 缺少 `loaders` 顶层字段: {json}"
))
})?;
serde_json::from_value(loaders.clone()).map_err(Error::from)
}
pub fn devices() -> Result<Vec<Device>, Error> {
let mut out: *mut c_char = ptr::null_mut();
ffi::check_rc(unsafe { audiocpp_registry_devices_json(&mut out) })?;
let json = unsafe { ffi::take_string(out)? };
serde_json::from_str(&json).map_err(Error::from)
}
pub fn supports_family(&self, family: &str) -> bool {
self.try_supports_family(family).unwrap_or(false)
}
pub fn try_supports_family(&self, family: &str) -> Result<bool, Error> {
let family_c = ffi::cstring(family)?;
let rc = unsafe {
audiocpp_registry_supports_family(self.raw, family_c.as_ptr() as *const c_char)
};
if rc < 0 {
return Err(Error::Ffi(ffi::last_error()));
}
Ok(rc != 0)
}
pub fn inspect(&self, model_path: &str) -> Result<ModelInspection, Error> {
let path_c = ffi::cstring(model_path)?;
let mut out: *mut c_char = ptr::null_mut();
ffi::check_rc(unsafe {
audiocpp_registry_inspect_json(self.raw, path_c.as_ptr() as *const c_char, &mut out)
})?;
let json = unsafe { ffi::take_string(out)? };
serde_json::from_str(&json).map_err(Error::from)
}
pub fn load(
&self,
model_path: &str,
family_hint: Option<ModelFamily>,
load_options: Option<&str>,
) -> Result<Model, Error> {
let path_c = ffi::cstring(model_path)?;
let hint_c = family_hint
.as_ref()
.map(|f| ffi::cstring(f.as_str()))
.transpose()?;
let options_c = load_options.map(ffi::cstring).transpose()?;
let raw = unsafe {
audiocpp_registry_load(
self.raw,
path_c.as_ptr() as *const c_char,
hint_c
.as_ref()
.map_or(ptr::null(), |s| s.as_ptr() as *const c_char),
options_c
.as_ref()
.map_or(ptr::null(), |s| s.as_ptr() as *const c_char),
)
};
if raw.is_null() {
return Err(Error::NullHandle(ffi::last_error()));
}
Ok(Model::from_raw(raw))
}
}
impl Drop for Registry {
fn drop(&mut self) {
unsafe {
audiocpp_registry_free(self.raw);
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
#[test]
fn supports_family_known_and_unknown() {
let reg = Registry::new().unwrap();
assert!(reg.supports_family("silero_vad"));
assert!(!reg.supports_family("definitely_not_a_real_family_xyz"));
}
}