freeswitch_rs/
modules.rs

1use std::ffi::CString;
2use std::io::ErrorKind;
3
4use crate::session::Session;
5use crate::types::*;
6use freeswitch_sys::switch_loadable_module_create_interface;
7
8#[repr(transparent)]
9pub struct StreamHandle(pub *mut switch_stream_handle_t);
10
11impl std::io::Write for StreamHandle {
12    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
13        unsafe {
14            if self.0.is_null() {
15                return std::io::Result::Err(std::io::Error::new(
16                    ErrorKind::Other,
17                    "No FSStream writer",
18                ));
19            }
20            if let Some(w) = (*self.0).write_function {
21                let cs = CString::new(buf)
22                    .map_err(|_| std::io::Error::new(ErrorKind::InvalidData, ""))?;
23                // The fs writer function success implies the full buffer is written
24                // we don't get any real info on error
25                let res = (w)(self.0, cs.as_ptr());
26                if res == switch_status_t::SWITCH_STATUS_SUCCESS {
27                    Ok(buf.len())
28                } else {
29                    std::io::Result::Err(std::io::Error::new(
30                        ErrorKind::Other,
31                        "FSStream writer Error",
32                    ))
33                }
34            } else {
35                std::io::Result::Err(std::io::Error::new(ErrorKind::Other, "No FSStream writer"))
36            }
37        }
38    }
39
40    fn flush(&mut self) -> std::io::Result<()> {
41        Ok(())
42    }
43}
44
45// ========
46
47// We will need a macro to transform trait into extern C functions ....
48// and call RUST function
49pub trait LoadableModule {
50    fn load(module: FSModuleInterface, pool: FSModulePool) -> switch_status_t;
51    fn shutdown() -> switch_status_t {
52        switch_status_t::SWITCH_STATUS_SUCCESS
53    }
54    fn runtime() -> switch_status_t {
55        switch_status_t::SWITCH_STATUS_TERM
56    }
57}
58
59// We have to make ptr public to allow macros to create them...
60#[repr(transparent)]
61pub struct FSModuleInterface(pub *mut *mut switch_loadable_module_interface_t);
62
63#[repr(transparent)]
64pub struct FSModulePool(pub *mut switch_memory_pool_t);
65
66impl FSModuleInterface {
67    // SAFETY: DONT CALL
68    pub unsafe fn create(
69        name: &str,
70        pool: *mut switch_memory_pool_t,
71    ) -> *mut switch_loadable_module_interface_t {
72        let mod_name = CString::new(name.to_owned()).unwrap().into_raw();
73        freeswitch_sys::switch_loadable_module_create_module_interface(pool, mod_name)
74    }
75
76    // Internally FS locks so safe to use &self
77    pub fn add_api<T: ApiInterface>(&self, _i: T) {
78        let t = switch_module_interface_name_t::SWITCH_API_INTERFACE;
79        // SAFETY: We assume the module ptr given to us is valid
80        // also we restrict access to the builder to ONLY the load function
81        unsafe {
82            let ptr =
83                switch_loadable_module_create_interface(*self.0, t) as *mut switch_api_interface_t;
84            let interface = &mut *ptr;
85            interface.interface_name = CString::new(T::NAME).unwrap().into_raw();
86            interface.desc = CString::new(T::DESC).unwrap().into_raw();
87            interface.function = Some(T::api_fn_raw);
88        }
89    }
90}
91
92pub trait ApiInterface {
93    const NAME: &'static str;
94    const DESC: &'static str;
95    fn api_fn(
96        cmd: &str,
97        session: Option<&Session>,
98        stream: StreamHandle,
99    ) -> freeswitch_sys::switch_status_t;
100    unsafe extern "C" fn api_fn_raw(
101        cmd: *const ::std::os::raw::c_char,
102        session: *mut freeswitch_sys::switch_core_session_t,
103        stream: *mut freeswitch_sys::switch_stream_handle_t,
104    ) -> freeswitch_sys::switch_status_t;
105}