sdp_nom/
session.rs

1use derive_into_owned::IntoOwned;
2
3use crate::{
4    attributes::AttributeLine,
5    lines::{
6        bandwidth::BandWidth, connection::Connection, email::EmailAddress, origin::Origin,
7        phone_number::PhoneNumber, session_information::SessionInformation,
8        session_name::SessionName, timing::Timing, uri::Uri, version::Version, SessionLine,
9    },
10    media_section::MediaSection,
11    sdp_line, SdpLine,
12};
13
14#[derive(Default, IntoOwned, PartialEq, Eq)]
15#[cfg_attr(feature = "debug", derive(Debug))]
16#[cfg_attr(
17    feature = "serde",
18    derive(serde::Serialize, serde::Deserialize),
19    serde(rename_all = "camelCase")
20)]
21pub struct Session<'a> {
22    /// `v=0`
23    pub version: Option<Version>,
24
25    /// `s=-`
26    pub name: Option<SessionName<'a>>,
27
28    /// `t=0 0`
29    pub timing: Option<Timing>,
30
31    /// `o=- 20518 0 IN IP4 203.0.113.1`
32    pub origin: Option<Origin<'a>>,
33
34    /// `b=AS:1024`
35    pub band_width: Option<BandWidth>,
36
37    /// `u=`
38    pub uri: Option<Uri<'a>>,
39
40    /// `p=0118 999 881 999 119 7253`
41    pub phone_number: Option<PhoneNumber<'a>>,
42
43    /// "e=email@example.com"
44    pub email_address: Option<EmailAddress<'a>>,
45
46    /// `c=IN IP4 10.23.42.137`
47    pub connection: Option<Connection>,
48
49    pub description: Option<SessionInformation<'a>>,
50
51    pub attributes: Vec<AttributeLine<'a>>,
52    pub media: Vec<MediaSection<'a>>,
53}
54
55type ParseError<'a> = nom::Err<nom::error::Error<&'a str>>;
56
57#[derive(Default)]
58#[cfg_attr(feature = "debug", derive(Debug))]
59struct ParserState<'a> {
60    session: Session<'a>,
61    current_msection: Option<MediaSection<'a>>,
62    failed: Option<nom::Err<nom::error::Error<&'a str>>>,
63}
64
65impl<'a> Session<'a> {
66    fn add_line(&mut self, line: SdpLine<'a>) {
67        use SessionLine::*;
68        match line {
69            //crate::SdpLine::Session(Session)       => todo!(),
70            SdpLine::Session(Version(version)) => self.version = Some(version),
71            SdpLine::Session(Name(session_name)) => self.name = Some(session_name),
72            SdpLine::Session(Timing(timing)) => self.timing = Some(timing),
73            SdpLine::Session(Origin(origin)) => self.origin = Some(origin),
74            SdpLine::Session(BandWidth(bw)) => self.band_width = Some(bw),
75            SdpLine::Session(Uri(uri)) => self.uri = Some(uri),
76            SdpLine::Session(PhoneNumber(phone)) => self.phone_number = Some(phone),
77            SdpLine::Session(EmailAddress(email)) => self.email_address = Some(email),
78            SdpLine::Session(Connection(connection)) => self.connection = Some(connection),
79            SdpLine::Session(Description(info)) => self.description = Some(info),
80            SdpLine::Session(Media(_)) => unreachable!(),
81            SdpLine::Attribute(a) => self.attributes.push(a),
82            SdpLine::Comment(_) => {}
83        }
84    }
85
86    fn try_from(sdp: &'a str, fallible: bool) -> Result<Session<'a>, ParseError<'a>> {
87        let mut state = {
88            sdp.lines().fold(ParserState::default(), |mut state, line| {
89                if state.failed.is_some() {
90                    return state;
91                }
92                match sdp_line(line) {
93                    Ok((_, parsed)) => {
94                        if let SdpLine::Session(SessionLine::Media(mline)) = parsed {
95                            if let Some(m) = state.current_msection.take() {
96                                state.session.media.push(m);
97                            }
98                            let new_m_section = MediaSection::from(mline);
99                            state.current_msection = Some(new_m_section);
100                        } else if let Some(ref mut msection) = state.current_msection {
101                            msection.add_line(parsed);
102                        } else {
103                            state.session.add_line(parsed);
104                        }
105                    }
106                    Err(e) => {
107                        if fallible {
108                            state.failed = Some(e)
109                        }
110                    }
111                }
112                state
113            })
114        };
115
116        if let Some(err) = state.failed {
117            return Err(err);
118        }
119        if let Some(m) = state.current_msection.take() {
120            state.session.media.push(m);
121        }
122        Ok(state.session)
123    }
124
125    pub fn read_str(sdp: &'a str) -> Session<'a> {
126        Self::try_from(sdp, false).expect("unfallible should mean this never unwraps")
127    }
128
129    pub fn modify_media<F>(mut self, f: F) -> Self
130    where
131        F: Fn(MediaSection) -> MediaSection,
132    {
133        self.media = self.media.into_iter().map(f).collect();
134        self
135    }
136}
137
138#[cfg(all(feature = "udisplay", not(feature = "display")))]
139impl std::string::ToString for Session<'_> {
140    fn to_string(&self) -> String {
141        let mut output = String::new();
142        ufmt::uwrite!(output, "{}", self).unwrap();
143        output
144    }
145}
146
147#[cfg(all(feature = "udisplay", not(feature = "display")))]
148pub fn ufmt_to_string<U: ufmt::uDisplay>(stuff: &U) -> String {
149    let mut output = String::new();
150    ufmt::uwrite!(output, "{}", stuff).unwrap();
151    output
152}