mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-12-22 20:16:27 +00:00
improve error
This commit is contained in:
parent
94d85d922f
commit
25f9691c75
13 changed files with 168 additions and 67 deletions
|
@ -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"
|
||||
|
|
159
src/error.rs
159
src/error.rs
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
|
|
|
@ -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()?),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue