use serde::{Deserialize, Serialize};
use super::{HangupCascade, MediaPathMode};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionPolicy {
pub hangup_cascade: HangupCascade,
pub media_path: MediaPathMode,
pub hangup_all_on_end: bool,
pub allow_transfer: bool,
pub allow_hold: bool,
pub allow_recording: bool,
pub timeout_secs: u32,
}
impl Default for SessionPolicy {
fn default() -> Self {
Self {
hangup_cascade: HangupCascade::All,
media_path: MediaPathMode::Anchored,
hangup_all_on_end: true,
allow_transfer: true,
allow_hold: true,
allow_recording: true,
timeout_secs: 0,
}
}
}
impl SessionPolicy {
pub fn inbound_sip() -> Self {
Self {
hangup_cascade: HangupCascade::All,
hangup_all_on_end: true,
..Default::default()
}
}
pub fn rwi_originate(auto_cascade: bool) -> Self {
Self {
hangup_cascade: if auto_cascade {
HangupCascade::All
} else {
HangupCascade::None
},
hangup_all_on_end: auto_cascade,
..Default::default()
}
}
pub fn consultation() -> Self {
Self {
hangup_cascade: HangupCascade::None,
hangup_all_on_end: false,
allow_transfer: false,
..Default::default()
}
}
pub fn supervisor() -> Self {
Self {
hangup_cascade: HangupCascade::None,
hangup_all_on_end: false,
allow_transfer: false,
allow_recording: true,
..Default::default()
}
}
pub fn conference() -> Self {
Self {
hangup_cascade: HangupCascade::None,
hangup_all_on_end: false,
allow_transfer: false,
allow_hold: false,
..Default::default()
}
}
pub fn extension_internal() -> Self {
Self {
hangup_cascade: HangupCascade::All,
hangup_all_on_end: true,
allow_transfer: true,
allow_hold: true,
..Default::default()
}
}
pub fn call_center_queue() -> Self {
Self {
hangup_cascade: HangupCascade::All,
hangup_all_on_end: true,
allow_transfer: true,
allow_hold: true,
allow_recording: true,
..Default::default()
}
}
pub fn with_media_path(mut self, path: MediaPathMode) -> Self {
self.media_path = path;
self
}
pub fn with_allow_transfer(mut self, allow: bool) -> Self {
self.allow_transfer = allow;
self
}
pub fn with_allow_recording(mut self, allow: bool) -> Self {
self.allow_recording = allow;
self
}
pub fn with_timeout(mut self, secs: u32) -> Self {
self.timeout_secs = secs;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[derive(Default)]
pub enum RingbackPolicy {
#[default]
PassThrough,
Block,
Replace { source: MediaSource },
EarlyMedia { source: MediaSource },
Conditional {
remote_timeout_ms: Option<u32>,
fallback: MediaSource,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum MediaSource {
File { path: String },
Url { url: String },
Tts { text: String, voice: Option<String> },
Silence,
Tone { frequency: u32, duration_ms: u32 },
}
impl MediaSource {
pub fn file(path: impl Into<String>) -> Self {
Self::File { path: path.into() }
}
pub fn url(url: impl Into<String>) -> Self {
Self::Url { url: url.into() }
}
pub fn tts(text: impl Into<String>) -> Self {
Self::Tts {
text: text.into(),
voice: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn session_policy_inbound_sip() {
let policy = SessionPolicy::inbound_sip();
assert_eq!(policy.hangup_cascade, HangupCascade::All);
assert!(policy.hangup_all_on_end);
}
#[test]
fn session_policy_consultation() {
let policy = SessionPolicy::consultation();
assert_eq!(policy.hangup_cascade, HangupCascade::None);
assert!(!policy.hangup_all_on_end);
assert!(!policy.allow_transfer);
}
#[test]
fn session_policy_rwi_originate() {
let auto_cascade = SessionPolicy::rwi_originate(true);
assert_eq!(auto_cascade.hangup_cascade, HangupCascade::All);
let no_cascade = SessionPolicy::rwi_originate(false);
assert_eq!(no_cascade.hangup_cascade, HangupCascade::None);
}
#[test]
fn ringback_policy_default() {
let policy = RingbackPolicy::default();
assert!(matches!(policy, RingbackPolicy::PassThrough));
}
#[test]
fn media_source_file() {
let source = MediaSource::file("/path/to/audio.wav");
assert!(matches!(source, MediaSource::File { .. }));
}
}