1use crate::header_addr::{ParseSipHeaderAddrError, SipHeaderAddr};
8use std::fmt;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12#[non_exhaustive]
13pub enum ContactValue {
14 Wildcard,
16 Addr(Box<SipHeaderAddr>),
18}
19
20impl fmt::Display for ContactValue {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match self {
23 Self::Wildcard => f.write_str("*"),
24 Self::Addr(addr) => write!(f, "{addr}"),
25 }
26 }
27}
28
29fn parse_contact_entry(raw: &str) -> Result<ContactValue, ParseSipHeaderAddrError> {
31 let trimmed = raw.trim();
32 if trimmed == "*" {
33 Ok(ContactValue::Wildcard)
34 } else {
35 trimmed
36 .parse::<SipHeaderAddr>()
37 .map(|a| ContactValue::Addr(Box::new(a)))
38 }
39}
40
41pub fn parse_contact_list(raw: &str) -> Result<Vec<ContactValue>, ParseSipHeaderAddrError> {
43 parse_contact_entries(crate::split_comma_entries(raw.trim()))
44}
45
46pub fn parse_contact_entries<'a>(
50 entries: impl IntoIterator<Item = &'a str>,
51) -> Result<Vec<ContactValue>, ParseSipHeaderAddrError> {
52 let values: Vec<_> = entries
53 .into_iter()
54 .map(parse_contact_entry)
55 .collect::<Result<_, _>>()?;
56 if values.len() > 1
57 && values
58 .iter()
59 .any(|v| matches!(v, ContactValue::Wildcard))
60 {
61 return Err(ParseSipHeaderAddrError(
62 "Contact wildcard must be the only value".to_string(),
63 ));
64 }
65 Ok(values)
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn wildcard() {
74 let contacts = parse_contact_list("*").unwrap();
75 assert_eq!(contacts.len(), 1);
76 assert!(matches!(contacts[0], ContactValue::Wildcard));
77 }
78
79 #[test]
80 fn single_addr() {
81 let contacts = parse_contact_list("<sip:alice@198.51.100.1>").unwrap();
82 assert_eq!(contacts.len(), 1);
83 match &contacts[0] {
84 ContactValue::Addr(addr) => {
85 assert!(addr
86 .uri()
87 .to_string()
88 .contains("alice"));
89 }
90 _ => panic!("expected Addr"),
91 }
92 }
93
94 #[test]
95 fn multiple_addrs() {
96 let contacts =
97 parse_contact_list("<sip:alice@198.51.100.1>, \"Bob\" <sip:bob@198.51.100.2>").unwrap();
98 assert_eq!(contacts.len(), 2);
99 match &contacts[1] {
100 ContactValue::Addr(addr) => {
101 assert_eq!(addr.display_name(), Some("Bob"));
102 }
103 _ => panic!("expected Addr"),
104 }
105 }
106
107 #[test]
108 fn display_wildcard() {
109 assert_eq!(ContactValue::Wildcard.to_string(), "*");
110 }
111
112 #[test]
113 fn display_addr() {
114 let addr = "sip:alice@198.51.100.1"
115 .parse::<sip_uri::Uri>()
116 .unwrap();
117 let cv = ContactValue::Addr(Box::new(SipHeaderAddr::new(addr)));
118 assert!(cv
119 .to_string()
120 .contains("alice"));
121 }
122
123 #[test]
124 fn entries_matches_list() {
125 let a = "<sip:alice@198.51.100.1>";
126 let b = "\"Bob\" <sip:bob@example.com>;expires=60";
127 let split = parse_contact_entries([a, b]).unwrap();
128 let joined = parse_contact_list(&format!("{a}, {b}")).unwrap();
129 assert_eq!(split, joined);
130 assert_eq!(split.len(), 2);
131 }
132
133 #[test]
134 fn entries_empty_is_empty_list() {
135 let contacts = parse_contact_entries(std::iter::empty::<&str>()).unwrap();
136 assert!(contacts.is_empty());
137 }
138
139 #[test]
140 fn entries_lone_wildcard() {
141 let contacts = parse_contact_entries(["*"]).unwrap();
142 assert_eq!(contacts, vec![ContactValue::Wildcard]);
143 }
144
145 #[test]
146 fn wildcard_mixed_with_addr_is_error() {
147 assert!(parse_contact_entries(["*", "<sip:alice@example.com>"]).is_err());
148 assert!(parse_contact_entries(["<sip:alice@example.com>", "*"]).is_err());
149 assert!(parse_contact_list("*, <sip:alice@example.com>").is_err());
150 }
151}