use std::fmt;
use std::str::FromStr;
use super::common::{ExecutionInfo, State};
use super::media::Media;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct Endpoint {
#[cfg_attr(feature = "serde", serde(rename = "@entity"))]
pub entity: String,
#[cfg_attr(
feature = "serde",
serde(rename = "@state", default, skip_serializing_if = "Option::is_none")
)]
pub state: Option<State>,
#[cfg_attr(
feature = "serde",
serde(
rename = "display-text",
default,
skip_serializing_if = "Option::is_none"
)
)]
pub display_text: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub referred: Option<ExecutionInfo>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub status: Option<EndpointStatus>,
#[cfg_attr(
feature = "serde",
serde(
rename = "joining-method",
default,
skip_serializing_if = "Option::is_none"
)
)]
pub joining_method: Option<JoiningMethod>,
#[cfg_attr(
feature = "serde",
serde(
rename = "joining-info",
default,
skip_serializing_if = "Option::is_none"
)
)]
pub joining_info: Option<ExecutionInfo>,
#[cfg_attr(
feature = "serde",
serde(
rename = "disconnection-method",
default,
skip_serializing_if = "Option::is_none"
)
)]
pub disconnection_method: Option<DisconnectionMethod>,
#[cfg_attr(
feature = "serde",
serde(
rename = "disconnection-info",
default,
skip_serializing_if = "Option::is_none"
)
)]
pub disconnection_info: Option<ExecutionInfo>,
#[cfg_attr(
feature = "serde",
serde(rename = "media", default, skip_serializing_if = "Vec::is_empty")
)]
pub media: Vec<Media>,
#[cfg_attr(
feature = "serde",
serde(rename = "call-info", default, skip_serializing_if = "Option::is_none")
)]
pub call_info: Option<CallInfo>,
}
impl Endpoint {
pub fn new(entity: impl Into<String>) -> Self {
Self {
entity: entity.into(),
state: None,
display_text: None,
referred: None,
status: None,
joining_method: None,
joining_info: None,
disconnection_method: None,
disconnection_info: None,
media: Vec::new(),
call_info: None,
}
}
pub fn with_status(mut self, status: EndpointStatus) -> Self {
self.status = Some(status);
self
}
pub fn with_joining_method(mut self, method: JoiningMethod) -> Self {
self.joining_method = Some(method);
self
}
pub fn with_joining_info(mut self, info: ExecutionInfo) -> Self {
self.joining_info = Some(info);
self
}
pub fn with_media(mut self, media: Media) -> Self {
self.media
.push(media);
self
}
pub fn with_call_info(mut self, call_info: CallInfo) -> Self {
self.call_info = Some(call_info);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum EndpointStatus {
#[cfg_attr(feature = "serde", serde(rename = "pending"))]
Pending,
#[cfg_attr(feature = "serde", serde(rename = "dialing-out"))]
DialingOut,
#[cfg_attr(feature = "serde", serde(rename = "dialing-in"))]
DialingIn,
#[cfg_attr(feature = "serde", serde(rename = "alerting"))]
Alerting,
#[cfg_attr(feature = "serde", serde(rename = "on-hold"))]
OnHold,
#[cfg_attr(feature = "serde", serde(rename = "connected"))]
Connected,
#[cfg_attr(feature = "serde", serde(rename = "muted-via-focus"))]
MutedViaFocus,
#[cfg_attr(feature = "serde", serde(rename = "disconnecting"))]
Disconnecting,
#[cfg_attr(feature = "serde", serde(rename = "disconnected"))]
Disconnected,
}
impl fmt::Display for EndpointStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Pending => "pending",
Self::DialingOut => "dialing-out",
Self::DialingIn => "dialing-in",
Self::Alerting => "alerting",
Self::OnHold => "on-hold",
Self::Connected => "connected",
Self::MutedViaFocus => "muted-via-focus",
Self::Disconnecting => "disconnecting",
Self::Disconnected => "disconnected",
})
}
}
#[derive(Debug, Clone)]
pub struct ParseEndpointStatusError(pub String);
impl fmt::Display for ParseEndpointStatusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"invalid endpoint status ({} bytes)",
self.0
.len()
)
}
}
impl std::error::Error for ParseEndpointStatusError {}
impl FromStr for EndpointStatus {
type Err = ParseEndpointStatusError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"pending" => Ok(Self::Pending),
"dialing-out" => Ok(Self::DialingOut),
"dialing-in" => Ok(Self::DialingIn),
"alerting" => Ok(Self::Alerting),
"on-hold" => Ok(Self::OnHold),
"connected" => Ok(Self::Connected),
"muted-via-focus" => Ok(Self::MutedViaFocus),
"disconnecting" => Ok(Self::Disconnecting),
"disconnected" => Ok(Self::Disconnected),
other => Err(ParseEndpointStatusError(other.to_owned())),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum JoiningMethod {
#[cfg_attr(feature = "serde", serde(rename = "dialed-in"))]
DialedIn,
#[cfg_attr(feature = "serde", serde(rename = "dialed-out"))]
DialedOut,
#[cfg_attr(feature = "serde", serde(rename = "focus-owner"))]
FocusOwner,
}
impl fmt::Display for JoiningMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::DialedIn => "dialed-in",
Self::DialedOut => "dialed-out",
Self::FocusOwner => "focus-owner",
})
}
}
#[derive(Debug, Clone)]
pub struct ParseJoiningMethodError(pub String);
impl fmt::Display for ParseJoiningMethodError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"invalid joining method ({} bytes)",
self.0
.len()
)
}
}
impl std::error::Error for ParseJoiningMethodError {}
impl FromStr for JoiningMethod {
type Err = ParseJoiningMethodError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"dialed-in" => Ok(Self::DialedIn),
"dialed-out" => Ok(Self::DialedOut),
"focus-owner" => Ok(Self::FocusOwner),
other => Err(ParseJoiningMethodError(other.to_owned())),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum DisconnectionMethod {
#[cfg_attr(feature = "serde", serde(rename = "departed"))]
Departed,
#[cfg_attr(feature = "serde", serde(rename = "booted"))]
Booted,
#[cfg_attr(feature = "serde", serde(rename = "failed"))]
Failed,
#[cfg_attr(feature = "serde", serde(rename = "busy"))]
Busy,
}
impl fmt::Display for DisconnectionMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Departed => "departed",
Self::Booted => "booted",
Self::Failed => "failed",
Self::Busy => "busy",
})
}
}
#[derive(Debug, Clone)]
pub struct ParseDisconnectionMethodError(pub String);
impl fmt::Display for ParseDisconnectionMethodError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"invalid disconnection method ({} bytes)",
self.0
.len()
)
}
}
impl std::error::Error for ParseDisconnectionMethodError {}
impl FromStr for DisconnectionMethod {
type Err = ParseDisconnectionMethodError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"departed" => Ok(Self::Departed),
"booted" => Ok(Self::Booted),
"failed" => Ok(Self::Failed),
"busy" => Ok(Self::Busy),
other => Err(ParseDisconnectionMethodError(other.to_owned())),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct CallInfo {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub sip: Option<SipDialogId>,
}
impl CallInfo {
pub fn with_sip(sip: SipDialogId) -> Self {
Self { sip: Some(sip) }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct SipDialogId {
#[cfg_attr(
feature = "serde",
serde(
rename = "display-text",
default,
skip_serializing_if = "Option::is_none"
)
)]
pub display_text: Option<String>,
#[cfg_attr(
feature = "serde",
serde(rename = "call-id", default, skip_serializing_if = "Option::is_none")
)]
pub call_id: Option<String>,
#[cfg_attr(
feature = "serde",
serde(rename = "from-tag", default, skip_serializing_if = "Option::is_none")
)]
pub from_tag: Option<String>,
#[cfg_attr(
feature = "serde",
serde(rename = "to-tag", default, skip_serializing_if = "Option::is_none")
)]
pub to_tag: Option<String>,
}
impl SipDialogId {
pub fn new(
call_id: impl Into<String>,
from_tag: impl Into<String>,
to_tag: impl Into<String>,
) -> Self {
Self {
display_text: None,
call_id: Some(call_id.into()),
from_tag: Some(from_tag.into()),
to_tag: Some(to_tag.into()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "serde")]
use crate::conference_info::assert_serde_wire_names;
use crate::conference_info::assert_wire_names;
const ENDPOINT_STATUSES: &[EndpointStatus] = &[
EndpointStatus::Pending,
EndpointStatus::DialingOut,
EndpointStatus::DialingIn,
EndpointStatus::Alerting,
EndpointStatus::OnHold,
EndpointStatus::Connected,
EndpointStatus::MutedViaFocus,
EndpointStatus::Disconnecting,
EndpointStatus::Disconnected,
];
const _: fn(EndpointStatus) = |v| match v {
EndpointStatus::Pending
| EndpointStatus::DialingOut
| EndpointStatus::DialingIn
| EndpointStatus::Alerting
| EndpointStatus::OnHold
| EndpointStatus::Connected
| EndpointStatus::MutedViaFocus
| EndpointStatus::Disconnecting
| EndpointStatus::Disconnected => {}
};
const JOINING_METHODS: &[JoiningMethod] = &[
JoiningMethod::DialedIn,
JoiningMethod::DialedOut,
JoiningMethod::FocusOwner,
];
const _: fn(JoiningMethod) = |v| match v {
JoiningMethod::DialedIn | JoiningMethod::DialedOut | JoiningMethod::FocusOwner => {}
};
const DISCONNECTION_METHODS: &[DisconnectionMethod] = &[
DisconnectionMethod::Departed,
DisconnectionMethod::Booted,
DisconnectionMethod::Failed,
DisconnectionMethod::Busy,
];
const _: fn(DisconnectionMethod) = |v| match v {
DisconnectionMethod::Departed
| DisconnectionMethod::Booted
| DisconnectionMethod::Failed
| DisconnectionMethod::Busy => {}
};
#[test]
fn endpoint_status_wire_names() {
assert_wire_names(ENDPOINT_STATUSES);
#[cfg(feature = "serde")]
assert_serde_wire_names(ENDPOINT_STATUSES);
}
#[test]
fn joining_method_wire_names() {
assert_wire_names(JOINING_METHODS);
#[cfg(feature = "serde")]
assert_serde_wire_names(JOINING_METHODS);
}
#[test]
fn disconnection_method_wire_names() {
assert_wire_names(DISCONNECTION_METHODS);
#[cfg(feature = "serde")]
assert_serde_wire_names(DISCONNECTION_METHODS);
}
#[test]
fn enum_error_display_omits_input() {
let status = "secret"
.parse::<EndpointStatus>()
.unwrap_err();
let joining = "secret"
.parse::<JoiningMethod>()
.unwrap_err();
let disconnection = "secret"
.parse::<DisconnectionMethod>()
.unwrap_err();
assert_eq!(
(&*status.0, &*joining.0, &*disconnection.0),
("secret", "secret", "secret")
);
assert_eq!(status.to_string(), "invalid endpoint status (6 bytes)");
assert_eq!(joining.to_string(), "invalid joining method (6 bytes)");
assert_eq!(
disconnection.to_string(),
"invalid disconnection method (6 bytes)"
);
}
}