use rsip::{Header, Uri};
pub fn refer_to_header(target: &Uri) -> Header {
Header::Other("Refer-To".into(), format!("<{target}>"))
}
pub fn parse_sipfrag_status(body: &[u8]) -> Option<u16> {
let text = std::str::from_utf8(body).ok()?;
let first_line = text.lines().next()?.trim();
let mut parts = first_line.split_whitespace();
let version = parts.next()?;
if !version.starts_with("SIP/") {
return None;
}
parts.next()?.parse::<u16>().ok()
}
pub fn is_final_sipfrag(code: u16) -> bool {
code >= 200
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn refer_to_wraps_the_target_uri() {
let uri = Uri::try_from("sip:carol@example.com").unwrap();
let Header::Other(name, value) = refer_to_header(&uri) else {
panic!("expected Header::Other");
};
assert_eq!(name, "Refer-To");
assert_eq!(value, "<sip:carol@example.com>");
}
#[test]
fn parses_final_and_provisional_sipfrag() {
assert_eq!(parse_sipfrag_status(b"SIP/2.0 100 Trying"), Some(100));
assert_eq!(parse_sipfrag_status(b"SIP/2.0 200 OK"), Some(200));
assert_eq!(
parse_sipfrag_status(b"SIP/2.0 486 Busy Here\r\n"),
Some(486)
);
assert_eq!(
parse_sipfrag_status(b" SIP/2.0 180 Ringing \r\n"),
Some(180)
);
}
#[test]
fn rejects_non_status_bodies() {
assert_eq!(parse_sipfrag_status(b""), None);
assert_eq!(parse_sipfrag_status(b"INVITE sip:x@y SIP/2.0"), None);
assert_eq!(parse_sipfrag_status(b"garbage"), None);
}
#[test]
fn finality_threshold_is_200() {
assert!(!is_final_sipfrag(100));
assert!(!is_final_sipfrag(180));
assert!(is_final_sipfrag(200));
assert!(is_final_sipfrag(486));
}
}