use std::fmt;
use std::ops::Deref;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
mod data;
#[cfg(test)]
pub(crate) use data::RtpMap;
pub(crate) use data::{FormatParam, Sdp, Session, SessionAttribute, Setup};
pub(crate) use data::{MediaAttribute, MediaLine, MediaType, Msid, Proto};
pub(crate) use data::{RestrictionId, Simulcast, SimulcastGroups, SimulcastLayer};
mod parser;
mod error;
pub use error::SdpError;
#[derive(Debug, PartialEq, Eq)]
pub struct SdpOffer(Sdp);
impl SdpOffer {
pub fn from_sdp_string(input: &str) -> Result<Self, SdpError> {
let sdp = Sdp::parse(input)?;
Ok(SdpOffer(sdp))
}
pub fn to_sdp_string(&self) -> String {
self.0.to_string()
}
#[cfg(test)]
pub(crate) fn into_inner(self) -> Sdp {
self.0
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct SdpAnswer(Sdp);
impl SdpAnswer {
pub fn from_sdp_string(input: &str) -> Result<Self, SdpError> {
let sdp = Sdp::parse(input)?;
Ok(SdpAnswer(sdp))
}
pub fn to_sdp_string(&self) -> String {
self.0.to_string()
}
}
impl Deref for SdpOffer {
type Target = Sdp;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Deref for SdpAnswer {
type Target = Sdp;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Sdp> for SdpOffer {
fn from(v: Sdp) -> Self {
SdpOffer(v)
}
}
impl From<Sdp> for SdpAnswer {
fn from(v: Sdp) -> Self {
SdpAnswer(v)
}
}
impl fmt::Display for SdpOffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", **self)
}
}
impl fmt::Display for SdpAnswer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", **self)
}
}
macro_rules! sdp_ser {
($Struct:tt, $Name:literal, $LCName:literal) => {
impl Serialize for $Struct {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct Data {
r#type: &'static str,
sdp: String,
}
Data {
r#type: $LCName,
sdp: self.0.to_string(),
}
.serialize(s)
}
}
impl<'de> Deserialize<'de> for $Struct {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Data {
r#type: String,
sdp: String,
}
let data = Data::deserialize(d)?;
if data.r#type != $LCName {
return Err(de::Error::custom(format!(
"Expected SDP type '{}', got '{}'",
$LCName, data.r#type
)));
}
let sdp = Sdp::parse(&data.sdp)
.map_err(|err| de::Error::custom(format!("Failed to parse SDP: {:?}", err)))?;
Ok(Self(sdp))
}
}
};
}
sdp_ser!(SdpOffer, "Offer", "offer");
sdp_ser!(SdpAnswer, "Answer", "answer");
#[cfg(test)]
mod test {
use crate::VERSION;
use crate::rtp_::SessionId;
use super::*;
fn sdp() -> Sdp {
Sdp {
session: Session {
id: SessionId::from(123_u64),
attrs: vec![],
bw: None,
},
media_lines: vec![],
}
}
#[test]
fn serialize_deserialize_offer() {
let offer = SdpOffer(sdp());
let json = serde_json::to_string(&offer).unwrap();
let expected = format!(
"{{\"type\":\"offer\",\"sdp\":\
\"v=0\\r\\no=str0m-{VERSION} 123 2 \
IN IP4 0.0.0.0\\r\\ns=-\\r\\nt=0 0\\r\\n\"}}"
);
assert_eq!(json, expected);
let offer2: SdpOffer = serde_json::from_str(&json).unwrap();
assert_eq!(offer, offer2);
}
#[test]
fn serialize_deserialize_answer() {
let answer = SdpAnswer(sdp());
let json = serde_json::to_string(&answer).unwrap();
let expected = format!(
"{{\"type\":\"answer\",\"sdp\":\
\"v=0\\r\\no=str0m-{VERSION} 123 2 \
IN IP4 0.0.0.0\\r\\ns=-\\r\\nt=0 0\\r\\n\"}}"
);
assert_eq!(json, expected);
let answer2: SdpAnswer = serde_json::from_str(&json).unwrap();
assert_eq!(answer, answer2);
}
}