use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct Media {
#[cfg_attr(feature = "serde", serde(rename = "@id", default))]
pub id: String,
#[cfg_attr(
feature = "serde",
serde(
rename = "display-text",
default,
skip_serializing_if = "Option::is_none"
)
)]
pub display_text: Option<String>,
#[cfg_attr(
feature = "serde",
serde(rename = "type", default, skip_serializing_if = "Option::is_none")
)]
pub media_type: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub label: Option<String>,
#[cfg_attr(
feature = "serde",
serde(rename = "src-id", default, skip_serializing_if = "Option::is_none")
)]
pub src_id: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub status: Option<MediaStatus>,
}
impl Media {
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
display_text: None,
media_type: None,
label: None,
src_id: None,
status: None,
}
}
pub fn with_type(mut self, media_type: impl Into<String>) -> Self {
self.media_type = Some(media_type.into());
self
}
pub fn with_status(mut self, status: MediaStatus) -> Self {
self.status = Some(status);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum MediaStatus {
#[cfg_attr(feature = "serde", serde(rename = "recvonly"))]
RecvOnly,
#[cfg_attr(feature = "serde", serde(rename = "sendonly"))]
SendOnly,
#[cfg_attr(feature = "serde", serde(rename = "sendrecv"))]
SendRecv,
#[cfg_attr(feature = "serde", serde(rename = "inactive"))]
Inactive,
}
impl fmt::Display for MediaStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::RecvOnly => "recvonly",
Self::SendOnly => "sendonly",
Self::SendRecv => "sendrecv",
Self::Inactive => "inactive",
})
}
}
#[derive(Debug, Clone)]
pub struct ParseMediaStatusError(pub String);
impl fmt::Display for ParseMediaStatusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid media status: {:?}", self.0)
}
}
impl std::error::Error for ParseMediaStatusError {}
impl FromStr for MediaStatus {
type Err = ParseMediaStatusError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"recvonly" => Ok(Self::RecvOnly),
"sendonly" => Ok(Self::SendOnly),
"sendrecv" => Ok(Self::SendRecv),
"inactive" => Ok(Self::Inactive),
other => Err(ParseMediaStatusError(other.to_owned())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn media_status_round_trip() {
for (s, expected) in [
("recvonly", MediaStatus::RecvOnly),
("sendonly", MediaStatus::SendOnly),
("sendrecv", MediaStatus::SendRecv),
("inactive", MediaStatus::Inactive),
] {
let parsed: MediaStatus = s
.parse()
.unwrap();
assert_eq!(parsed, expected);
assert_eq!(parsed.to_string(), s);
}
}
#[test]
fn media_builder() {
let m = Media::new("1")
.with_type("audio")
.with_status(MediaStatus::SendRecv);
assert_eq!(m.id, "1");
assert_eq!(
m.media_type
.as_deref(),
Some("audio")
);
assert_eq!(m.status, Some(MediaStatus::SendRecv));
}
}