sip_header/lib.rs
1//! SIP header field parsers for standard RFC types.
2//!
3//! This crate provides parsers for SIP header values as defined in RFC 3261
4//! and extensions. It sits between URI parsing ([`sip_uri`]) and full SIP
5//! stacks, handling the header-level grammar: display names, header parameters,
6//! and structured header values like Call-Info and History-Info.
7//!
8//! # Modules
9//!
10//! - [`header_addr`] — RFC 3261 `name-addr` with header-level parameters
11//! - [`header`] — SIP header name catalog and [`SipHeaderLookup`] trait
12//! - [`message`] — Extract headers and Request-URI from raw SIP message text (feature: `message`)
13//! - [`call_info`] — RFC 3261 §20.9 Call-Info header parser
14//! - [`history_info`] — RFC 7044 History-Info header parser
15//! - [`geolocation`] — RFC 6442 Geolocation header parser
16//! - `conference_info` — RFC 4575 conference event package (feature: `conference-info`)
17
18#[macro_use]
19mod macros;
20
21pub use sip_uri;
22
23pub mod call_info;
24#[cfg(feature = "conference-info")]
25pub mod conference_info;
26pub mod geolocation;
27pub mod header;
28pub mod header_addr;
29pub mod history_info;
30#[cfg(feature = "message")]
31pub mod message;
32
33pub use call_info::{SipCallInfo, SipCallInfoEntry, SipCallInfoError};
34pub use geolocation::{SipGeolocation, SipGeolocationRef};
35pub use header::{ParseSipHeaderError, SipHeader, SipHeaderLookup};
36pub use header_addr::{ParseSipHeaderAddrError, SipHeaderAddr};
37pub use history_info::{HistoryInfo, HistoryInfoEntry, HistoryInfoError, HistoryInfoReason};
38#[cfg(feature = "message")]
39pub use message::{extract_header, extract_request_uri};
40
41/// Format a slice of displayable items as a separated list.
42pub(crate) fn fmt_joined<T: std::fmt::Display>(
43 f: &mut std::fmt::Formatter<'_>,
44 items: &[T],
45 separator: &str,
46) -> std::fmt::Result {
47 for (i, item) in items
48 .iter()
49 .enumerate()
50 {
51 if i > 0 {
52 f.write_str(separator)?;
53 }
54 write!(f, "{item}")?;
55 }
56 Ok(())
57}
58
59/// Split comma-separated header entries respecting angle-bracket nesting.
60///
61/// SIP headers that carry lists (RFC 3261 §7.3.1) use commas as delimiters,
62/// but commas may also appear inside angle-bracketed URIs. This function
63/// splits only on commas at bracket depth zero.
64pub fn split_comma_entries(raw: &str) -> Vec<&str> {
65 let mut entries = Vec::new();
66 let mut depth = 0u32;
67 let mut start = 0;
68
69 for (i, ch) in raw.char_indices() {
70 match ch {
71 '<' => depth += 1,
72 '>' => depth = depth.saturating_sub(1),
73 ',' if depth == 0 => {
74 entries.push(&raw[start..i]);
75 start = i + 1;
76 }
77 _ => {}
78 }
79 }
80 if start < raw.len() {
81 entries.push(&raw[start..]);
82 }
83
84 entries
85}