mirror of
https://github.com/sile/hls_m3u8.git
synced 2025-02-16 21:25:15 +00:00
replaced builder with derive_builder
#14
This commit is contained in:
parent
81f9a421fe
commit
3240417304
1 changed files with 69 additions and 172 deletions
|
@ -1,195 +1,87 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use derive_builder::Builder;
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::attribute::AttributePairs;
|
||||||
use crate::types::{InStreamId, MediaType, ProtocolVersion, RequiredVersion};
|
use crate::types::{InStreamId, MediaType, ProtocolVersion, RequiredVersion};
|
||||||
use crate::utils::{parse_yes_or_no, quote, tag, unquote};
|
use crate::utils::{parse_yes_or_no, quote, tag, unquote};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
/// `ExtXMedia` builder.
|
/// [4.3.4.1. EXT-X-MEDIA]
|
||||||
#[derive(Debug, Clone)]
|
///
|
||||||
pub struct ExtXMediaBuilder {
|
/// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
|
||||||
media_type: Option<MediaType>,
|
#[derive(Builder, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[builder(setter(into))]
|
||||||
|
#[builder(build_fn(validate = "Self::validate"))]
|
||||||
|
pub struct ExtXMedia {
|
||||||
|
/// Sets the media type of the rendition.
|
||||||
|
media_type: MediaType,
|
||||||
|
#[builder(setter(strip_option, into), default)]
|
||||||
|
/// Sets the URI that identifies the media playlist.
|
||||||
uri: Option<String>,
|
uri: Option<String>,
|
||||||
group_id: Option<String>,
|
/// Sets the identifier that specifies the group to which the rendition belongs.
|
||||||
|
group_id: String,
|
||||||
|
/// Sets the name of the primary language used in the rendition.
|
||||||
|
#[builder(setter(strip_option, into), default)]
|
||||||
language: Option<String>,
|
language: Option<String>,
|
||||||
|
/// Sets the name of a language associated with the rendition.
|
||||||
|
#[builder(setter(strip_option, into), default)]
|
||||||
assoc_language: Option<String>,
|
assoc_language: Option<String>,
|
||||||
name: Option<String>,
|
/// Sets a human-readable description of the rendition.
|
||||||
default: bool,
|
name: String,
|
||||||
autoselect: Option<bool>,
|
/// Sets the value of the `default` flag.
|
||||||
forced: Option<bool>,
|
#[builder(default)]
|
||||||
|
is_default: bool,
|
||||||
|
/// Sets the value of the `autoselect` flag.
|
||||||
|
#[builder(default)]
|
||||||
|
is_autoselect: bool,
|
||||||
|
/// Sets the value of the `forced` flag.
|
||||||
|
#[builder(default)]
|
||||||
|
is_forced: bool,
|
||||||
|
/// Sets the identifier that specifies a rendition within the segments in the media playlist.
|
||||||
|
#[builder(setter(strip_option, into), default)]
|
||||||
instream_id: Option<InStreamId>,
|
instream_id: Option<InStreamId>,
|
||||||
|
/// Sets the string that represents uniform type identifiers (UTI).
|
||||||
|
#[builder(setter(strip_option, into), default)]
|
||||||
characteristics: Option<String>,
|
characteristics: Option<String>,
|
||||||
|
/// Sets the string that represents the parameters of the rendition.
|
||||||
|
#[builder(setter(strip_option, into), default)]
|
||||||
channels: Option<String>,
|
channels: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtXMediaBuilder {
|
impl ExtXMediaBuilder {
|
||||||
/// Makes a `ExtXMediaBuilder` instance.
|
fn validate(&self) -> Result<(), String> {
|
||||||
pub const fn new() -> Self {
|
|
||||||
ExtXMediaBuilder {
|
|
||||||
media_type: None,
|
|
||||||
uri: None,
|
|
||||||
group_id: None,
|
|
||||||
language: None,
|
|
||||||
assoc_language: None,
|
|
||||||
name: None,
|
|
||||||
default: false,
|
|
||||||
autoselect: None,
|
|
||||||
forced: None,
|
|
||||||
instream_id: None,
|
|
||||||
characteristics: None,
|
|
||||||
channels: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the media type of the rendition.
|
|
||||||
pub fn media_type(&mut self, media_type: MediaType) -> &mut Self {
|
|
||||||
self.media_type = Some(media_type);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the identifier that specifies the group to which the rendition belongs.
|
|
||||||
pub fn group_id<T: ToString>(&mut self, group_id: T) -> &mut Self {
|
|
||||||
self.group_id = Some(group_id.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets a human-readable description of the rendition.
|
|
||||||
pub fn name<T: ToString>(&mut self, name: T) -> &mut Self {
|
|
||||||
self.name = Some(name.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the URI that identifies the media playlist.
|
|
||||||
pub fn uri<T: ToString>(&mut self, uri: T) -> &mut Self {
|
|
||||||
self.uri = Some(uri.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the name of the primary language used in the rendition.
|
|
||||||
pub fn language<T: ToString>(&mut self, language: T) -> &mut Self {
|
|
||||||
self.language = Some(language.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the name of a language associated with the rendition.
|
|
||||||
pub fn assoc_language<T: ToString>(&mut self, language: T) -> &mut Self {
|
|
||||||
self.assoc_language = Some(language.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the value of the `default` flag.
|
|
||||||
pub fn default(&mut self, b: bool) -> &mut Self {
|
|
||||||
self.default = b;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the value of the `autoselect` flag.
|
|
||||||
pub fn autoselect(&mut self, b: bool) -> &mut Self {
|
|
||||||
self.autoselect = Some(b);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the value of the `forced` flag.
|
|
||||||
pub fn forced(&mut self, b: bool) -> &mut Self {
|
|
||||||
self.forced = Some(b);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the identifier that specifies a rendition within the segments in the media playlist.
|
|
||||||
pub fn instream_id(&mut self, id: InStreamId) -> &mut Self {
|
|
||||||
self.instream_id = Some(id);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the string that represents uniform type identifiers (UTI).
|
|
||||||
pub fn characteristics<T: ToString>(&mut self, characteristics: T) -> &mut Self {
|
|
||||||
self.characteristics = Some(characteristics.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the string that represents the parameters of the rendition.
|
|
||||||
pub fn channels<T: ToString>(&mut self, channels: T) -> &mut Self {
|
|
||||||
self.channels = Some(channels.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a `ExtXMedia` instance.
|
|
||||||
pub fn finish(self) -> crate::Result<ExtXMedia> {
|
|
||||||
let media_type = self
|
let media_type = self
|
||||||
.media_type
|
.media_type
|
||||||
.ok_or(Error::missing_value("self.media_type"))?;
|
.ok_or(Error::missing_value("self.media_type").to_string())?;
|
||||||
let group_id = self.group_id.ok_or(Error::missing_value("self.group_id"))?;
|
|
||||||
let name = self.name.ok_or(Error::missing_value("self.name"))?;
|
|
||||||
|
|
||||||
if MediaType::ClosedCaptions == media_type {
|
if MediaType::ClosedCaptions == media_type {
|
||||||
if let None = self.uri {
|
if let None = self.uri {
|
||||||
return Err(Error::missing_value("self.uri"));
|
return Err(Error::missing_value("self.uri").to_string());
|
||||||
}
|
}
|
||||||
self.instream_id
|
self.instream_id
|
||||||
.ok_or(Error::missing_value("self.instream_id"))?;
|
.ok_or(Error::missing_value("self.instream_id").to_string())?;
|
||||||
} else {
|
} else {
|
||||||
if let Some(_) = &self.instream_id {
|
if let Some(_) = &self.instream_id {
|
||||||
Err(Error::invalid_input())?;
|
return Err(Error::invalid_input().to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.default && self.autoselect.is_some() {
|
if self.is_default.unwrap_or(false) && self.is_autoselect.unwrap_or(false) {
|
||||||
if let Some(value) = &self.autoselect {
|
return Err(Error::invalid_input().to_string());
|
||||||
if *value {
|
|
||||||
Err(Error::invalid_input())?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if MediaType::Subtitles != media_type {
|
if MediaType::Subtitles != media_type {
|
||||||
if self.forced.is_some() {
|
if self.is_forced.is_some() {
|
||||||
Err(Error::invalid_input())?;
|
return Err(Error::invalid_input().to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExtXMedia {
|
Ok(())
|
||||||
media_type,
|
|
||||||
uri: self.uri,
|
|
||||||
group_id,
|
|
||||||
language: self.language,
|
|
||||||
assoc_language: self.assoc_language,
|
|
||||||
name,
|
|
||||||
default: self.default,
|
|
||||||
autoselect: self.autoselect.unwrap_or(false),
|
|
||||||
forced: self.forced.unwrap_or(false),
|
|
||||||
instream_id: self.instream_id,
|
|
||||||
characteristics: self.characteristics,
|
|
||||||
channels: self.channels,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ExtXMediaBuilder {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [4.3.4.1. EXT-X-MEDIA]
|
|
||||||
///
|
|
||||||
/// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct ExtXMedia {
|
|
||||||
media_type: MediaType,
|
|
||||||
uri: Option<String>,
|
|
||||||
group_id: String,
|
|
||||||
language: Option<String>,
|
|
||||||
assoc_language: Option<String>,
|
|
||||||
name: String,
|
|
||||||
default: bool,
|
|
||||||
autoselect: bool,
|
|
||||||
forced: bool,
|
|
||||||
instream_id: Option<InStreamId>,
|
|
||||||
characteristics: Option<String>,
|
|
||||||
channels: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtXMedia {
|
impl ExtXMedia {
|
||||||
pub(crate) const PREFIX: &'static str = "#EXT-X-MEDIA:";
|
pub(crate) const PREFIX: &'static str = "#EXT-X-MEDIA:";
|
||||||
|
|
||||||
|
@ -202,15 +94,20 @@ impl ExtXMedia {
|
||||||
language: None,
|
language: None,
|
||||||
assoc_language: None,
|
assoc_language: None,
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
default: false,
|
is_default: false,
|
||||||
autoselect: false,
|
is_autoselect: false,
|
||||||
forced: false,
|
is_forced: false,
|
||||||
instream_id: None,
|
instream_id: None,
|
||||||
characteristics: None,
|
characteristics: None,
|
||||||
channels: None,
|
channels: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Makes a [ExtXMediaBuilder] for [ExtXMedia].
|
||||||
|
pub fn builder() -> ExtXMediaBuilder {
|
||||||
|
ExtXMediaBuilder::default()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the type of the media associated with this tag.
|
/// Returns the type of the media associated with this tag.
|
||||||
pub const fn media_type(&self) -> MediaType {
|
pub const fn media_type(&self) -> MediaType {
|
||||||
self.media_type
|
self.media_type
|
||||||
|
@ -242,19 +139,19 @@ impl ExtXMedia {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether this is the default rendition.
|
/// Returns whether this is the default rendition.
|
||||||
pub const fn default(&self) -> bool {
|
pub const fn is_default(&self) -> bool {
|
||||||
self.default
|
self.is_default
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the client may choose to
|
/// Returns whether the client may choose to
|
||||||
/// play this rendition in the absence of explicit user preference.
|
/// play this rendition in the absence of explicit user preference.
|
||||||
pub const fn autoselect(&self) -> bool {
|
pub const fn autoselect(&self) -> bool {
|
||||||
self.autoselect
|
self.is_autoselect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the rendition contains content that is considered essential to play.
|
/// Returns whether the rendition contains content that is considered essential to play.
|
||||||
pub const fn forced(&self) -> bool {
|
pub const fn is_forced(&self) -> bool {
|
||||||
self.forced
|
self.is_forced
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the identifier that specifies a rendition within the segments in the media playlist.
|
/// Returns the identifier that specifies a rendition within the segments in the media playlist.
|
||||||
|
@ -303,13 +200,13 @@ impl fmt::Display for ExtXMedia {
|
||||||
write!(f, ",ASSOC-LANGUAGE={}", quote(value))?;
|
write!(f, ",ASSOC-LANGUAGE={}", quote(value))?;
|
||||||
}
|
}
|
||||||
write!(f, ",NAME={}", quote(&self.name))?;
|
write!(f, ",NAME={}", quote(&self.name))?;
|
||||||
if self.default {
|
if self.is_default {
|
||||||
write!(f, ",DEFAULT=YES")?;
|
write!(f, ",DEFAULT=YES")?;
|
||||||
}
|
}
|
||||||
if self.autoselect {
|
if self.is_autoselect {
|
||||||
write!(f, ",AUTOSELECT=YES")?;
|
write!(f, ",AUTOSELECT=YES")?;
|
||||||
}
|
}
|
||||||
if self.forced {
|
if self.is_forced {
|
||||||
write!(f, ",FORCED=YES")?;
|
write!(f, ",FORCED=YES")?;
|
||||||
}
|
}
|
||||||
if let Some(value) = &self.instream_id {
|
if let Some(value) = &self.instream_id {
|
||||||
|
@ -331,12 +228,12 @@ impl FromStr for ExtXMedia {
|
||||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
let input = tag(input, Self::PREFIX)?;
|
let input = tag(input, Self::PREFIX)?;
|
||||||
|
|
||||||
let mut builder = ExtXMediaBuilder::new();
|
let mut builder = ExtXMedia::builder();
|
||||||
|
|
||||||
for (key, value) in input.parse::<AttributePairs>()? {
|
for (key, value) in input.parse::<AttributePairs>()? {
|
||||||
match key.as_str() {
|
match key.as_str() {
|
||||||
"TYPE" => {
|
"TYPE" => {
|
||||||
builder.media_type(value.parse()?);
|
builder.media_type(value.parse::<MediaType>()?);
|
||||||
}
|
}
|
||||||
"URI" => {
|
"URI" => {
|
||||||
builder.uri(unquote(value));
|
builder.uri(unquote(value));
|
||||||
|
@ -354,16 +251,16 @@ impl FromStr for ExtXMedia {
|
||||||
builder.name(unquote(value));
|
builder.name(unquote(value));
|
||||||
}
|
}
|
||||||
"DEFAULT" => {
|
"DEFAULT" => {
|
||||||
builder.default((parse_yes_or_no(value))?);
|
builder.is_default(parse_yes_or_no(value)?);
|
||||||
}
|
}
|
||||||
"AUTOSELECT" => {
|
"AUTOSELECT" => {
|
||||||
builder.autoselect((parse_yes_or_no(value))?);
|
builder.is_autoselect(parse_yes_or_no(value)?);
|
||||||
}
|
}
|
||||||
"FORCED" => {
|
"FORCED" => {
|
||||||
builder.forced((parse_yes_or_no(value))?);
|
builder.is_forced(parse_yes_or_no(value)?);
|
||||||
}
|
}
|
||||||
"INSTREAM-ID" => {
|
"INSTREAM-ID" => {
|
||||||
builder.instream_id(unquote(value).parse()?);
|
builder.instream_id(unquote(value).parse::<InStreamId>()?);
|
||||||
}
|
}
|
||||||
"CHARACTERISTICS" => {
|
"CHARACTERISTICS" => {
|
||||||
builder.characteristics(unquote(value));
|
builder.characteristics(unquote(value));
|
||||||
|
@ -377,7 +274,7 @@ impl FromStr for ExtXMedia {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(builder.finish())
|
builder.build().map_err(Error::builder_error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue