1#[derive(Debug, thiserror::Error)]
5#[non_exhaustive]
6pub enum Error {
7 #[error("no extractor supports this URL: {0}")]
9 UnsupportedUrl(String),
10 #[error("network error during {stage}: {source}")]
12 Network {
13 stage: &'static str,
15 #[source]
17 source: reqwest::Error,
18 },
19 #[error("extraction failed at {stage}: {message}")]
21 Extraction {
22 stage: &'static str,
24 message: String,
26 },
27 #[error("media unavailable: {reason}")]
29 Unavailable {
30 reason: UnavailableReason,
32 message: String,
34 },
35 #[error("cipher error: {0}")]
37 Cipher(String),
38 #[error("no matching format: {0}")]
40 FormatNotFound(String),
41 #[error("io error: {0}")]
43 Io(#[from] std::io::Error),
44 #[error("postprocess error: {0}")]
46 Postprocess(String),
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[non_exhaustive]
52pub enum UnavailableReason {
53 Gone,
55 AgeRestricted,
57 GeoBlocked,
59 Live,
61 Other,
63}
64
65impl std::fmt::Display for UnavailableReason {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 let s = match self {
68 UnavailableReason::Gone => "gone",
69 UnavailableReason::AgeRestricted => "age-restricted",
70 UnavailableReason::GeoBlocked => "geo-blocked",
71 UnavailableReason::Live => "live",
72 UnavailableReason::Other => "other",
73 };
74 f.write_str(s)
75 }
76}
77
78pub type Result<T> = std::result::Result<T, Error>;
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn error_display_is_useful() {
87 let e = Error::UnsupportedUrl("https://example.com/x".into());
88 assert!(e.to_string().contains("example.com"));
89 }
90}