use derive_into_owned::IntoOwned;
use crate::{
attributes::AttributeLine,
lines::{
bandwidth::BandWidth, connection::Connection, email::EmailAddress, origin::Origin,
phone_number::PhoneNumber, session_information::SessionInformation,
session_name::SessionName, timing::Timing, uri::Uri, version::Version, SessionLine,
},
media_section::MediaSection,
sdp_line, SdpLine,
};
#[derive(Default, IntoOwned, PartialEq, Eq)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase")
)]
pub struct Session<'a> {
pub version: Option<Version>,
pub name: Option<SessionName<'a>>,
pub timing: Option<Timing>,
pub origin: Option<Origin<'a>>,
pub band_width: Option<BandWidth>,
pub uri: Option<Uri<'a>>,
pub phone_number: Option<PhoneNumber<'a>>,
pub email_address: Option<EmailAddress<'a>>,
pub connection: Option<Connection>,
pub description: Option<SessionInformation<'a>>,
pub attributes: Vec<AttributeLine<'a>>,
pub media: Vec<MediaSection<'a>>,
}
type ParseError<'a> = nom::Err<nom::error::Error<&'a str>>;
#[derive(Default)]
#[cfg_attr(feature = "debug", derive(Debug))]
struct ParserState<'a> {
session: Session<'a>,
current_msection: Option<MediaSection<'a>>,
failed: Option<nom::Err<nom::error::Error<&'a str>>>,
}
impl<'a> Session<'a> {
fn add_line(&mut self, line: SdpLine<'a>) {
use SessionLine::*;
match line {
SdpLine::Session(Version(version)) => self.version = Some(version),
SdpLine::Session(Name(session_name)) => self.name = Some(session_name),
SdpLine::Session(Timing(timing)) => self.timing = Some(timing),
SdpLine::Session(Origin(origin)) => self.origin = Some(origin),
SdpLine::Session(BandWidth(bw)) => self.band_width = Some(bw),
SdpLine::Session(Uri(uri)) => self.uri = Some(uri),
SdpLine::Session(PhoneNumber(phone)) => self.phone_number = Some(phone),
SdpLine::Session(EmailAddress(email)) => self.email_address = Some(email),
SdpLine::Session(Connection(connection)) => self.connection = Some(connection),
SdpLine::Session(Description(info)) => self.description = Some(info),
SdpLine::Session(Media(_)) => unreachable!(),
SdpLine::Attribute(a) => self.attributes.push(a),
SdpLine::Comment(_) => {}
}
}
fn try_from(sdp: &'a str, fallible: bool) -> Result<Session<'a>, ParseError<'a>> {
let mut state = {
sdp.lines().fold(ParserState::default(), |mut state, line| {
if state.failed.is_some() {
return state;
}
match sdp_line(line) {
Ok((_, parsed)) => {
if let SdpLine::Session(SessionLine::Media(mline)) = parsed {
if let Some(m) = state.current_msection.take() {
state.session.media.push(m);
}
let new_m_section = MediaSection::from(mline);
state.current_msection = Some(new_m_section);
} else if let Some(ref mut msection) = state.current_msection {
msection.add_line(parsed);
} else {
state.session.add_line(parsed);
}
}
Err(e) => {
if fallible {
state.failed = Some(e)
}
}
}
state
})
};
if let Some(err) = state.failed {
return Err(err);
}
if let Some(m) = state.current_msection.take() {
state.session.media.push(m);
}
Ok(state.session)
}
pub fn read_str(sdp: &'a str) -> Session<'a> {
Self::try_from(sdp, false).expect("unfallible should mean this never unwraps")
}
pub fn modify_media<F>(mut self, f: F) -> Self
where
F: Fn(MediaSection) -> MediaSection,
{
self.media = self.media.into_iter().map(f).collect();
self
}
}
#[cfg(all(feature = "udisplay", not(feature = "display")))]
impl std::string::ToString for Session<'_> {
fn to_string(&self) -> String {
let mut output = String::new();
ufmt::uwrite!(output, "{}", self).unwrap();
output
}
}
#[cfg(all(feature = "udisplay", not(feature = "display")))]
pub fn ufmt_to_string<U: ufmt::uDisplay>(stuff: &U) -> String {
let mut output = String::new();
ufmt::uwrite!(output, "{}", stuff).unwrap();
output
}