use std::ffi::{c_char, CStr, CString};
use std::ptr;
use crate::inference::Engine;
pub struct PhosttEngine {
engine: Engine,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn phostt_engine_new(model_dir: *const c_char) -> *mut PhosttEngine {
if model_dir.is_null() {
tracing::error!("phostt_engine_new: model_dir is null");
eprintln!("phostt_engine_new: model_dir is null");
return ptr::null_mut();
}
let dir_str = match unsafe { CStr::from_ptr(model_dir) }.to_str() {
Ok(s) => s,
Err(e) => {
tracing::error!("phostt_engine_new: model_dir is not valid UTF-8: {e}");
eprintln!("phostt_engine_new: model_dir is not valid UTF-8: {e}");
return ptr::null_mut();
}
};
match Engine::load(dir_str) {
Ok(engine) => {
let handle = Box::new(PhosttEngine { engine });
Box::into_raw(handle)
}
Err(e) => {
tracing::error!("phostt_engine_new: failed to load engine: {e}");
eprintln!("phostt_engine_new: failed to load engine: {e}");
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn phostt_transcribe_file(
engine: *mut PhosttEngine,
wav_path: *const c_char,
) -> *mut c_char {
if engine.is_null() {
tracing::error!("phostt_transcribe_file: engine is null");
eprintln!("phostt_transcribe_file: engine is null");
return ptr::null_mut();
}
if wav_path.is_null() {
tracing::error!("phostt_transcribe_file: wav_path is null");
eprintln!("phostt_transcribe_file: wav_path is null");
return ptr::null_mut();
}
let path_str = match unsafe { CStr::from_ptr(wav_path) }.to_str() {
Ok(s) => s,
Err(e) => {
tracing::error!("phostt_transcribe_file: wav_path is not valid UTF-8: {e}");
eprintln!("phostt_transcribe_file: wav_path is not valid UTF-8: {e}");
return ptr::null_mut();
}
};
let engine_ref = unsafe { &(*engine).engine };
let mut guard = match engine_ref.pool.checkout_blocking() {
Ok(g) => g,
Err(e) => {
tracing::error!("phostt_transcribe_file: failed to checkout session from pool: {e}");
eprintln!("phostt_transcribe_file: failed to checkout session from pool: {e}");
return ptr::null_mut();
}
};
let result = match engine_ref.transcribe_file(path_str, &mut guard) {
Ok(r) => r,
Err(e) => {
tracing::error!("phostt_transcribe_file: transcription failed: {e}");
eprintln!("phostt_transcribe_file: transcription failed: {e}");
return ptr::null_mut();
}
};
match CString::new(result.text) {
Ok(cstr) => cstr.into_raw(),
Err(e) => {
tracing::error!("phostt_transcribe_file: result contains interior NUL: {e}");
eprintln!("phostt_transcribe_file: result contains interior NUL: {e}");
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn phostt_string_free(s: *mut c_char) {
if !s.is_null() {
let _ = unsafe { CString::from_raw(s) };
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn phostt_engine_free(engine: *mut PhosttEngine) {
if !engine.is_null() {
let _ = unsafe { Box::from_raw(engine) };
}
}