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 pub version: Option<Version>,
24
25 pub name: Option<SessionName<'a>>,
27
28 pub timing: Option<Timing>,
30
31 pub origin: Option<Origin<'a>>,
33
34 pub band_width: Option<BandWidth>,
36
37 pub uri: Option<Uri<'a>>,
39
40 pub phone_number: Option<PhoneNumber<'a>>,
42
43 pub email_address: Option<EmailAddress<'a>>,
45
46 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 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}