Skip to main content

freeswitch_types/commands/
channel.rs

1//! Builders for `uuid_*` API commands that target a specific channel by UUID.
2
3use std::fmt;
4
5use crate::channel::HangupCause;
6use crate::commands::originate::DialplanType;
7
8/// Answer a channel: `uuid_answer <uuid>`.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub struct UuidAnswer {
12    /// Channel UUID.
13    pub uuid: String,
14}
15
16impl UuidAnswer {
17    /// Create a new uuid_answer command.
18    pub fn new(uuid: impl Into<String>) -> Self {
19        Self { uuid: uuid.into() }
20    }
21}
22
23impl fmt::Display for UuidAnswer {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        write!(f, "uuid_answer {}", self.uuid)
26    }
27}
28
29/// Bridge two channels: `uuid_bridge <uuid> <other_uuid>`.
30#[derive(Debug, Clone, PartialEq, Eq)]
31#[non_exhaustive]
32pub struct UuidBridge {
33    /// Channel UUID.
34    pub uuid: String,
35    /// UUID of the channel to bridge to.
36    pub other: String,
37}
38
39impl UuidBridge {
40    /// Create a new uuid_bridge command.
41    pub fn new(uuid: impl Into<String>, other: impl Into<String>) -> Self {
42        Self {
43            uuid: uuid.into(),
44            other: other.into(),
45        }
46    }
47}
48
49impl fmt::Display for UuidBridge {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(f, "uuid_bridge {} {}", self.uuid, self.other)
52    }
53}
54
55/// Deflect (redirect) a channel to a new SIP URI: `uuid_deflect <uuid> <uri>`.
56///
57/// Sends a SIP REFER to the endpoint.
58#[derive(Debug, Clone, PartialEq, Eq)]
59#[non_exhaustive]
60pub struct UuidDeflect {
61    /// Channel UUID.
62    pub uuid: String,
63    /// SIP URI to redirect to (e.g. `sip:user@host`).
64    pub uri: String,
65}
66
67impl UuidDeflect {
68    /// Create a new uuid_deflect command.
69    pub fn new(uuid: impl Into<String>, uri: impl Into<String>) -> Self {
70        Self {
71            uuid: uuid.into(),
72            uri: uri.into(),
73        }
74    }
75}
76
77impl fmt::Display for UuidDeflect {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(f, "uuid_deflect {} {}", self.uuid, self.uri)
80    }
81}
82
83/// Place a channel on hold or take it off hold: `uuid_hold [off] <uuid>`.
84#[derive(Debug, Clone, PartialEq, Eq)]
85#[non_exhaustive]
86pub struct UuidHold {
87    /// Channel UUID.
88    pub uuid: String,
89    /// `true` = take off hold; `false` = place on hold.
90    pub off: bool,
91}
92
93impl UuidHold {
94    /// Place a channel on hold.
95    pub fn hold(uuid: impl Into<String>) -> Self {
96        Self {
97            uuid: uuid.into(),
98            off: false,
99        }
100    }
101
102    /// Take a channel off hold.
103    pub fn unhold(uuid: impl Into<String>) -> Self {
104        Self {
105            uuid: uuid.into(),
106            off: true,
107        }
108    }
109}
110
111impl fmt::Display for UuidHold {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        if self.off {
114            write!(f, "uuid_hold off {}", self.uuid)
115        } else {
116            write!(f, "uuid_hold {}", self.uuid)
117        }
118    }
119}
120
121/// Kill a channel: `uuid_kill <uuid> [cause]`.
122#[derive(Debug, Clone, PartialEq, Eq)]
123#[non_exhaustive]
124pub struct UuidKill {
125    /// Channel UUID.
126    pub uuid: String,
127    /// Hangup cause. If `None`, uses FreeSWITCH default.
128    pub cause: Option<HangupCause>,
129}
130
131impl UuidKill {
132    /// Kill a channel with the default hangup cause.
133    pub fn new(uuid: impl Into<String>) -> Self {
134        Self {
135            uuid: uuid.into(),
136            cause: None,
137        }
138    }
139
140    /// Kill a channel with a specific hangup cause.
141    pub fn with_cause(uuid: impl Into<String>, cause: HangupCause) -> Self {
142        Self {
143            uuid: uuid.into(),
144            cause: Some(cause),
145        }
146    }
147}
148
149impl fmt::Display for UuidKill {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        write!(f, "uuid_kill {}", self.uuid)?;
152        if let Some(ref cause) = self.cause {
153            write!(f, " {}", cause)?;
154        }
155        Ok(())
156    }
157}
158
159/// Get a channel variable: `uuid_getvar <uuid> <key>`.
160///
161/// Note: FreeSWITCH returns the bare value (no `+OK` prefix), so
162/// [`EslResponse::reply_status()`] will be [`ReplyStatus::Other`].
163///
164/// [`EslResponse::reply_status()`]: https://docs.rs/freeswitch-esl-tokio/latest/freeswitch_esl_tokio/struct.EslResponse.html#method.reply_status
165/// [`ReplyStatus::Other`]: https://docs.rs/freeswitch-esl-tokio/latest/freeswitch_esl_tokio/enum.ReplyStatus.html#variant.Other
166#[derive(Debug, Clone, PartialEq, Eq)]
167#[non_exhaustive]
168pub struct UuidGetVar {
169    /// Channel UUID.
170    pub uuid: String,
171    /// Variable name to read.
172    pub key: String,
173}
174
175impl UuidGetVar {
176    /// Create a new uuid_getvar command.
177    pub fn new(uuid: impl Into<String>, key: impl Into<String>) -> Self {
178        Self {
179            uuid: uuid.into(),
180            key: key.into(),
181        }
182    }
183}
184
185impl fmt::Display for UuidGetVar {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        write!(f, "uuid_getvar {} {}", self.uuid, self.key)
188    }
189}
190
191/// Set a channel variable: `uuid_setvar <uuid> <key> <value>`.
192#[derive(Debug, Clone, PartialEq, Eq)]
193#[non_exhaustive]
194pub struct UuidSetVar {
195    /// Channel UUID.
196    pub uuid: String,
197    /// Variable name to set.
198    pub key: String,
199    /// Variable value.
200    pub value: String,
201}
202
203impl UuidSetVar {
204    /// Create a new uuid_setvar command.
205    pub fn new(uuid: impl Into<String>, key: impl Into<String>, value: impl Into<String>) -> Self {
206        Self {
207            uuid: uuid.into(),
208            key: key.into(),
209            value: value.into(),
210        }
211    }
212}
213
214impl fmt::Display for UuidSetVar {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        write!(f, "uuid_setvar {} {} {}", self.uuid, self.key, self.value)
217    }
218}
219
220/// Transfer a channel to a new destination: `uuid_transfer <uuid> <dest> [dialplan]`.
221#[derive(Debug, Clone, PartialEq, Eq)]
222#[non_exhaustive]
223pub struct UuidTransfer {
224    /// Channel UUID.
225    pub uuid: String,
226    /// Destination extension or dial string.
227    pub destination: String,
228    /// Dialplan to use. If `None`, uses the channel's current dialplan.
229    pub dialplan: Option<DialplanType>,
230}
231
232impl UuidTransfer {
233    /// Transfer a channel to a new destination.
234    pub fn new(uuid: impl Into<String>, destination: impl Into<String>) -> Self {
235        Self {
236            uuid: uuid.into(),
237            destination: destination.into(),
238            dialplan: None,
239        }
240    }
241
242    /// Set the dialplan to use for the transfer.
243    pub fn with_dialplan(mut self, dialplan: DialplanType) -> Self {
244        self.dialplan = Some(dialplan);
245        self
246    }
247}
248
249impl fmt::Display for UuidTransfer {
250    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251        write!(f, "uuid_transfer {} {}", self.uuid, self.destination)?;
252        if let Some(ref dp) = self.dialplan {
253            write!(f, " {}", dp)?;
254        }
255        Ok(())
256    }
257}
258
259/// Send DTMF digits to a channel: `uuid_send_dtmf <uuid> <digits>`.
260#[derive(Debug, Clone, PartialEq, Eq)]
261#[non_exhaustive]
262pub struct UuidSendDtmf {
263    /// Channel UUID.
264    pub uuid: String,
265    /// DTMF digit string (e.g. `"1234#"`).
266    pub dtmf: String,
267}
268
269impl UuidSendDtmf {
270    /// Create a new uuid_send_dtmf command.
271    pub fn new(uuid: impl Into<String>, dtmf: impl Into<String>) -> Self {
272        Self {
273            uuid: uuid.into(),
274            dtmf: dtmf.into(),
275        }
276    }
277}
278
279impl fmt::Display for UuidSendDtmf {
280    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281        write!(f, "uuid_send_dtmf {} {}", self.uuid, self.dtmf)
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    const UUID: &str = "abc12345-6789-0abc-def0-123456789abc";
290    const OTHER: &str = "def12345-6789-0abc-def0-123456789abc";
291
292    #[test]
293    fn uuid_answer() {
294        let cmd = UuidAnswer { uuid: UUID.into() };
295        assert_eq!(cmd.to_string(), format!("uuid_answer {}", UUID));
296    }
297
298    #[test]
299    fn uuid_bridge() {
300        let cmd = UuidBridge {
301            uuid: UUID.into(),
302            other: OTHER.into(),
303        };
304        assert_eq!(cmd.to_string(), format!("uuid_bridge {} {}", UUID, OTHER));
305    }
306
307    #[test]
308    fn uuid_deflect() {
309        let cmd = UuidDeflect {
310            uuid: UUID.into(),
311            uri: "sip:user@host".into(),
312        };
313        assert_eq!(
314            cmd.to_string(),
315            format!("uuid_deflect {} sip:user@host", UUID)
316        );
317    }
318
319    #[test]
320    fn uuid_hold_on() {
321        let cmd = UuidHold {
322            uuid: UUID.into(),
323            off: false,
324        };
325        assert_eq!(cmd.to_string(), format!("uuid_hold {}", UUID));
326    }
327
328    #[test]
329    fn uuid_hold_off() {
330        let cmd = UuidHold {
331            uuid: UUID.into(),
332            off: true,
333        };
334        assert_eq!(cmd.to_string(), format!("uuid_hold off {}", UUID));
335    }
336
337    #[test]
338    fn uuid_kill_no_cause() {
339        let cmd = UuidKill::new(UUID);
340        assert_eq!(cmd.to_string(), format!("uuid_kill {}", UUID));
341    }
342
343    #[test]
344    fn uuid_kill_with_cause() {
345        let cmd = UuidKill::with_cause(UUID, HangupCause::NormalClearing);
346        assert_eq!(
347            cmd.to_string(),
348            format!("uuid_kill {} NORMAL_CLEARING", UUID)
349        );
350    }
351
352    #[test]
353    fn uuid_getvar() {
354        let cmd = UuidGetVar {
355            uuid: UUID.into(),
356            key: "sip_call_id".into(),
357        };
358        assert_eq!(cmd.to_string(), format!("uuid_getvar {} sip_call_id", UUID));
359    }
360
361    #[test]
362    fn uuid_setvar() {
363        let cmd = UuidSetVar {
364            uuid: UUID.into(),
365            key: "hangup_after_bridge".into(),
366            value: "true".into(),
367        };
368        assert_eq!(
369            cmd.to_string(),
370            format!("uuid_setvar {} hangup_after_bridge true", UUID)
371        );
372    }
373
374    #[test]
375    fn uuid_transfer_no_dialplan() {
376        let cmd = UuidTransfer {
377            uuid: UUID.into(),
378            destination: "1000".into(),
379            dialplan: None,
380        };
381        assert_eq!(cmd.to_string(), format!("uuid_transfer {} 1000", UUID));
382    }
383
384    #[test]
385    fn uuid_transfer_with_dialplan() {
386        let cmd = UuidTransfer::new(UUID, "1000").with_dialplan(DialplanType::Xml);
387        assert_eq!(cmd.to_string(), format!("uuid_transfer {} 1000 XML", UUID));
388    }
389
390    #[test]
391    fn uuid_send_dtmf() {
392        let cmd = UuidSendDtmf {
393            uuid: UUID.into(),
394            dtmf: "1234#".into(),
395        };
396        assert_eq!(cmd.to_string(), format!("uuid_send_dtmf {} 1234#", UUID));
397    }
398}