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}