1use std::io;
2
3use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Debug, Error)]
10pub enum Error {
11 #[error("ffmpeg binary not found on PATH")]
13 FFmpegNotFound {
14 suggestion: Option<String>,
16 },
17
18 #[error("{binary} failed (code: {exit_code:?}): {message}")]
20 ProcessingError {
21 binary: String,
23 exit_code: Option<i32>,
25 message: String,
27 },
28
29 #[error("invalid input: {0}")]
31 InvalidInput(String),
32
33 #[error("filter error: {0}")]
35 FilterError(String),
36
37 #[error("timeout: {0}")]
39 TimeoutError(String),
40
41 #[error(transparent)]
43 Io(#[from] io::Error),
44
45 #[error(transparent)]
47 Json(#[from] serde_json::Error),
48
49 #[error("parse error: {0}")]
51 Parse(String),
52
53 #[error("unsupported operation: {0}")]
55 Unsupported(String),
56}
57
58impl Error {
59 pub(crate) fn command_failed(binary: &str, exit_code: Option<i32>, stderr: &[u8]) -> Self {
61 let message = truncate(stderr);
62 Error::ProcessingError {
63 binary: binary.to_string(),
64 exit_code,
65 message,
66 }
67 }
68
69 pub fn suggestion(&self) -> Option<String> {
71 match self {
72 Error::FFmpegNotFound { suggestion } => suggestion.clone(),
73 Error::InvalidInput(msg) => {
74 if msg.contains("input path") {
75 Some("ensure input file exists and path is correct".to_string())
76 } else if msg.contains("output path") {
77 Some("ensure output directory exists".to_string())
78 } else {
79 Some("check your parameters".to_string())
80 }
81 }
82 Error::ProcessingError { .. } => {
83 Some("check FFmpeg is installed and your parameters are valid".to_string())
84 }
85 Error::FilterError(msg) => {
86 if msg.contains("unsupported") || msg.contains("not supported") {
87 Some("check FFmpeg version supports this filter".to_string())
88 } else {
89 Some("review filter parameters and syntax".to_string())
90 }
91 }
92 _ => None,
93 }
94 }
95}
96
97fn truncate(message: &[u8]) -> String {
98 const MAX: usize = 4096;
99 let mut text = String::from_utf8_lossy(message).into_owned();
100 if text.len() > MAX {
101 text.truncate(MAX);
102 text.push_str("…");
103 }
104 text
105}