use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int};
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::ptr;
use crate::{ScanConfig, analyze_path};
const VERSION: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
#[unsafe(no_mangle)]
pub unsafe extern "C" fn audioscan_analyze_json(
path: *const c_char,
threshold_db: f64,
min_gap_sec: f64,
strict: c_int,
) -> *mut c_char {
catch_unwind(AssertUnwindSafe(|| {
analyze_json(path, threshold_db, min_gap_sec, strict).unwrap_or(ptr::null_mut())
}))
.unwrap_or(ptr::null_mut())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn audioscan_string_free(s: *mut c_char) {
let _ = catch_unwind(AssertUnwindSafe(|| {
if !s.is_null() {
drop(unsafe { CString::from_raw(s) });
}
}));
}
#[unsafe(no_mangle)]
pub extern "C" fn audioscan_version() -> *const c_char {
catch_unwind(|| VERSION.as_ptr().cast::<c_char>())
.unwrap_or_else(|_| VERSION.as_ptr().cast::<c_char>())
}
fn analyze_json(
path: *const c_char,
threshold_db: f64,
min_gap_sec: f64,
strict: c_int,
) -> Option<*mut c_char> {
if path.is_null() {
return None;
}
let path = unsafe { CStr::from_ptr(path) }.to_str().ok()?;
let config = ScanConfig {
threshold_db,
min_gap_sec,
strict: strict != 0,
max_decode_secs: None,
};
let analysis = analyze_path(path, &config).ok()?;
let json = serde_json::to_string(&analysis).ok()?;
let c_json = CString::new(json).ok()?;
Some(c_json.into_raw())
}