use crate::callrecord::CallRecordHangupReason;
use serde::{Deserialize, Serialize};
use super::LegId;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum HangupCascade {
#[default]
All,
None,
AllExcept(Vec<LegId>),
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum HangupInitiator {
Remote {
leg_id: LegId,
sip_code: u16,
reason: Option<String>,
},
Local {
source: String,
command_id: Option<String>,
},
System {
reason: SystemHangupReason,
details: Option<String>,
},
Media {
leg_id: LegId,
error: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SystemHangupReason {
NoAnswer,
SessionExpired,
MaxDurationExceeded,
QueueTimeout,
NoAgentAvailable,
SystemShutdown,
InternalError,
ResourceLimit,
PolicyRejection,
}
impl std::fmt::Display for SystemHangupReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SystemHangupReason::NoAnswer => write!(f, "no_answer"),
SystemHangupReason::SessionExpired => write!(f, "session_expired"),
SystemHangupReason::MaxDurationExceeded => write!(f, "max_duration_exceeded"),
SystemHangupReason::QueueTimeout => write!(f, "queue_timeout"),
SystemHangupReason::NoAgentAvailable => write!(f, "no_agent_available"),
SystemHangupReason::SystemShutdown => write!(f, "system_shutdown"),
SystemHangupReason::InternalError => write!(f, "internal_error"),
SystemHangupReason::ResourceLimit => write!(f, "resource_limit"),
SystemHangupReason::PolicyRejection => write!(f, "policy_rejection"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HangupCommand {
pub leg_id: Option<LegId>,
pub cascade: HangupCascade,
pub initiator: HangupInitiator,
pub reason: Option<CallRecordHangupReason>,
pub code: Option<u16>,
}
impl HangupCommand {
pub fn all(reason: Option<CallRecordHangupReason>, code: Option<u16>) -> Self {
Self {
leg_id: None,
cascade: HangupCascade::All,
initiator: HangupInitiator::Local {
source: "unknown".to_string(),
command_id: None,
},
reason,
code,
}
}
pub fn local(
source: impl Into<String>,
reason: Option<CallRecordHangupReason>,
code: Option<u16>,
) -> Self {
Self {
leg_id: None,
cascade: HangupCascade::All,
initiator: HangupInitiator::Local {
source: source.into(),
command_id: None,
},
reason,
code,
}
}
pub fn remote(
leg_id: LegId,
sip_code: u16,
reason: Option<String>,
cdr_reason: Option<CallRecordHangupReason>,
) -> Self {
Self {
leg_id: Some(leg_id.clone()),
cascade: HangupCascade::default(),
initiator: HangupInitiator::Remote {
leg_id,
sip_code,
reason,
},
reason: cdr_reason,
code: Some(sip_code),
}
}
pub fn system(reason: SystemHangupReason, details: Option<String>) -> Self {
Self {
leg_id: None,
cascade: HangupCascade::All,
initiator: HangupInitiator::System { reason, details },
reason: None,
code: Some(500),
}
}
pub fn with_cascade(mut self, cascade: HangupCascade) -> Self {
self.cascade = cascade;
self
}
pub fn with_command_id(mut self, id: impl Into<String>) -> Self {
if let HangupInitiator::Local { source, .. } = &mut self.initiator {
self.initiator = HangupInitiator::Local {
source: source.clone(),
command_id: Some(id.into()),
};
}
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hangup_command_all() {
let cmd = HangupCommand::all(Some(CallRecordHangupReason::BySystem), Some(200));
assert_eq!(cmd.cascade, HangupCascade::All);
assert!(cmd.leg_id.is_none());
}
#[test]
fn hangup_command_remote() {
let cmd = HangupCommand::remote(
LegId::new("leg-1"),
486,
Some("Busy Here".to_string()),
Some(CallRecordHangupReason::Rejected),
);
assert!(cmd.leg_id.is_some());
assert_eq!(cmd.code, Some(486));
if let HangupInitiator::Remote { sip_code, .. } = cmd.initiator {
assert_eq!(sip_code, 486);
} else {
panic!("Expected Remote initiator");
}
}
#[test]
fn system_hangup_reason_display() {
assert_eq!(SystemHangupReason::NoAnswer.to_string(), "no_answer");
}
}