extern crate spandsp_sys;
use std::ffi::CString;
use std::fmt;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr::NonNull;
use crate::error::{Result, SpanDspError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(i32)]
pub enum LogLevel {
None = 0,
Error = 1,
Warning = 2,
ProtocolError = 3,
ProtocolWarning = 4,
Flow = 5,
Flow2 = 6,
Flow3 = 7,
Debug = 8,
Debug2 = 9,
Debug3 = 10,
}
impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
LogLevel::None => "none",
LogLevel::Error => "error",
LogLevel::Warning => "warning",
LogLevel::ProtocolError => "protocol-error",
LogLevel::ProtocolWarning => "protocol-warning",
LogLevel::Flow => "flow",
LogLevel::Flow2 => "flow-2",
LogLevel::Flow3 => "flow-3",
LogLevel::Debug => "debug",
LogLevel::Debug2 => "debug-2",
LogLevel::Debug3 => "debug-3",
};
f.write_str(name)
}
}
impl From<LogLevel> for i32 {
fn from(level: LogLevel) -> Self {
level as i32
}
}
impl TryFrom<i32> for LogLevel {
type Error = SpanDspError;
fn try_from(value: i32) -> std::result::Result<Self, <Self as TryFrom<i32>>::Error> {
match value {
0 => Ok(LogLevel::None),
1 => Ok(LogLevel::Error),
2 => Ok(LogLevel::Warning),
3 => Ok(LogLevel::ProtocolError),
4 => Ok(LogLevel::ProtocolWarning),
5 => Ok(LogLevel::Flow),
6 => Ok(LogLevel::Flow2),
7 => Ok(LogLevel::Flow3),
8 => Ok(LogLevel::Debug),
9 => Ok(LogLevel::Debug2),
10 => Ok(LogLevel::Debug3),
_ => Err(SpanDspError::InvalidInput(format!(
"invalid log level: {value}"
))),
}
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LogShowFlags: i32 {
const DATE = 0x0100;
const SAMPLE_TIME = 0x0200;
const SEVERITY = 0x0400;
const PROTOCOL = 0x0800;
const VARIANT = 0x1000;
const TAG = 0x2000;
const SUPPRESS_LABELLING = 0x8000;
}
}
impl fmt::Display for LogShowFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
bitflags::parser::to_writer(self, f)
}
}
type LogHandler = Box<dyn FnMut(LogLevel, &str)>;
unsafe extern "C" fn message_handler_trampoline(
user_data: *mut c_void,
level: c_int,
text: *const c_char,
) {
unsafe {
if user_data.is_null() || text.is_null() {
return;
}
let closure = &mut *(user_data as *mut LogHandler);
let c_str = std::ffi::CStr::from_ptr(text);
if let Ok(s) = c_str.to_str() {
let log_level = LogLevel::try_from(level).unwrap_or(LogLevel::None);
closure(log_level, s);
}
}
}
pub struct LoggingState {
ptr: NonNull<spandsp_sys::logging_state_t>,
_handler: Option<Box<LogHandler>>,
}
impl LoggingState {
pub fn new(level: LogLevel, tag: &str) -> Result<Self> {
let c_tag = CString::new(tag)
.map_err(|_| SpanDspError::InvalidInput("tag contains NUL byte".into()))?;
let ptr = unsafe {
spandsp_sys::span_log_init(std::ptr::null_mut(), level as c_int, c_tag.as_ptr())
};
let ptr = NonNull::new(ptr).ok_or(SpanDspError::InitFailed)?;
Ok(Self {
ptr,
_handler: None,
})
}
pub unsafe fn from_ptr_borrowed(ptr: NonNull<spandsp_sys::logging_state_t>) -> Self {
Self {
ptr,
_handler: None,
}
}
pub fn as_ptr(&self) -> *mut spandsp_sys::logging_state_t {
self.ptr.as_ptr()
}
pub fn set_level(&mut self, level: LogLevel) {
unsafe {
spandsp_sys::span_log_set_level(self.ptr.as_ptr(), level as c_int);
}
}
pub fn set_level_with_flags(&mut self, level: LogLevel, flags: LogShowFlags) {
let combined = (level as i32) | flags.bits();
unsafe {
spandsp_sys::span_log_set_level(self.ptr.as_ptr(), combined as c_int);
}
}
pub fn set_tag(&mut self, tag: &str) -> Result<()> {
let c_tag = CString::new(tag)
.map_err(|_| SpanDspError::InvalidInput("tag contains NUL byte".into()))?;
unsafe {
spandsp_sys::span_log_set_tag(self.ptr.as_ptr(), c_tag.as_ptr());
}
Ok(())
}
pub fn set_protocol(&mut self, protocol: &str) -> Result<()> {
let c_proto = CString::new(protocol)
.map_err(|_| SpanDspError::InvalidInput("protocol contains NUL byte".into()))?;
unsafe {
spandsp_sys::span_log_set_protocol(self.ptr.as_ptr(), c_proto.as_ptr());
}
Ok(())
}
pub fn set_sample_rate(&mut self, samples_per_second: i32) {
unsafe {
spandsp_sys::span_log_set_sample_rate(self.ptr.as_ptr(), samples_per_second as c_int);
}
}
pub fn set_message_handler<F>(&mut self, handler: F)
where
F: FnMut(LogLevel, &str) + 'static,
{
let boxed: Box<LogHandler> = Box::new(Box::new(handler));
let user_data = &*boxed as *const LogHandler as *mut c_void;
unsafe {
spandsp_sys::span_log_set_message_handler(
self.ptr.as_ptr(),
Some(message_handler_trampoline),
user_data,
);
}
self._handler = Some(boxed);
}
}
impl Drop for LoggingState {
fn drop(&mut self) {
unsafe {
spandsp_sys::span_log_free(self.ptr.as_ptr());
}
}
}
pub unsafe fn set_global_message_handler(
handler: spandsp_sys::message_handler_func_t,
user_data: *mut c_void,
) {
unsafe {
spandsp_sys::span_set_message_handler(handler, user_data);
}
}