freeswitch_rs/
channel.rs

1use freeswitch_sys::*;
2use std::ffi::c_void;
3use std::ffi::CStr;
4use std::ptr;
5
6use crate::prelude::*;
7
8pub use freeswitch_rs_macros::switch_state_handler;
9type StateHandlerTable = switch_state_handler_table_t;
10
11/// Default state handler table with all callbacks set to `None`.
12pub const DEFAULT_STATE_HANDLER_TABLE: StateHandlerTable = StateHandlerTable {
13    on_init: None,
14    on_execute: None,
15    on_hibernate: None,
16    on_destroy: None,
17    on_consume_media: None,
18    on_routing: None,
19    on_hangup: None,
20    on_exchange_media: None,
21    on_soft_execute: None,
22    on_reset: None,
23    on_park: None,
24    on_reporting: None,
25    padding: [ptr::null_mut(); 10],
26    flags: 0,
27};
28
29/// # Safety
30///
31/// Implementors should be aware that channels take no ownership of values
32/// and so it up to you to free if needed at suitable point in time.
33/// Additionally as Channels store void ptrs, its on calling code to cast correctly
34pub unsafe trait IntoChannelValue {
35    fn into_value(self) -> *const c_void;
36    fn from_value(ptr: *const c_void) -> Self;
37}
38
39// Blanket impl for all FS wrapper types so they store their wrapped pointer in a channel
40unsafe impl<T, U> IntoChannelValue for T
41where
42    T: FSNewType<Inner = *mut U>,
43{
44    fn from_value(ptr: *const c_void) -> T {
45        Self::from_ptr(ptr as *mut U)
46    }
47    fn into_value(self) -> *const c_void {
48        self.as_ptr() as *const c_void
49    }
50}
51
52fs_session_owned_type!(Channel, *mut switch_channel_t);
53type ChannelStateHandlerIndex = usize;
54
55impl<'a> Channel<'a> {
56    // We should treat values within a channel as 'shared'
57    // since the channels themselve make no claim over ownership.
58    // To this end, we require values to implement clone
59    // to avoid inadverntanly invalidating the ptr stored.
60    /// Set private data on channel. See: [`switch_channel_set_private`](../../freeswitch_sys/fn.switch_channel_set_private.html).
61    pub fn set_private<T>(&self, key: &CStr, data: T) -> Result<()>
62    where
63        T: IntoChannelValue + Clone,
64        T: 'a, // data must be equal or outlive channel/session
65    {
66        let data_ptr = data.into_value();
67
68        // SAFETY:
69        // FS will take a lock on channel insert / read so its safe to call
70        // with shared reference
71        unsafe {
72            match switch_channel_set_private(self.as_ptr(), key.as_ptr(), data_ptr) {
73                switch_status_t::SWITCH_STATUS_SUCCESS => Ok(()),
74                other => Err(other.into()),
75            }
76        }
77    }
78
79    /// Retrieve private data from a given channel for the given key. See: [`switch_channel_get_private`](../../freeswitch_sys/fn.switch_channel_get_private.html).
80    pub fn get_private<T>(&self, key: &CStr) -> Option<T>
81    where
82        T: IntoChannelValue + Clone,
83        T: 'a, // data must be equal or outlive channel/session
84    {
85        unsafe {
86            let ptr = self.get_private_raw_ptr(key)?;
87            let t = T::from_value(ptr);
88            let ret = Some(t.clone());
89            let _ = t.into_value();
90            ret
91        }
92    }
93
94    /// Remove private data from channel for the given key. See: [`switch_channel_set_private`](../../freeswitch_sys/fn.switch_channel_set_private.html).
95    pub fn remove_private<T: IntoChannelValue>(&self, key: &CStr) -> Result<()> {
96        unsafe {
97            match switch_channel_set_private(self.as_ptr(), key.as_ptr(), ptr::null()) {
98                switch_status_t::SWITCH_STATUS_SUCCESS => Ok(()),
99                other => Err(other.into()),
100            }
101        }
102    }
103
104    /// Set private data on channel. See: [`switch_channel_set_private`](../../freeswitch_sys/fn.switch_channel_set_private.html).
105    ///
106    /// # Safety
107    ///
108    /// Channels do not own or cleanup their data, so caller must ensure
109    /// rust allocated structs are cleaned up IF required ie anything on the heap
110    /// normally by using [`add_state_handler`]
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// #[switch_state_handler]
116    /// pub fn cleanup(s: &Session) -> switch_status_t {
117    ///   unsafe {
118    ///     let ptr = s.get_channel().map(|c| c.get_private_raw_ptr());
119    ///     // Cast and drop ...
120    ///   }
121    /// }
122    /// ```
123    ///
124    pub unsafe fn set_private_raw_ptr<T>(&self, key: &CStr, data: *const T) -> Result<()> {
125        // SAFETY:
126        // FS will take a lock on channel insert / read so its safe to call
127        // with shared reference
128        unsafe {
129            match switch_channel_set_private(self.as_ptr(), key.as_ptr(), data as *const c_void) {
130                switch_status_t::SWITCH_STATUS_SUCCESS => Ok(()),
131                other => Err(other.into()),
132            }
133        }
134    }
135
136    /// Retrieve private data from a given channel for the given key. See: [`switch_channel_get_private`](../../freeswitch_sys/fn.switch_channel_get_private.html).
137    ///
138    /// # Safety
139    ///
140    /// Care must be taken to not invalidate the stored pointer whilst the channel holds the value
141    /// ie ensure T is not dropped
142    pub unsafe fn get_private_raw_ptr<T>(&self, key: &CStr) -> Option<*mut T> {
143        // SAFETY:
144        // FS will take a lock on channel insert / read so its safe to call
145        // with shared reference
146        unsafe {
147            let ptr = switch_channel_get_private(self.as_ptr(), key.as_ptr());
148            if ptr.is_null() {
149                return None;
150            }
151            Some(ptr as *mut T)
152        }
153    }
154
155    /// Add a state handler table to a given channel. Returns the index number/priority of the table.
156    ///
157    /// See: [`switch_channel_add_state_handler`](../../freeswitch_sys/fn.switch_channel_add_state_handler.html).
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// #[switch_state_handler]
163    /// pub fn cleanup(s: &Session) -> switch_status_t {
164    ///    // cleanup ...
165    /// }
166    /// const STATE_HANDLERS: StateHandlerTable = StateHandlerTable {
167    ///     on_destroy: Some(cleanup),
168    ///     ..DEFAULT_STATE_HANDLER_TABLE
169    /// }
170    /// ```
171    pub fn add_state_handler(
172        &self,
173        table: &'static StateHandlerTable,
174    ) -> Result<ChannelStateHandlerIndex> {
175        unsafe {
176            match switch_channel_add_state_handler(self.as_ptr(), table) {
177                n if n < 0 => Err(switch_status_t::SWITCH_STATUS_GENERR.into()),
178                n => Ok(n.try_into().unwrap()),
179            }
180        }
181    }
182}