use crate::core::SipError;
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Method {
Invite,
Ack,
Bye,
Cancel,
Register,
Options,
Info,
Update,
Refer,
Notify,
Subscribe,
Prack,
Message,
Publish,
}
impl Method {
pub fn as_str(&self) -> &'static str {
match self {
Method::Invite => "INVITE",
Method::Ack => "ACK",
Method::Bye => "BYE",
Method::Cancel => "CANCEL",
Method::Register => "REGISTER",
Method::Options => "OPTIONS",
Method::Info => "INFO",
Method::Update => "UPDATE",
Method::Refer => "REFER",
Method::Notify => "NOTIFY",
Method::Subscribe => "SUBSCRIBE",
Method::Prack => "PRACK",
Method::Message => "MESSAGE",
Method::Publish => "PUBLISH",
}
}
pub fn creates_dialog(&self) -> bool {
matches!(self, Method::Invite | Method::Subscribe)
}
pub fn requires_ack(&self) -> bool {
matches!(self, Method::Invite)
}
}
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Method {
type Err = SipError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
const ALL: &[Method] = &[
Method::Invite,
Method::Ack,
Method::Bye,
Method::Cancel,
Method::Register,
Method::Options,
Method::Info,
Method::Update,
Method::Refer,
Method::Notify,
Method::Subscribe,
Method::Prack,
Method::Message,
Method::Publish,
];
for m in ALL {
if s.eq_ignore_ascii_case(m.as_str()) {
return Ok(*m);
}
}
Err(SipError::Parse(format!("unknown method: {s}")))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_method_from_str_canonical() {
assert_eq!("INVITE".parse::<Method>().unwrap(), Method::Invite);
assert_eq!("ACK".parse::<Method>().unwrap(), Method::Ack);
assert_eq!("BYE".parse::<Method>().unwrap(), Method::Bye);
assert_eq!("CANCEL".parse::<Method>().unwrap(), Method::Cancel);
assert_eq!("REGISTER".parse::<Method>().unwrap(), Method::Register);
assert_eq!("OPTIONS".parse::<Method>().unwrap(), Method::Options);
assert_eq!("INFO".parse::<Method>().unwrap(), Method::Info);
assert_eq!("UPDATE".parse::<Method>().unwrap(), Method::Update);
assert_eq!("REFER".parse::<Method>().unwrap(), Method::Refer);
assert_eq!("NOTIFY".parse::<Method>().unwrap(), Method::Notify);
assert_eq!("SUBSCRIBE".parse::<Method>().unwrap(), Method::Subscribe);
assert_eq!("PRACK".parse::<Method>().unwrap(), Method::Prack);
assert_eq!("MESSAGE".parse::<Method>().unwrap(), Method::Message);
assert_eq!("PUBLISH".parse::<Method>().unwrap(), Method::Publish);
}
#[test]
fn test_method_from_str_case_insensitive() {
assert_eq!("invite".parse::<Method>().unwrap(), Method::Invite);
assert_eq!("Invite".parse::<Method>().unwrap(), Method::Invite);
assert_eq!("iNvItE".parse::<Method>().unwrap(), Method::Invite);
assert_eq!("bye".parse::<Method>().unwrap(), Method::Bye);
assert_eq!("Publish".parse::<Method>().unwrap(), Method::Publish);
}
#[test]
fn test_method_from_str_unknown_rejects() {
assert!("BOGUS".parse::<Method>().is_err());
assert!("".parse::<Method>().is_err());
assert!("INVIT".parse::<Method>().is_err());
assert!("INVITES".parse::<Method>().is_err());
}
#[test]
fn test_method_publish_variant() {
assert_eq!("PUBLISH".parse::<Method>().unwrap(), Method::Publish);
assert_eq!(Method::Publish.to_string(), "PUBLISH");
assert_eq!(Method::Publish.as_str(), "PUBLISH");
}
#[test]
fn test_method_display_uppercase() {
assert_eq!(Method::Invite.to_string(), "INVITE");
assert_eq!(Method::Ack.to_string(), "ACK");
assert_eq!(Method::Subscribe.to_string(), "SUBSCRIBE");
}
#[test]
fn test_method_creates_dialog() {
assert!(Method::Invite.creates_dialog());
assert!(Method::Subscribe.creates_dialog());
assert!(!Method::Ack.creates_dialog());
assert!(!Method::Bye.creates_dialog());
assert!(!Method::Cancel.creates_dialog());
assert!(!Method::Register.creates_dialog());
assert!(!Method::Options.creates_dialog());
assert!(!Method::Info.creates_dialog());
assert!(!Method::Update.creates_dialog());
assert!(!Method::Refer.creates_dialog());
assert!(!Method::Notify.creates_dialog());
assert!(!Method::Prack.creates_dialog());
assert!(!Method::Message.creates_dialog());
assert!(!Method::Publish.creates_dialog());
}
#[test]
fn test_method_requires_ack() {
assert!(Method::Invite.requires_ack());
assert!(!Method::Ack.requires_ack());
assert!(!Method::Bye.requires_ack());
assert!(!Method::Cancel.requires_ack());
assert!(!Method::Register.requires_ack());
assert!(!Method::Options.requires_ack());
assert!(!Method::Info.requires_ack());
assert!(!Method::Update.requires_ack());
assert!(!Method::Refer.requires_ack());
assert!(!Method::Notify.requires_ack());
assert!(!Method::Subscribe.requires_ack());
assert!(!Method::Prack.requires_ack());
assert!(!Method::Message.requires_ack());
assert!(!Method::Publish.requires_ack());
}
}