1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-05-25 03:38:21 +00:00

switch error implementation #23

closes #23
This commit is contained in:
Luro02 2020-01-23 19:13:26 +01:00
parent 448c331447
commit ac80ac5c9d
No known key found for this signature in database
GPG key ID: B66FD4F74501A9CF
17 changed files with 115 additions and 141 deletions

View file

@ -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"] }

View file

@ -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) }
}

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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;
/// #

View file

@ -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`]:
/// ```

View file

@ -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)?,
})
}
}

View file

@ -735,7 +735,7 @@ impl FromStr for ExtXMedia {
}
}
builder.build().map_err(Error::builder_error)
builder.build().map_err(Error::builder)
}
}

View file

@ -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()),
_ => {}
}
}

View file

@ -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());

View file

@ -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))
}
}

View file

@ -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,

View file

@ -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 {

View file

@ -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)?,
})
}
}

View file

@ -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

View file

@ -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]

View file

@ -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)),