1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-06-02 08:52:11 +00:00

improve error

This commit is contained in:
Luro02 2020-02-14 13:01:42 +01:00
parent 94d85d922f
commit 25f9691c75
No known key found for this signature in database
GPG key ID: B66FD4F74501A9CF
13 changed files with 168 additions and 67 deletions

View file

@ -23,6 +23,7 @@ hex = "0.4"
shorthand = "0.1" shorthand = "0.1"
strum = { version = "0.17", features = ["derive"] } strum = { version = "0.17", features = ["derive"] }
thiserror = "1.0" thiserror = "1.0"
backtrace = { version = "0.3", features = ["std"], optional = true }
[dev-dependencies] [dev-dependencies]
pretty_assertions = "0.6" pretty_assertions = "0.6"

View file

@ -1,6 +1,9 @@
use std::fmt; use std::fmt;
#[cfg(feature = "backtrace")]
use backtrace::Backtrace;
use thiserror::Error; use thiserror::Error;
//use crate::types::ProtocolVersion; //use crate::types::ProtocolVersion;
/// This crate specific `Result` type. /// This crate specific `Result` type.
@ -9,19 +12,25 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error, Clone, PartialEq)] #[derive(Debug, Error, Clone, PartialEq)]
#[non_exhaustive] #[non_exhaustive]
enum ErrorKind { enum ErrorKind {
#[error("a value is missing for the attribute {}", _0)] #[error("a value is missing for the attribute {value}")]
MissingValue(String), MissingValue { value: String },
#[error("invalid input")] #[error("invalid input")]
InvalidInput, InvalidInput,
#[error("{}", _0)] #[error("{source}: {input:?}")]
ParseIntError(::std::num::ParseIntError), ParseIntError {
input: String,
source: ::std::num::ParseIntError,
},
#[error("{}", _0)] #[error("{source}: {input:?}")]
ParseFloatError(::std::num::ParseFloatError), ParseFloatError {
input: String,
source: ::std::num::ParseFloatError,
},
#[error("expected `{}` at the start of {:?}", tag, input)] #[error("expected `{tag}` at the start of {input:?}")]
MissingTag { MissingTag {
/// The required tag. /// The required tag.
tag: String, tag: String,
@ -29,41 +38,46 @@ enum ErrorKind {
input: String, input: String,
}, },
#[error("{}", _0)] #[error("{0}")]
Custom(String), Custom(String),
#[error("unmatched group: {:?}", _0)] #[error("unmatched group: {0:?}")]
UnmatchedGroup(String), UnmatchedGroup(String),
#[error("unknown protocol version {:?}", _0)] #[error("unknown protocol version {0:?}")]
UnknownProtocolVersion(String), UnknownProtocolVersion(String),
// #[error("required_version: {:?}, specified_version: {:?}", _0, _1)] // #[error("required_version: {:?}, specified_version: {:?}", _0, _1)]
// VersionError(ProtocolVersion, ProtocolVersion), // VersionError(ProtocolVersion, ProtocolVersion),
#[error("missing attribute: {}", _0)] #[error("missing attribute: {attribute:?}")]
MissingAttribute(String), MissingAttribute { attribute: String },
#[error("unexpected attribute: {:?}", _0)] #[error("unexpected attribute: {attribute:?}")]
UnexpectedAttribute(String), UnexpectedAttribute { attribute: String },
#[error("unexpected tag: {:?}", _0)] #[error("unexpected tag: {tag:?}")]
UnexpectedTag(String), UnexpectedTag { tag: String },
#[error("{}", _0)] #[error("{source}")]
ChronoParseError(chrono::ParseError), Chrono { source: chrono::ParseError },
#[error("builder error: {}", _0)] #[error("builder error: {message}")]
Builder(String), Builder { message: String },
#[doc(hidden)] #[error("{source}")]
#[error("{}", _0)] Hex { source: hex::FromHexError },
Hex(hex::FromHexError),
} }
/// The Error type of this library. /// The Error type of this library.
#[derive(Debug)] #[derive(Debug)]
pub struct Error { pub struct Error {
inner: ErrorKind, inner: ErrorKind,
#[cfg(feature = "backtrace")]
backtrace: Backtrace,
}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool { self.inner == other.inner }
} }
impl std::error::Error for Error {} impl std::error::Error for Error {}
@ -74,32 +88,53 @@ impl fmt::Display for Error {
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
impl Error { impl Error {
const fn new(inner: ErrorKind) -> Self { Self { inner } } fn new(inner: ErrorKind) -> Self {
Self {
inner,
#[cfg(feature = "backtrace")]
backtrace: Backtrace::new(),
}
}
pub(crate) fn custom<T: fmt::Display>(value: T) -> Self { pub(crate) fn custom<T: fmt::Display>(value: T) -> Self {
Self::new(ErrorKind::Custom(value.to_string())) Self::new(ErrorKind::Custom(value.to_string()))
} }
pub(crate) fn missing_value<T: ToString>(value: T) -> Self { pub(crate) fn missing_value<T: ToString>(value: T) -> Self {
Self::new(ErrorKind::MissingValue(value.to_string())) Self::new(ErrorKind::MissingValue {
value: value.to_string(),
})
} }
pub(crate) fn unexpected_attribute<T: ToString>(value: T) -> Self { pub(crate) fn unexpected_attribute<T: ToString>(value: T) -> Self {
Self::new(ErrorKind::UnexpectedAttribute(value.to_string())) Self::new(ErrorKind::UnexpectedAttribute {
attribute: value.to_string(),
})
} }
pub(crate) fn unexpected_tag<T: ToString>(value: T) -> Self { pub(crate) fn unexpected_tag<T: ToString>(value: T) -> Self {
Self::new(ErrorKind::UnexpectedTag(value.to_string())) Self::new(ErrorKind::UnexpectedTag {
tag: value.to_string(),
})
} }
pub(crate) const fn invalid_input() -> Self { Self::new(ErrorKind::InvalidInput) } pub(crate) fn invalid_input() -> Self { Self::new(ErrorKind::InvalidInput) }
pub(crate) fn parse_int(value: ::std::num::ParseIntError) -> Self { pub(crate) fn parse_int<T: fmt::Display>(input: T, source: ::std::num::ParseIntError) -> Self {
Self::new(ErrorKind::ParseIntError(value)) Self::new(ErrorKind::ParseIntError {
input: input.to_string(),
source,
})
} }
pub(crate) fn parse_float(value: ::std::num::ParseFloatError) -> Self { pub(crate) fn parse_float<T: fmt::Display>(
Self::new(ErrorKind::ParseFloatError(value)) input: T,
source: ::std::num::ParseFloatError,
) -> Self {
Self::new(ErrorKind::ParseFloatError {
input: input.to_string(),
source,
})
} }
pub(crate) fn missing_tag<T, U>(tag: T, input: U) -> Self pub(crate) fn missing_tag<T, U>(tag: T, input: U) -> Self
@ -122,36 +157,66 @@ impl Error {
} }
pub(crate) fn builder<T: ToString>(value: T) -> Self { pub(crate) fn builder<T: ToString>(value: T) -> Self {
Self::new(ErrorKind::Builder(value.to_string())) Self::new(ErrorKind::Builder {
message: value.to_string(),
})
} }
pub(crate) fn missing_attribute<T: ToString>(value: T) -> Self { pub(crate) fn missing_attribute<T: ToString>(value: T) -> Self {
Self::new(ErrorKind::MissingAttribute(value.to_string())) Self::new(ErrorKind::MissingAttribute {
attribute: value.to_string(),
})
} }
// third party crates: // third party crates:
pub(crate) fn chrono(value: chrono::format::ParseError) -> Self { pub(crate) fn chrono(source: chrono::format::ParseError) -> Self {
Self::new(ErrorKind::ChronoParseError(value)) Self::new(ErrorKind::Chrono { source })
} }
pub(crate) fn hex(value: hex::FromHexError) -> Self { Self::new(ErrorKind::Hex(value)) } pub(crate) fn hex(source: hex::FromHexError) -> Self {
//
Self::new(ErrorKind::Hex { source })
}
pub(crate) fn strum(value: strum::ParseError) -> Self { pub(crate) fn strum(value: strum::ParseError) -> Self {
Self::new(ErrorKind::Custom(value.to_string())) 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(value) }
}
#[doc(hidden)]
impl From<::std::num::ParseFloatError> for Error {
fn from(value: ::std::num::ParseFloatError) -> Self { Self::parse_float(value) }
}
#[doc(hidden)] #[doc(hidden)]
impl From<::strum::ParseError> for Error { impl From<::strum::ParseError> for Error {
fn from(value: ::strum::ParseError) -> Self { Self::strum(value) } fn from(value: ::strum::ParseError) -> Self { Self::strum(value) }
} }
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_parse_float_error() {
assert_eq!(
Error::parse_float(
"1.x234",
"1.x234"
.parse::<f32>()
.expect_err("this should not parse as a float!")
)
.to_string(),
"invalid float literal: \"1.x234\"".to_string()
);
}
#[test]
fn test_parse_int_error() {
assert_eq!(
Error::parse_int(
"1x",
"1x".parse::<usize>()
.expect_err("this should not parse as an usize!")
)
.to_string(),
"invalid digit found in string: \"1x\"".to_string()
);
}
}

View file

@ -5,6 +5,7 @@ use shorthand::ShortHand;
use crate::types::ProtocolVersion; use crate::types::ProtocolVersion;
use crate::utils::tag; use crate::utils::tag;
use crate::Error;
use crate::RequiredVersion; use crate::RequiredVersion;
/// # [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE] /// # [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE]
@ -62,10 +63,12 @@ impl fmt::Display for ExtXDiscontinuitySequence {
} }
impl FromStr for ExtXDiscontinuitySequence { impl FromStr for ExtXDiscontinuitySequence {
type Err = crate::Error; type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let seq_num = tag(input, Self::PREFIX)?.parse()?; let input = tag(input, Self::PREFIX)?;
let seq_num = input.parse().map_err(|e| Error::parse_int(input, e))?;
Ok(Self::new(seq_num)) Ok(Self::new(seq_num))
} }
} }
@ -97,6 +100,11 @@ mod test {
ExtXDiscontinuitySequence::new(123), ExtXDiscontinuitySequence::new(123),
"#EXT-X-DISCONTINUITY-SEQUENCE:123".parse().unwrap() "#EXT-X-DISCONTINUITY-SEQUENCE:123".parse().unwrap()
); );
assert_eq!(
ExtXDiscontinuitySequence::from_str("#EXT-X-DISCONTINUITY-SEQUENCE:12A"),
Err(Error::parse_int("12A", "12A".parse::<u64>().expect_err("")))
);
} }
#[test] #[test]

View file

@ -72,7 +72,9 @@ impl FromStr for ExtXMediaSequence {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let seq_num = tag(input, Self::PREFIX)?.parse()?; let input = tag(input, Self::PREFIX)?;
let seq_num = input.parse().map_err(|e| Error::parse_int(input, e))?;
Ok(Self::new(seq_num)) Ok(Self::new(seq_num))
} }
} }

View file

@ -68,7 +68,9 @@ impl FromStr for ExtXTargetDuration {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let input = tag(input, Self::PREFIX)?.parse()?; let input = tag(input, Self::PREFIX)?;
let input = input.parse().map_err(|e| Error::parse_int(input, e))?;
Ok(Self::new(Duration::from_secs(input))) Ok(Self::new(Duration::from_secs(input)))
} }
} }

View file

@ -211,10 +211,14 @@ impl FromStr for ExtXDateRange {
"START-DATE" => start_date = Some(unquote(value)), "START-DATE" => start_date = Some(unquote(value)),
"END-DATE" => end_date = Some(unquote(value).parse().map_err(Error::chrono)?), "END-DATE" => end_date = Some(unquote(value).parse().map_err(Error::chrono)?),
"DURATION" => { "DURATION" => {
duration = Some(Duration::from_secs_f64(value.parse()?)); duration = Some(Duration::from_secs_f64(
value.parse().map_err(|e| Error::parse_float(value, e))?,
));
} }
"PLANNED-DURATION" => { "PLANNED-DURATION" => {
planned_duration = Some(Duration::from_secs_f64(value.parse()?)); planned_duration = Some(Duration::from_secs_f64(
value.parse().map_err(|e| Error::parse_float(value, e))?,
));
} }
"SCTE35-CMD" => scte35_cmd = Some(unquote(value)), "SCTE35-CMD" => scte35_cmd = Some(unquote(value)),
"SCTE35-OUT" => scte35_out = Some(unquote(value)), "SCTE35-OUT" => scte35_out = Some(unquote(value)),

View file

@ -153,7 +153,13 @@ impl FromStr for ExtInf {
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut input = tag(input, Self::PREFIX)?.splitn(2, ','); let mut input = tag(input, Self::PREFIX)?.splitn(2, ',');
let duration = Duration::from_secs_f64(input.next().unwrap().parse()?); let duration = input.next().unwrap();
let duration = Duration::from_secs_f64(
duration
.parse()
.map_err(|e| Error::parse_float(duration, e))?,
);
let title = input let title = input
.next() .next()
.map(str::trim) .map(str::trim)

View file

@ -79,10 +79,13 @@ impl FromStr for ByteRange {
let length = input let length = input
.next() .next()
.ok_or_else(|| Error::custom("missing length for #EXT-X-BYTERANGE")) .ok_or_else(|| Error::custom("missing length"))?;
.and_then(|s| s.parse().map_err(Error::parse_int))?; let length = length.parse().map_err(|e| Error::parse_int(length, e))?;
let start = input.next().map(str::parse).transpose()?; let start = input
.next()
.map(|v| v.parse().map_err(|e| Error::parse_int(v, e)))
.transpose()?;
Ok(Self::new(length, start)) Ok(Self::new(length, start))
} }

View file

@ -85,11 +85,11 @@ impl FromStr for Channels {
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut parameters = input.split('/'); let mut parameters = input.split('/');
let channel_number = parameters let param_1 = parameters
.next() .next()
.ok_or_else(|| Error::missing_attribute("first parameter of channels"))? .ok_or_else(|| Error::missing_attribute("first parameter of channels"))?;
.parse()
.map_err(Error::parse_int)?; let channel_number = param_1.parse().map_err(|e| Error::parse_int(param_1, e))?;
Ok(Self { Ok(Self {
channel_number, channel_number,

View file

@ -59,7 +59,7 @@ impl FromStr for Float {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let float = f32::from_str(input).map_err(Error::parse_float)?; let float = f32::from_str(input).map_err(|e| Error::parse_float(input, e))?;
Self::try_from(float) Self::try_from(float)
} }
} }

View file

@ -41,12 +41,12 @@ impl FromStr for Resolution {
let width = input let width = input
.next() .next()
.ok_or_else(|| Error::custom("missing width for `Resolution` or an invalid input")) .ok_or_else(|| Error::custom("missing width for `Resolution` or an invalid input"))
.and_then(|v| v.parse().map_err(Error::parse_int))?; .and_then(|v| v.parse().map_err(|e| Error::parse_int(v, e)))?;
let height = input let height = input
.next() .next()
.ok_or_else(|| Error::custom("missing height for `Resolution` or an invalid input")) .ok_or_else(|| Error::custom("missing height for `Resolution` or an invalid input"))
.and_then(|v| v.parse().map_err(Error::parse_int))?; .and_then(|v| v.parse().map_err(|e| Error::parse_int(v, e)))?;
Ok(Self { width, height }) Ok(Self { width, height })
} }

View file

@ -283,9 +283,19 @@ impl FromStr for StreamData {
for (key, value) in AttributePairs::new(input) { for (key, value) in AttributePairs::new(input) {
match key { match key {
"BANDWIDTH" => bandwidth = Some(value.parse::<u64>().map_err(Error::parse_int)?), "BANDWIDTH" => {
bandwidth = Some(
value
.parse::<u64>()
.map_err(|e| Error::parse_int(value, e))?,
);
}
"AVERAGE-BANDWIDTH" => { "AVERAGE-BANDWIDTH" => {
average_bandwidth = Some(value.parse::<u64>().map_err(Error::parse_int)?) average_bandwidth = Some(
value
.parse::<u64>()
.map_err(|e| Error::parse_int(value, e))?,
)
} }
"CODECS" => codecs = Some(unquote(value)), "CODECS" => codecs = Some(unquote(value)),
"RESOLUTION" => resolution = Some(value.parse()?), "RESOLUTION" => resolution = Some(value.parse()?),

View file

@ -63,7 +63,7 @@ impl FromStr for UFloat {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let float = f32::from_str(input).map_err(Error::parse_float)?; let float = f32::from_str(input).map_err(|e| Error::parse_float(input, e))?;
Self::try_from(float) Self::try_from(float)
} }
} }