Skip to main content

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 from raw SIP message text
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;
30pub mod message;
31
32pub use call_info::{SipCallInfo, SipCallInfoEntry, SipCallInfoError};
33pub use geolocation::{SipGeolocation, SipGeolocationRef};
34pub use header::{ParseSipHeaderError, SipHeader, SipHeaderLookup};
35pub use header_addr::{ParseSipHeaderAddrError, SipHeaderAddr};
36pub use history_info::{HistoryInfo, HistoryInfoEntry, HistoryInfoError, HistoryInfoReason};
37pub use message::extract_header;
38
39/// Format a slice of displayable items as a separated list.
40pub(crate) fn fmt_joined<T: std::fmt::Display>(
41    f: &mut std::fmt::Formatter<'_>,
42    items: &[T],
43    separator: &str,
44) -> std::fmt::Result {
45    for (i, item) in items
46        .iter()
47        .enumerate()
48    {
49        if i > 0 {
50            f.write_str(separator)?;
51        }
52        write!(f, "{item}")?;
53    }
54    Ok(())
55}
56
57/// Split comma-separated header entries respecting angle-bracket nesting.
58///
59/// SIP headers that carry lists (RFC 3261 §7.3.1) use commas as delimiters,
60/// but commas may also appear inside angle-bracketed URIs. This function
61/// splits only on commas at bracket depth zero.
62pub fn split_comma_entries(raw: &str) -> Vec<&str> {
63    let mut entries = Vec::new();
64    let mut depth = 0u32;
65    let mut start = 0;
66
67    for (i, ch) in raw.char_indices() {
68        match ch {
69            '<' => depth += 1,
70            '>' => depth = depth.saturating_sub(1),
71            ',' if depth == 0 => {
72                entries.push(&raw[start..i]);
73                start = i + 1;
74            }
75            _ => {}
76        }
77    }
78    if start < raw.len() {
79        entries.push(&raw[start..]);
80    }
81
82    entries
83}