#![allow(clippy::not_unsafe_ptr_arg_deref)]
pub use fswtch_sys as sys;
use std::{
error::Error,
ffi::{CStr, c_char},
fmt,
ptr::NonNull,
};
pub type Status = sys::switch_status_t;
pub const SUCCESS: Status = sys::switch_status_t::SWITCH_STATUS_SUCCESS;
pub const FALSE: Status = sys::switch_status_t::SWITCH_STATUS_FALSE;
pub const GENERR: Status = sys::switch_status_t::SWITCH_STATUS_GENERR;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SwitchError(pub Status);
impl fmt::Display for SwitchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FreeSWITCH returned status {:?}", self.0)
}
}
impl Error for SwitchError {}
pub type Result<T> = std::result::Result<T, SwitchError>;
pub fn status_to_result(status: Status) -> Result<()> {
if status == SUCCESS {
Ok(())
} else {
Err(SwitchError(status))
}
}
#[derive(Copy, Clone)]
pub struct Module {
raw: NonNull<sys::switch_loadable_module_interface_t>,
}
impl Module {
pub unsafe fn create(
slot: *mut *mut sys::switch_loadable_module_interface_t,
pool: *mut sys::switch_memory_pool_t,
name: &'static CStr,
) -> Result<Self> {
if slot.is_null() {
return Err(SwitchError(GENERR));
}
let raw =
unsafe { sys::switch_loadable_module_create_module_interface(pool, name.as_ptr()) };
let raw = NonNull::new(raw).ok_or(SwitchError(GENERR))?;
unsafe {
*slot = raw.as_ptr();
}
Ok(Self { raw })
}
pub fn as_ptr(&self) -> *mut sys::switch_loadable_module_interface_t {
self.raw.as_ptr()
}
pub unsafe fn add_api(
self,
name: &'static CStr,
description: &'static CStr,
syntax: &'static CStr,
function: unsafe extern "C" fn(
*const c_char,
*mut sys::switch_core_session_t,
*mut sys::switch_stream_handle_t,
) -> Status,
) -> Result<ApiInterface> {
let raw = unsafe {
sys::switch_loadable_module_create_interface(
self.raw.as_ptr(),
sys::switch_module_interface_name_t::SWITCH_API_INTERFACE,
)
};
let api =
NonNull::new(raw.cast::<sys::switch_api_interface_t>()).ok_or(SwitchError(GENERR))?;
unsafe {
let api_ref = api.as_ptr();
(*api_ref).interface_name = name.as_ptr();
(*api_ref).desc = description.as_ptr();
(*api_ref).function = Some(function);
(*api_ref).syntax = syntax.as_ptr();
}
Ok(ApiInterface { raw: api })
}
}
#[derive(Copy, Clone)]
pub struct ApiInterface {
raw: NonNull<sys::switch_api_interface_t>,
}
impl ApiInterface {
pub fn as_ptr(&self) -> *mut sys::switch_api_interface_t {
self.raw.as_ptr()
}
}
pub struct Stream {
raw: NonNull<sys::switch_stream_handle_t>,
}
impl Stream {
pub unsafe fn from_raw(raw: *mut sys::switch_stream_handle_t) -> Option<Self> {
NonNull::new(raw).map(|raw| Self { raw })
}
pub fn as_ptr(&self) -> *mut sys::switch_stream_handle_t {
self.raw.as_ptr()
}
pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
let raw = self.raw.as_ptr();
let Some(write) = (unsafe { &*raw }).raw_write_function else {
return Err(SwitchError(GENERR));
};
let status = unsafe { write(raw, bytes.as_ptr().cast_mut(), bytes.len()) };
status_to_result(status)
}
pub fn write_str(&mut self, text: &str) -> Result<()> {
self.write_bytes(text.as_bytes())
}
}
#[macro_export]
macro_rules! module_exports {
(
module = $module:ident,
load = $load:path $(,)?
) => {
$crate::module_exports! {
module = $module,
load = $load,
shutdown = None,
runtime = None,
}
};
(
module = $module:ident,
load = $load:path,
shutdown = $shutdown:expr,
runtime = $runtime:expr $(,)?
) => {
#[unsafe(export_name = concat!(stringify!($module), "_module_interface"))]
pub static mut SWITCH_RUST_MODULE_INTERFACE:
$crate::sys::switch_loadable_module_function_table_t =
$crate::sys::switch_loadable_module_function_table_t {
switch_api_version: $crate::sys::SWITCH_API_VERSION as _,
load: Some($load),
shutdown: $shutdown,
runtime: $runtime,
flags: 0,
};
};
}