1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-12-23 04:26:27 +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"
strum = { version = "0.17", features = ["derive"] }
thiserror = "1.0"
backtrace = { version = "0.3", features = ["std"], optional = true }
[dev-dependencies]
pretty_assertions = "0.6"

View file

@ -1,6 +1,9 @@
use std::fmt;
#[cfg(feature = "backtrace")]
use backtrace::Backtrace;
use thiserror::Error;
//use crate::types::ProtocolVersion;
/// This crate specific `Result` type.
@ -9,19 +12,25 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error, Clone, PartialEq)]
#[non_exhaustive]
enum ErrorKind {
#[error("a value is missing for the attribute {}", _0)]
MissingValue(String),
#[error("a value is missing for the attribute {value}")]
MissingValue { value: String },
#[error("invalid input")]
InvalidInput,
#[error("{}", _0)]
ParseIntError(::std::num::ParseIntError),
#[error("{source}: {input:?}")]
ParseIntError {
input: String,
source: ::std::num::ParseIntError,
},
#[error("{}", _0)]
ParseFloatError(::std::num::ParseFloatError),
#[error("{source}: {input:?}")]
ParseFloatError {
input: String,
source: ::std::num::ParseFloatError,
},
#[error("expected `{}` at the start of {:?}", tag, input)]
#[error("expected `{tag}` at the start of {input:?}")]
MissingTag {
/// The required tag.
tag: String,
@ -29,41 +38,46 @@ enum ErrorKind {
input: String,
},
#[error("{}", _0)]
#[error("{0}")]
Custom(String),
#[error("unmatched group: {:?}", _0)]
#[error("unmatched group: {0:?}")]
UnmatchedGroup(String),
#[error("unknown protocol version {:?}", _0)]
#[error("unknown protocol version {0:?}")]
UnknownProtocolVersion(String),
// #[error("required_version: {:?}, specified_version: {:?}", _0, _1)]
// VersionError(ProtocolVersion, ProtocolVersion),
#[error("missing attribute: {}", _0)]
MissingAttribute(String),
#[error("missing attribute: {attribute:?}")]
MissingAttribute { attribute: String },
#[error("unexpected attribute: {:?}", _0)]
UnexpectedAttribute(String),
#[error("unexpected attribute: {attribute:?}")]
UnexpectedAttribute { attribute: String },
#[error("unexpected tag: {:?}", _0)]
UnexpectedTag(String),
#[error("unexpected tag: {tag:?}")]
UnexpectedTag { tag: String },
#[error("{}", _0)]
ChronoParseError(chrono::ParseError),
#[error("{source}")]
Chrono { source: chrono::ParseError },
#[error("builder error: {}", _0)]
Builder(String),
#[error("builder error: {message}")]
Builder { message: String },
#[doc(hidden)]
#[error("{}", _0)]
Hex(hex::FromHexError),
#[error("{source}")]
Hex { source: hex::FromHexError },
}
/// The Error type of this library.
#[derive(Debug)]
pub struct Error {
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 {}
@ -74,32 +88,53 @@ impl fmt::Display for Error {
#[allow(clippy::needless_pass_by_value)]
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 {
Self::new(ErrorKind::Custom(value.to_string()))
}
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 {
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 {
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 {
Self::new(ErrorKind::ParseIntError(value))
pub(crate) fn parse_int<T: fmt::Display>(input: T, source: ::std::num::ParseIntError) -> Self {
Self::new(ErrorKind::ParseIntError {
input: input.to_string(),
source,
})
}
pub(crate) fn parse_float(value: ::std::num::ParseFloatError) -> Self {
Self::new(ErrorKind::ParseFloatError(value))
pub(crate) fn parse_float<T: fmt::Display>(
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
@ -122,36 +157,66 @@ impl Error {
}
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 {
Self::new(ErrorKind::MissingAttribute(value.to_string()))
Self::new(ErrorKind::MissingAttribute {
attribute: value.to_string(),
})
}
// third party crates:
pub(crate) fn chrono(value: chrono::format::ParseError) -> Self {
Self::new(ErrorKind::ChronoParseError(value))
pub(crate) fn chrono(source: chrono::format::ParseError) -> Self {
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 {
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)]
impl From<::strum::ParseError> for Error {
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::utils::tag;
use crate::Error;
use crate::RequiredVersion;
/// # [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE]
@ -62,10 +63,12 @@ impl fmt::Display for ExtXDiscontinuitySequence {
}
impl FromStr for ExtXDiscontinuitySequence {
type Err = crate::Error;
type Err = Error;
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))
}
}
@ -97,6 +100,11 @@ mod test {
ExtXDiscontinuitySequence::new(123),
"#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]

View file

@ -72,7 +72,9 @@ impl FromStr for ExtXMediaSequence {
type Err = Error;
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))
}
}

View file

@ -68,7 +68,9 @@ impl FromStr for ExtXTargetDuration {
type Err = Error;
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)))
}
}

View file

@ -211,10 +211,14 @@ impl FromStr for ExtXDateRange {
"START-DATE" => start_date = Some(unquote(value)),
"END-DATE" => end_date = Some(unquote(value).parse().map_err(Error::chrono)?),
"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 = 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-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> {
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
.next()
.map(str::trim)

View file

@ -79,10 +79,13 @@ impl FromStr for ByteRange {
let length = input
.next()
.ok_or_else(|| Error::custom("missing length for #EXT-X-BYTERANGE"))
.and_then(|s| s.parse().map_err(Error::parse_int))?;
.ok_or_else(|| Error::custom("missing length"))?;
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))
}

View file

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

View file

@ -59,7 +59,7 @@ impl FromStr for Float {
type Err = Error;
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)
}
}

View file

@ -41,12 +41,12 @@ impl FromStr for Resolution {
let width = input
.next()
.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
.next()
.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 })
}

View file

@ -283,9 +283,19 @@ impl FromStr for StreamData {
for (key, value) in AttributePairs::new(input) {
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 = 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)),
"RESOLUTION" => resolution = Some(value.parse()?),

View file

@ -63,7 +63,7 @@ impl FromStr for UFloat {
type Err = Error;
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)
}
}