mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-12-23 12:30:29 +00:00
parent
448c331447
commit
ac80ac5c9d
17 changed files with 115 additions and 141 deletions
|
@ -16,7 +16,7 @@ travis-ci = { repository = "sile/hls_m3u8" }
|
|||
codecov = { repository = "sile/hls_m3u8" }
|
||||
|
||||
[dependencies]
|
||||
failure = "0.1.5"
|
||||
thiserror = "1.0"
|
||||
derive_builder = "0.8.0"
|
||||
chrono = "0.4.9"
|
||||
strum = { version = "0.16.0", features = ["derive"] }
|
||||
|
|
179
src/error.rs
179
src/error.rs
|
@ -1,39 +1,33 @@
|
|||
use std::fmt;
|
||||
|
||||
use failure::{Backtrace, Context, Fail};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::types::ProtocolVersion;
|
||||
|
||||
/// This crate specific `Result` type.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// The [`ErrorKind`].
|
||||
#[derive(Debug, Fail, Clone, PartialEq, Eq)]
|
||||
pub enum ErrorKind {
|
||||
#[fail(display = "ChronoParseError: {}", _0)]
|
||||
/// An error from the [Chrono](chrono) crate.
|
||||
ChronoParseError(String),
|
||||
|
||||
#[fail(display = "UnknownError: {}", _0)]
|
||||
/// An unknown error occured.
|
||||
UnknownError(String),
|
||||
|
||||
#[fail(display = "A value is missing for the attribute {}", _0)]
|
||||
#[derive(Debug, Error, Clone, PartialEq)]
|
||||
enum ErrorKind {
|
||||
/// A required value is missing.
|
||||
#[error("A value is missing for the attribute {}", _0)]
|
||||
MissingValue(String),
|
||||
|
||||
#[fail(display = "Invalid Input")]
|
||||
/// Error for anything.
|
||||
#[error("Invalid Input")]
|
||||
InvalidInput,
|
||||
|
||||
#[fail(display = "ParseIntError: {}", _0)]
|
||||
#[error("{}", _0)]
|
||||
/// Failed to parse a String to int.
|
||||
ParseIntError(String),
|
||||
ParseIntError(::std::num::ParseIntError),
|
||||
|
||||
#[fail(display = "ParseFloatError: {}", _0)]
|
||||
#[error("{}", _0)]
|
||||
/// Failed to parse a String to float.
|
||||
ParseFloatError(String),
|
||||
ParseFloatError(::std::num::ParseFloatError),
|
||||
|
||||
#[fail(display = "MissingTag: Expected {} at the start of {:?}", tag, input)]
|
||||
/// A tag is missing, that is required at the start of the input.
|
||||
#[error("Expected `{}` at the start of {:?}", tag, input)]
|
||||
MissingTag {
|
||||
/// The required tag.
|
||||
tag: String,
|
||||
|
@ -41,100 +35,98 @@ pub enum ErrorKind {
|
|||
input: String,
|
||||
},
|
||||
|
||||
#[fail(display = "CustomError: {}", _0)]
|
||||
#[error("{}", _0)]
|
||||
/// A custom error.
|
||||
Custom(String),
|
||||
|
||||
#[fail(display = "Unmatched Group: {:?}", _0)]
|
||||
/// Unmatched Group
|
||||
#[error("Unmatched Group: {:?}", _0)]
|
||||
UnmatchedGroup(String),
|
||||
|
||||
#[fail(display = "Unknown Protocol version: {:?}", _0)]
|
||||
/// Unknown m3u8 version. This library supports up to ProtocolVersion 7.
|
||||
#[error("Unknown protocol version {:?}", _0)]
|
||||
UnknownProtocolVersion(String),
|
||||
|
||||
#[fail(display = "IoError: {}", _0)]
|
||||
/// Some io error
|
||||
#[error("{}", _0)]
|
||||
Io(String),
|
||||
|
||||
#[fail(
|
||||
display = "VersionError: required_version: {:?}, specified_version: {:?}",
|
||||
_0, _1
|
||||
)]
|
||||
/// This error occurs, if there is a ProtocolVersion mismatch.
|
||||
VersionError(String, String),
|
||||
#[error("required_version: {:?}, specified_version: {:?}", _0, _1)]
|
||||
VersionError(ProtocolVersion, ProtocolVersion),
|
||||
|
||||
#[fail(display = "BuilderError: {}", _0)]
|
||||
/// An Error from a Builder.
|
||||
BuilderError(String),
|
||||
|
||||
#[fail(display = "Missing Attribute: {}", _0)]
|
||||
/// An attribute is missing.
|
||||
#[error("Missing Attribute: {}", _0)]
|
||||
MissingAttribute(String),
|
||||
|
||||
#[fail(display = "Unexpected Attribute: {:?}", _0)]
|
||||
/// An unexpected value.
|
||||
#[error("Unexpected Attribute: {:?}", _0)]
|
||||
UnexpectedAttribute(String),
|
||||
|
||||
#[fail(display = "Unexpected Tag: {:?}", _0)]
|
||||
/// An unexpected tag.
|
||||
#[error("Unexpected Tag: {:?}", _0)]
|
||||
UnexpectedTag(String),
|
||||
|
||||
/// An error from the [`chrono`] crate.
|
||||
#[error("{}", _0)]
|
||||
ChronoParseError(chrono::ParseError),
|
||||
|
||||
/// An error from a Builder.
|
||||
#[error("BuilderError: {}", _0)]
|
||||
Builder(String),
|
||||
|
||||
#[error("{}", _0)]
|
||||
Hex(hex::FromHexError),
|
||||
|
||||
/// Hints that destructuring should not be exhaustive.
|
||||
///
|
||||
/// This enum may grow additional variants, so this makes sure clients
|
||||
/// don't count on exhaustive matching. (Otherwise, adding a new variant
|
||||
/// could break existing code.)
|
||||
#[doc(hidden)]
|
||||
#[fail(display = "Invalid error")]
|
||||
#[error("Invalid error")]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// The Error type of this library.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
inner: Context<ErrorKind>,
|
||||
inner: ErrorKind,
|
||||
}
|
||||
|
||||
impl Fail for Error {
|
||||
fn cause(&self) -> Option<&dyn Fail> { self.inner.cause() }
|
||||
|
||||
fn backtrace(&self) -> Option<&Backtrace> { self.inner.backtrace() }
|
||||
}
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt(f) }
|
||||
}
|
||||
|
||||
impl From<ErrorKind> for Error {
|
||||
fn from(kind: ErrorKind) -> Self { Self::from(Context::new(kind)) }
|
||||
}
|
||||
|
||||
impl From<Context<ErrorKind>> for Error {
|
||||
fn from(inner: Context<ErrorKind>) -> Self { Self { inner } }
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn new(inner: ErrorKind) -> Self { Self { inner } }
|
||||
|
||||
pub(crate) fn custom<T: fmt::Display>(value: T) -> Self {
|
||||
Self::new(ErrorKind::Custom(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn missing_value<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::MissingValue(value.to_string()))
|
||||
Self::new(ErrorKind::MissingValue(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn unexpected_attribute<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::UnexpectedAttribute(value.to_string()))
|
||||
Self::new(ErrorKind::UnexpectedAttribute(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn unexpected_tag<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::UnexpectedTag(value.to_string()))
|
||||
Self::new(ErrorKind::UnexpectedTag(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn invalid_input() -> Self { Self::from(ErrorKind::InvalidInput) }
|
||||
pub(crate) fn invalid_input() -> Self { Self::new(ErrorKind::InvalidInput) }
|
||||
|
||||
pub(crate) fn parse_int_error<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::ParseIntError(value.to_string()))
|
||||
pub(crate) fn parse_int(value: ::std::num::ParseIntError) -> Self {
|
||||
Self::new(ErrorKind::ParseIntError(value))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_float_error<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::ParseFloatError(value.to_string()))
|
||||
pub(crate) fn parse_float(value: ::std::num::ParseFloatError) -> Self {
|
||||
Self::new(ErrorKind::ParseFloatError(value))
|
||||
}
|
||||
|
||||
pub(crate) fn missing_tag<T, U>(tag: T, input: U) -> Self
|
||||
|
@ -142,76 +134,53 @@ impl Error {
|
|||
T: ToString,
|
||||
U: ToString,
|
||||
{
|
||||
Self::from(ErrorKind::MissingTag {
|
||||
Self::new(ErrorKind::MissingTag {
|
||||
tag: tag.to_string(),
|
||||
input: input.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn unmatched_group<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::UnmatchedGroup(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn custom<T>(value: T) -> Self
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
Self::from(ErrorKind::Custom(value.to_string()))
|
||||
Self::new(ErrorKind::UnmatchedGroup(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn unknown_protocol_version<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::UnknownProtocolVersion(value.to_string()))
|
||||
Self::new(ErrorKind::UnknownProtocolVersion(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn io<T: ToString>(value: T) -> Self { Self::from(ErrorKind::Io(value.to_string())) }
|
||||
pub(crate) fn io<T: ToString>(value: T) -> Self { Self::new(ErrorKind::Io(value.to_string())) }
|
||||
|
||||
pub(crate) fn builder_error<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::BuilderError(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn chrono<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::ChronoParseError(value.to_string()))
|
||||
pub(crate) fn builder<T: ToString>(value: T) -> Self {
|
||||
Self::new(ErrorKind::Builder(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn missing_attribute<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::MissingAttribute(value.to_string()))
|
||||
Self::new(ErrorKind::MissingAttribute(value.to_string()))
|
||||
}
|
||||
|
||||
// third party crates:
|
||||
pub(crate) fn chrono(value: chrono::format::ParseError) -> Self {
|
||||
Self::new(ErrorKind::ChronoParseError(value))
|
||||
}
|
||||
|
||||
pub(crate) fn hex(value: hex::FromHexError) -> Self { Self::new(ErrorKind::Hex(value)) }
|
||||
|
||||
pub(crate) fn strum(value: strum::ParseError) -> Self {
|
||||
Self::new(ErrorKind::Custom(value.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<::std::num::ParseIntError> for Error {
|
||||
fn from(value: ::std::num::ParseIntError) -> Self { Self::parse_int_error(value) }
|
||||
fn from(value: ::std::num::ParseIntError) -> Self { Self::parse_int(value) }
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<::std::num::ParseFloatError> for Error {
|
||||
fn from(value: ::std::num::ParseFloatError) -> Self { Self::parse_float_error(value) }
|
||||
}
|
||||
|
||||
impl From<::std::io::Error> for Error {
|
||||
fn from(value: ::std::io::Error) -> Self { Self::io(value) }
|
||||
}
|
||||
|
||||
impl From<::chrono::ParseError> for Error {
|
||||
fn from(value: ::chrono::ParseError) -> Self { Self::chrono(value) }
|
||||
fn from(value: ::std::num::ParseFloatError) -> Self { Self::parse_float(value) }
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<::strum::ParseError> for Error {
|
||||
fn from(value: ::strum::ParseError) -> Self {
|
||||
Self::custom(value) // TODO!
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(value: String) -> Self { Self::custom(value) }
|
||||
}
|
||||
|
||||
impl From<::core::convert::Infallible> for Error {
|
||||
fn from(_: ::core::convert::Infallible) -> Self {
|
||||
Self::custom("An Infallible error has been returned! (this should never happen!)")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::hex::FromHexError> for Error {
|
||||
fn from(value: ::hex::FromHexError) -> Self {
|
||||
Self::custom(value) // TODO!
|
||||
}
|
||||
fn from(value: ::strum::ParseError) -> Self { Self::strum(value) }
|
||||
}
|
||||
|
|
|
@ -65,16 +65,15 @@ impl MasterPlaylist {
|
|||
/// Returns a Builder for a [`MasterPlaylist`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use hls_m3u8::tags::ExtXStart;
|
||||
/// use hls_m3u8::MasterPlaylist;
|
||||
///
|
||||
/// # fn main() -> Result<(), hls_m3u8::Error> {
|
||||
/// MasterPlaylist::builder()
|
||||
/// .start_tag(ExtXStart::new(20.123456))
|
||||
/// .build()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # Ok::<(), Box<dyn ::std::error::Error>>(())
|
||||
/// ```
|
||||
pub fn builder() -> MasterPlaylistBuilder { MasterPlaylistBuilder::default() }
|
||||
|
||||
|
@ -438,7 +437,7 @@ impl FromStr for MasterPlaylist {
|
|||
builder.session_data_tags(session_data_tags);
|
||||
builder.session_key_tags(session_key_tags);
|
||||
|
||||
builder.build().map_err(Error::builder_error)
|
||||
builder.build().map_err(Error::builder)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -353,7 +353,7 @@ fn parse_media_playlist(
|
|||
Line::Uri(uri) => {
|
||||
segment.uri(uri);
|
||||
segment.keys(available_key_tags.clone());
|
||||
segments.push(segment.build().map_err(Error::builder_error)?);
|
||||
segments.push(segment.build().map_err(Error::builder)?);
|
||||
segment = MediaSegment::builder();
|
||||
has_partial_segment = false;
|
||||
}
|
||||
|
@ -365,7 +365,7 @@ fn parse_media_playlist(
|
|||
}
|
||||
|
||||
builder.segments(segments);
|
||||
builder.build().map_err(Error::builder_error)
|
||||
builder.build().map_err(Error::builder)
|
||||
}
|
||||
|
||||
impl FromStr for MediaPlaylist {
|
||||
|
|
|
@ -12,18 +12,18 @@ use crate::{Error, RequiredVersion};
|
|||
/// It is the at the start of every [`Media Playlist`] and [`Master Playlist`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Parsing from a [`str`]:
|
||||
///
|
||||
/// ```
|
||||
/// # use failure::Error;
|
||||
/// # use hls_m3u8::tags::ExtM3u;
|
||||
/// #
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// assert_eq!("#EXTM3U".parse::<ExtM3u>()?, ExtM3u);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # Ok::<(), Box<dyn ::std::error::Error>>(())
|
||||
/// ```
|
||||
///
|
||||
/// Converting to a [`str`]:
|
||||
///
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtM3u;
|
||||
/// #
|
||||
|
|
|
@ -12,21 +12,18 @@ use crate::{Error, RequiredVersion};
|
|||
/// It applies to the entire Playlist.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Parsing from a [`str`]:
|
||||
/// ```
|
||||
/// # use failure::Error;
|
||||
/// # use hls_m3u8::tags::ExtXVersion;
|
||||
/// #
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// use hls_m3u8::types::ProtocolVersion;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// "#EXT-X-VERSION:5".parse::<ExtXVersion>()?,
|
||||
/// ExtXVersion::new(ProtocolVersion::V5)
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # Ok::<(), Box<dyn ::std::error::Error>>(())
|
||||
/// ```
|
||||
/// Converting to a [`str`]:
|
||||
/// ```
|
||||
|
|
|
@ -85,7 +85,7 @@ impl ExtXIFrameStreamInfBuilder {
|
|||
.uri
|
||||
.clone()
|
||||
.ok_or_else(|| Error::missing_value("frame rate"))?,
|
||||
stream_inf: self.stream_inf.build().map_err(Error::builder_error)?,
|
||||
stream_inf: self.stream_inf.build().map_err(Error::builder)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -735,7 +735,7 @@ impl FromStr for ExtXMedia {
|
|||
}
|
||||
}
|
||||
|
||||
builder.build().map_err(Error::builder_error)
|
||||
builder.build().map_err(Error::builder)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ impl ExtXStreamInfBuilder {
|
|||
audio: self.audio.clone(),
|
||||
subtitles: self.subtitles.clone(),
|
||||
closed_captions: self.closed_captions.clone(),
|
||||
stream_inf: self.stream_inf.build().map_err(Error::builder_error)?,
|
||||
stream_inf: self.stream_inf.build().map_err(Error::builder)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ impl FromStr for ExtXStreamInf {
|
|||
"FRAME-RATE" => frame_rate = Some((value.parse())?),
|
||||
"AUDIO" => audio = Some(unquote(value)),
|
||||
"SUBTITLES" => subtitles = Some(unquote(value)),
|
||||
"CLOSED-CAPTIONS" => closed_captions = Some(value.parse()?),
|
||||
"CLOSED-CAPTIONS" => closed_captions = Some(value.parse().unwrap()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -719,7 +719,7 @@ impl FromStr for ExtXDateRange {
|
|||
"ID" => id = Some(unquote(value)),
|
||||
"CLASS" => class = Some(unquote(value)),
|
||||
"START-DATE" => start_date = Some(unquote(value)),
|
||||
"END-DATE" => end_date = Some(unquote(value).parse()?),
|
||||
"END-DATE" => end_date = Some(unquote(value).parse().map_err(Error::chrono)?),
|
||||
"DURATION" => {
|
||||
duration = Some(Duration::from_secs_f64(value.parse()?));
|
||||
}
|
||||
|
@ -750,7 +750,8 @@ impl FromStr for ExtXDateRange {
|
|||
let id = id.ok_or_else(|| Error::missing_value("ID"))?;
|
||||
let start_date = start_date
|
||||
.ok_or_else(|| Error::missing_value("START-DATE"))?
|
||||
.parse()?;
|
||||
.parse()
|
||||
.map_err(Error::chrono)?;
|
||||
|
||||
if end_on_next && class.is_none() {
|
||||
return Err(Error::invalid_input());
|
||||
|
|
|
@ -114,7 +114,7 @@ impl FromStr for ExtXProgramDateTime {
|
|||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let input = tag(input, Self::PREFIX)?;
|
||||
|
||||
let date_time = DateTime::parse_from_rfc3339(input)?;
|
||||
let date_time = DateTime::parse_from_rfc3339(input).map_err(Error::chrono)?;
|
||||
Ok(Self::new(date_time))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,8 @@ impl FromStr for Channels {
|
|||
let channel_number = parameters
|
||||
.first()
|
||||
.ok_or_else(|| Error::missing_attribute("First parameter of channels!"))?
|
||||
.parse()?;
|
||||
.parse()
|
||||
.map_err(Error::parse_int)?;
|
||||
|
||||
Ok(Self {
|
||||
channel_number,
|
||||
|
|
|
@ -40,7 +40,9 @@ impl DecimalFloatingPoint {
|
|||
impl FromStr for DecimalFloatingPoint {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> { Self::new(input.parse()?) }
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
Self::new(input.parse().map_err(Error::parse_float)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DecimalFloatingPoint {
|
||||
|
|
|
@ -59,8 +59,8 @@ impl FromStr for DecimalResolution {
|
|||
}
|
||||
|
||||
Ok(Self {
|
||||
width: tokens[0].parse()?,
|
||||
height: tokens[1].parse()?,
|
||||
width: tokens[0].parse().map_err(Error::parse_int)?,
|
||||
height: tokens[1].parse().map_err(Error::parse_int)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -295,11 +295,11 @@ impl FromStr for DecryptionKey {
|
|||
|
||||
for (key, value) in input.parse::<AttributePairs>()? {
|
||||
match key.as_str() {
|
||||
"METHOD" => method = Some(value.parse()?),
|
||||
"METHOD" => method = Some(value.parse().map_err(Error::strum)?),
|
||||
"URI" => uri = Some(unquote(value)),
|
||||
"IV" => iv = Some(value.parse()?),
|
||||
"KEYFORMAT" => key_format = Some(value.parse()?),
|
||||
"KEYFORMATVERSIONS" => key_format_versions = Some(value.parse()?),
|
||||
"KEYFORMATVERSIONS" => key_format_versions = Some(value.parse().unwrap()),
|
||||
_ => {
|
||||
// [6.3.1. General Client Responsibilities]
|
||||
// > ignore any attribute/value pair with an unrecognized
|
||||
|
|
|
@ -270,11 +270,15 @@ impl FromStr for StreamInf {
|
|||
|
||||
for (key, value) in input.parse::<AttributePairs>()? {
|
||||
match key.as_str() {
|
||||
"BANDWIDTH" => bandwidth = Some(value.parse::<u64>()?),
|
||||
"AVERAGE-BANDWIDTH" => average_bandwidth = Some(value.parse::<u64>()?),
|
||||
"BANDWIDTH" => bandwidth = Some(value.parse::<u64>().map_err(Error::parse_int)?),
|
||||
"AVERAGE-BANDWIDTH" => {
|
||||
average_bandwidth = Some(value.parse::<u64>().map_err(Error::parse_int)?)
|
||||
}
|
||||
"CODECS" => codecs = Some(unquote(value)),
|
||||
"RESOLUTION" => resolution = Some(value.parse()?),
|
||||
"HDCP-LEVEL" => hdcp_level = Some(value.parse()?),
|
||||
"HDCP-LEVEL" => {
|
||||
hdcp_level = Some(value.parse::<HdcpLevel>().map_err(Error::strum)?)
|
||||
}
|
||||
"VIDEO" => video = Some(unquote(value)),
|
||||
_ => {
|
||||
// [6.3.1. General Client Responsibilities]
|
||||
|
|
|
@ -32,9 +32,10 @@ impl FromStr for Value {
|
|||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
if input.starts_with("0x") || input.starts_with("0X") {
|
||||
Ok(Self::Hex(hex::decode(
|
||||
input.trim_start_matches("0x").trim_start_matches("0X"),
|
||||
)?))
|
||||
Ok(Self::Hex(
|
||||
hex::decode(input.trim_start_matches("0x").trim_start_matches("0X"))
|
||||
.map_err(Error::hex)?,
|
||||
))
|
||||
} else {
|
||||
match input.parse() {
|
||||
Ok(value) => Ok(Self::Float(value)),
|
||||
|
|
Loading…
Reference in a new issue