From c4802b76fc123a33904f9da0d9b22af0160809b4 Mon Sep 17 00:00:00 2001 From: Odilf <53334434+Odilf@users.noreply.github.com> Date: Sat, 10 Feb 2024 20:17:29 +0100 Subject: [PATCH] feat: use `anyhow` (#25) --- Cargo.toml | 2 +- README.md | 2 +- examples/download_ffmpeg.rs | 3 +- examples/hello_world.rs | 4 +- src/child.rs | 11 +++-- src/download.rs | 85 +++++++++++++++++--------------- src/error.rs | 97 ------------------------------------- src/ffprobe.rs | 13 ++--- src/iter.rs | 26 +++++----- src/lib.rs | 5 +- src/log_parser.rs | 12 ++--- src/metadata.rs | 5 +- src/paths.rs | 11 +++-- src/version.rs | 13 ++--- 14 files changed, 98 insertions(+), 191 deletions(-) delete mode 100644 src/error.rs diff --git a/Cargo.toml b/Cargo.toml index d2a5846..04bfe56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ license = "MIT" crate-type = ["lib"] [dependencies] -# there are none! +anyhow = "1.0.79" diff --git a/README.md b/README.md index 3683663..e27d4de 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ## Features -- ✨ Zero dependencies +- ✨ Minimal dependencies - ⚡ Automatic FFmpeg CLI download (if needed) - 🤗 Support for Windows, MacOS, and Linux - 🧪 Thoroughly unit tested diff --git a/examples/download_ffmpeg.rs b/examples/download_ffmpeg.rs index 4c38dc4..b7a8441 100644 --- a/examples/download_ffmpeg.rs +++ b/examples/download_ffmpeg.rs @@ -1,12 +1,11 @@ use ffmpeg_sidecar::{ command::ffmpeg_is_installed, download::{check_latest_version, download_ffmpeg_package, ffmpeg_download_url, unpack_ffmpeg}, - error::Result, paths::sidecar_dir, version::ffmpeg_version, }; -fn main() -> Result<()> { +fn main() -> anyhow::Result<()> { if ffmpeg_is_installed() { println!("FFmpeg is already installed! 🎉"); println!("For demo purposes, we'll re-download and unpack it anyway."); diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 895854e..6723faa 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,11 +1,11 @@ -use ffmpeg_sidecar::{command::FfmpegCommand, error::Result, event::FfmpegEvent}; +use ffmpeg_sidecar::{command::FfmpegCommand, event::FfmpegEvent}; /// Iterates over the frames of a `testsrc`. /// /// ```console /// cargo run --example hello_world /// ``` -fn main() -> Result<()> { +fn main() -> anyhow::Result<()> { FfmpegCommand::new() // <- Builder API like `std::process::Command` .testsrc() // <- Discoverable aliases for FFmpeg args .rawvideo() // <- Convenient argument presets diff --git a/src/child.rs b/src/child.rs index 9f49f1e..5226574 100644 --- a/src/child.rs +++ b/src/child.rs @@ -3,7 +3,8 @@ use std::{ process::{Child, ChildStderr, ChildStdin, ChildStdout, ExitStatus}, }; -use crate::error::{Error, Result}; +use anyhow::Context; + use crate::iter::FfmpegIterator; /// A wrapper around [`std::process::Child`] containing a spawned FFmpeg command. @@ -22,7 +23,7 @@ impl FfmpegChild { /// - Progress updates /// - Errors and warnings /// - Raw output frames - pub fn iter(&mut self) -> Result { + pub fn iter(&mut self) -> anyhow::Result { FfmpegIterator::new(self) } @@ -66,12 +67,12 @@ impl FfmpegChild { /// q quit /// s Show QP histogram /// ``` - pub fn send_stdin_command(&mut self, command: &[u8]) -> Result<()> { + pub fn send_stdin_command(&mut self, command: &[u8]) -> anyhow::Result<()> { let mut stdin = self .inner .stdin .take() - .ok_or_else(|| Error::msg("Missing child stdin"))?; + .context("Missing child stdin")?; stdin.write_all(command)?; self.inner.stdin.replace(stdin); Ok(()) @@ -83,7 +84,7 @@ impl FfmpegChild { /// This method returns after the command has been sent; the actual shut down /// may take a few more frames as ffmpeg flushes its buffers and writes the /// trailer, if applicable. - pub fn quit(&mut self) -> Result<()> { + pub fn quit(&mut self) -> anyhow::Result<()> { self.send_stdin_command(b"q") } diff --git a/src/download.rs b/src/download.rs index d4901fb..4121b2b 100644 --- a/src/download.rs +++ b/src/download.rs @@ -5,21 +5,17 @@ use std::{ process::{Command, ExitStatus, Stdio}, }; -use crate::{ - command::ffmpeg_is_installed, - error::{Error, Result}, - paths::sidecar_dir, -}; +use anyhow::Context; + +use crate::{command::ffmpeg_is_installed, paths::sidecar_dir}; pub const UNPACK_DIRNAME: &str = "ffmpeg_release_temp"; /// URL of a manifest file containing the latest published build of FFmpeg. The /// correct URL for the target platform is baked in at compile time. -pub fn ffmpeg_manifest_url() -> Result<&'static str> { +pub fn ffmpeg_manifest_url() -> anyhow::Result<&'static str> { if cfg!(not(target_arch = "x86_64")) { - return Err(Error::msg( - "Downloads must be manually provided for non-x86_64 architectures", - )); + anyhow::bail!("Downloads must be manually provided for non-x86_64 architectures"); } if cfg!(target_os = "windows") { @@ -29,13 +25,13 @@ pub fn ffmpeg_manifest_url() -> Result<&'static str> { } else if cfg!(target_os = "linux") { Ok("https://johnvansickle.com/ffmpeg/release-readme.txt") } else { - Err(Error::msg("Unsupported platform")) + anyhow::bail!("Unsupported platform") } } /// URL for the latest published FFmpeg release. The correct URL for the target /// platform is baked in at compile time. -pub fn ffmpeg_download_url() -> Result<&'static str> { +pub fn ffmpeg_download_url() -> anyhow::Result<&'static str> { if cfg!(all(target_os = "windows", target_arch = "x86_64")) { Ok("https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip") } else if cfg!(all(target_os = "linux", target_arch = "x86_64")) { @@ -45,9 +41,7 @@ pub fn ffmpeg_download_url() -> Result<&'static str> { } else if cfg!(all(target_os = "macos", target_arch = "aarch64")) { Ok("https://www.osxexperts.net/ffmpeg6arm.zip") // Mac M1 } else { - Err(Error::msg( - "Unsupported platform; you can provide your own URL instead and call download_ffmpeg_package directly.", - )) + anyhow::bail!("Unsupported platform; you can provide your own URL instead and call download_ffmpeg_package directly.") } } @@ -57,7 +51,7 @@ pub fn ffmpeg_download_url() -> Result<&'static str> { /// /// If FFmpeg is already installed, the method exits early without downloading /// anything. -pub fn auto_download() -> Result<()> { +pub fn auto_download() -> anyhow::Result<()> { if ffmpeg_is_installed() { return Ok(()); } @@ -67,12 +61,11 @@ pub fn auto_download() -> Result<()> { let archive_path = download_ffmpeg_package(download_url, &destination)?; unpack_ffmpeg(&archive_path, &destination)?; - match ffmpeg_is_installed() { - false => Err(Error::msg( - "FFmpeg failed to install, please install manually.", - )), - true => Ok(()), + if !ffmpeg_is_installed() { + anyhow::bail!("FFmpeg failed to install, please install manually."); } + + Ok(()) } /// Parse the the MacOS version number from a JSON string manifest file. @@ -115,7 +108,7 @@ pub fn parse_linux_version(version: &str) -> Option { } /// Invoke cURL on the command line to download a file, returning it as a string. -pub fn curl(url: &str) -> Result { +pub fn curl(url: &str) -> anyhow::Result { let mut child = Command::new("curl") .args(["-L", url]) .stderr(Stdio::null()) @@ -125,7 +118,7 @@ pub fn curl(url: &str) -> Result { let stdout = child .stdout .take() - .ok_or_else(|| Error::msg("Failed to get stdout"))?; + .context("Failed to get stdout")?; let mut string = String::new(); std::io::BufReader::new(stdout).read_to_string(&mut string)?; @@ -133,45 +126,47 @@ pub fn curl(url: &str) -> Result { } /// Invoke cURL on the command line to download a file, writing to a file. -pub fn curl_to_file(url: &str, destination: &str) -> Result { +pub fn curl_to_file(url: &str, destination: &str) -> anyhow::Result { Command::new("curl") .args(["-L", url]) .args(["-o", destination]) .status() - .map_err(Error::from) + .map_err(Into::into) } /// Makes an HTTP request to obtain the latest version available online, /// automatically choosing the correct URL for the current platform. -pub fn check_latest_version() -> Result { +pub fn check_latest_version() -> anyhow::Result { let string = curl(ffmpeg_manifest_url()?)?; if cfg!(target_os = "windows") { Ok(string) } else if cfg!(target_os = "macos") { - Ok(parse_macos_version(&string).ok_or("failed to parse version number (macos variant)")?) + parse_macos_version(&string).context("failed to parse version number (macos variant)") } else if cfg!(target_os = "linux") { - Ok(parse_linux_version(&string).ok_or("failed to parse version number (linux variant)")?) + parse_linux_version(&string).context("failed to parse version number (linux variant)") } else { - Err(Error::msg("Unsupported platform")) + Err(anyhow::Error::msg("Unsupported platform")) } } /// Invoke `curl` to download an archive (ZIP on windows, TAR on linux and mac) /// from the latest published release online. -pub fn download_ffmpeg_package(url: &str, download_dir: &Path) -> Result { +pub fn download_ffmpeg_package(url: &str, download_dir: &Path) -> anyhow::Result { let filename = Path::new(url) .file_name() - .ok_or_else(|| Error::msg("Failed to get filename"))?; + .context("Failed to get filename")?; let archive_path = download_dir.join(filename); - let archive_filename = archive_path.to_str().ok_or("invalid download path")?; + let archive_filename = archive_path + .to_str() + .context("invalid download path")?; let exit_status = curl_to_file(url, archive_filename)?; if !exit_status.success() { - return Err(Error::msg("Failed to download ffmpeg")); + anyhow::bail!("Failed to download ffmpeg"); } Ok(archive_path) @@ -179,7 +174,7 @@ pub fn download_ffmpeg_package(url: &str, download_dir: &Path) -> Result Result<()> { +pub fn unpack_ffmpeg(from_archive: &PathBuf, binary_folder: &Path) -> anyhow::Result<()> { let temp_dirname = UNPACK_DIRNAME; let temp_folder = binary_folder.join(temp_dirname); create_dir_all(&temp_folder)?; @@ -192,13 +187,13 @@ pub fn unpack_ffmpeg(from_archive: &PathBuf, binary_folder: &Path) -> Result<()> .status()? .success() .then_some(()) - .ok_or("Failed to unpack ffmpeg")?; + .context("Failed to unpack ffmpeg")?; // Move binaries let (ffmpeg, ffplay, ffprobe) = if cfg!(target_os = "windows") { let inner_folder = read_dir(&temp_folder)? .next() - .ok_or("Failed to get inner folder")??; + .context("Failed to get inner folder")??; ( inner_folder.path().join("bin/ffmpeg.exe"), inner_folder.path().join("bin/ffplay.exe"), @@ -207,7 +202,7 @@ pub fn unpack_ffmpeg(from_archive: &PathBuf, binary_folder: &Path) -> Result<()> } else if cfg!(target_os = "linux") { let inner_folder = read_dir(&temp_folder)? .next() - .ok_or("Failed to get inner folder")??; + .context("Failed to get inner folder")??; ( inner_folder.path().join("./ffmpeg"), inner_folder.path().join("./ffplay"), // <- no ffplay on linux @@ -220,18 +215,28 @@ pub fn unpack_ffmpeg(from_archive: &PathBuf, binary_folder: &Path) -> Result<()> temp_folder.join("ffprobe"), // <-- no ffprobe on mac ) } else { - return Err(Error::msg("Unsupported platform")); + anyhow::bail!("Unsupported platform"); }; // Move binaries - rename(&ffmpeg, binary_folder.join(ffmpeg.file_name().ok_or(())?))?; + let move_bin = |path: &Path| { + let file_name = binary_folder.join( + path + .file_name() + .with_context(|| format!("Path {} does not have a file_name", path.to_string_lossy()))?, + ); + rename(path, file_name)?; + anyhow::Ok(()) + }; + + move_bin(&ffmpeg)?; if ffprobe.exists() { - rename(&ffprobe, binary_folder.join(ffprobe.file_name().ok_or(())?))?; + move_bin(&ffprobe)?; } if ffplay.exists() { - rename(&ffplay, binary_folder.join(ffplay.file_name().ok_or(())?))?; + move_bin(&ffplay)?; } // Delete archive and unpacked files diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 8b1e90f..0000000 --- a/src/error.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::error::Error as StdError; -use std::fmt::{Display, Formatter}; -use std::io; -use std::result::Result as StdResult; -use std::str::Utf8Error; -use std::string::FromUtf8Error; - -/// Shorthand alias for `Result` using `ffmpeg_sidecar` error type. -pub type Result = StdResult; - -/// A generic error type for the `ffmpeg-sidecar` crate. -#[derive(Debug)] -pub struct Error { - pub message: String, - pub source: Option>, -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.message) - } -} - -impl StdError for Error { - fn source(&self) -> Option<&(dyn StdError + 'static)> { - self.source.as_deref() - } -} - -impl Error { - /// Wrap any standard Error into a library Error. - /// Similar to [`anyhow`](https://github.com/dtolnay/anyhow/blob/master/src/error.rs#L88). - pub fn from_std(e: E) -> Self - where - E: StdError + 'static, - { - Error { - message: e.to_string(), - source: Some(Box::new(e)), - } - } - - /// Wrap any Display into a library Error. - pub fn from_display(e: E) -> Self - where - E: Display, - { - Error { - message: e.to_string(), - source: None, - } - } - - /// Create an error message from a string. - pub fn msg>(message: S) -> Self { - Error { - message: message.as_ref().to_string(), - source: None, - } - } -} - -impl From for Error { - fn from(e: io::Error) -> Self { - Error::from_std(e) - } -} - -impl From for Error { - fn from(e: Utf8Error) -> Self { - Error::from_std(e) - } -} - -impl From for Error { - fn from(e: FromUtf8Error) -> Self { - Error::from_std(e) - } -} - -impl From<&str> for Error { - fn from(e: &str) -> Self { - Error::from_display(e) - } -} - -impl From for Error { - fn from(e: String) -> Self { - Error::from_display(e) - } -} - -impl From<()> for Error { - fn from(_: ()) -> Self { - Error::from_display("empty error") - } -} diff --git a/src/ffprobe.rs b/src/ffprobe.rs index 2ccccc6..4ff2fbd 100644 --- a/src/ffprobe.rs +++ b/src/ffprobe.rs @@ -1,10 +1,11 @@ -use crate::error::{Error, Result}; use std::{env::current_exe, ffi::OsStr, path::PathBuf}; use std::{ path::Path, process::{Command, Stdio}, }; +use anyhow::Context; + /// Returns the path of the downloaded FFprobe executable, or falls back to /// assuming its installed in the system path. Note that not all FFmpeg /// distributions include FFprobe. @@ -23,10 +24,10 @@ pub fn ffprobe_path() -> PathBuf { /// /// The extension between platforms, with Windows using `.exe`, while Mac and /// Linux have no extension. -pub fn ffprobe_sidecar_path() -> Result { +pub fn ffprobe_sidecar_path() -> anyhow::Result { let mut path = current_exe()? .parent() - .ok_or("Can't get parent of current_exe")? + .context("Can't get parent of current_exe")? .join("ffprobe"); if cfg!(windows) { path.set_extension("exe"); @@ -35,18 +36,18 @@ pub fn ffprobe_sidecar_path() -> Result { } /// Alias for `ffprobe -version`, parsing the version number and returning it. -pub fn ffprobe_version() -> Result { +pub fn ffprobe_version() -> anyhow::Result { ffprobe_version_with_path(ffprobe_path()) } /// Lower level variant of `ffprobe_version` that exposes a customized the path /// to the ffmpeg binary. -pub fn ffprobe_version_with_path>(path: S) -> Result { +pub fn ffprobe_version_with_path>(path: S) -> anyhow::Result { let output = Command::new(&path).arg("-version").output()?; // note:version parsing is not implemented for ffprobe - String::from_utf8(output.stdout).map_err(Error::from) + Ok(String::from_utf8(output.stdout)?) } /// Verify whether ffprobe is installed on the system. This will return true if diff --git a/src/iter.rs b/src/iter.rs index a771cb0..db0a889 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -5,9 +5,10 @@ use std::{ thread::JoinHandle, }; +use anyhow::Context; + use crate::{ child::FfmpegChild, - error::{Error, Result}, event::{AVStream, FfmpegEvent, FfmpegOutput, FfmpegProgress, LogLevel, OutputVideoFrame}, log_parser::FfmpegLogParser, metadata::FfmpegMetadata, @@ -23,8 +24,8 @@ pub struct FfmpegIterator { } impl FfmpegIterator { - pub fn new(child: &mut FfmpegChild) -> Result { - let stderr = child.take_stderr().ok_or_else(|| Error::msg("No stderr channel\n - Did you call `take_stderr` elsewhere?\n - Did you forget to call `.stderr(Stdio::piped)` on the `ChildProcess`?"))?; + pub fn new(child: &mut FfmpegChild) -> anyhow::Result { + let stderr = child.take_stderr().context("No stderr channel\n - Did you call `take_stderr` elsewhere?\n - Did you forget to call `.stderr(Stdio::piped)` on the `ChildProcess`?")?; let (tx, rx) = sync_channel::(0); spawn_stderr_thread(stderr, tx.clone()); let stdout = child.take_stdout(); @@ -40,19 +41,19 @@ impl FfmpegIterator { /// Called after all metadata has been obtained to spawn the thread that will /// handle output. The metadata is needed to determine the output format and /// other parameters. - fn start_stdout(&mut self) -> Result<()> { + fn start_stdout(&mut self) -> anyhow::Result<()> { // No output detected if self.metadata.output_streams.is_empty() || self.metadata.outputs.is_empty() { - let err = Error::msg("No output streams found"); + let err = "No output streams found"; self.tx.take(); // drop the tx so that the channel closes - return Err(err); + anyhow::bail!(err) } // Handle stdout if let Some(stdout) = self.stdout.take() { spawn_stdout_thread( stdout, - self.tx.take().ok_or("missing channel tx")?, + self.tx.take().context("missing channel tx")?, self.metadata.output_streams.clone(), self.metadata.outputs.clone(), ); @@ -62,7 +63,7 @@ impl FfmpegIterator { } /// Advance the iterator until all metadata has been collected, returning it. - pub fn collect_metadata(&mut self) -> Result { + pub fn collect_metadata(&mut self) -> anyhow::Result { let mut event_queue: Vec = Vec::new(); while !self.metadata.is_completed() { @@ -78,11 +79,10 @@ impl FfmpegIterator { }) .collect::>() .join(""); - let msg = format!( - "Iterator ran out before metadata was gathered. The following errors occurred: {}", - errors - ); - return Err(Error::msg(msg)); + + anyhow::bail!( + "Iterator ran out before metadata was gathered. The following errors occurred: {errors}", + ) } } } diff --git a/src/lib.rs b/src/lib.rs index b8d09e7..170b0a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,9 +3,9 @@ //! ## Example //! //! ```rust -//! use ffmpeg_sidecar::{command::FfmpegCommand, error::Result, event::FfmpegEvent}; +//! use ffmpeg_sidecar::{command::FfmpegCommand, event::FfmpegEvent}; //! -//! fn main() -> Result<()> { +//! fn main() -> anyhow::Result<()> { //! FfmpegCommand::new() // <- Builder API like `std::process::Command` //! .testsrc() // <- Discoverable aliases for FFmpeg args //! .rawvideo() // <- Convenient argument presets @@ -38,7 +38,6 @@ pub mod child; pub mod comma_iter; pub mod command; pub mod download; -pub mod error; pub mod event; pub mod ffprobe; pub mod iter; diff --git a/src/log_parser.rs b/src/log_parser.rs index 1ea6aa8..6698636 100644 --- a/src/log_parser.rs +++ b/src/log_parser.rs @@ -5,7 +5,6 @@ use std::{ use crate::{ comma_iter::CommaIter, - error::{Error, Result}, event::{ AVStream, FfmpegConfiguration, FfmpegDuration, FfmpegEvent, FfmpegInput, FfmpegOutput, FfmpegProgress, FfmpegVersion, LogLevel, @@ -38,14 +37,14 @@ impl FfmpegLogParser { /// - `\n` (MacOS), /// - `\r\n` (Windows) /// - `\r` (Windows, progress updates which overwrite the previous line) - pub fn parse_next_event(&mut self) -> Result { + pub fn parse_next_event(&mut self) -> anyhow::Result { let mut buf = Vec::::new(); let bytes_read = read_until_any(&mut self.reader, &[b'\r', b'\n'], &mut buf); let line = from_utf8(buf.as_slice())?.trim(); let raw_log_message = line.to_string(); - match bytes_read { - Ok(0) => Ok(FfmpegEvent::LogEOF), - Ok(_) => { + match bytes_read? { + 0 => Ok(FfmpegEvent::LogEOF), + _ => { // Track log section if let Some(input_number) = try_parse_input(line) { self.cur_section = LogSection::Input(input_number); @@ -87,7 +86,7 @@ impl FfmpegLogParser { match self.cur_section { LogSection::Input(_) => Ok(FfmpegEvent::ParsedInputStream(stream)), LogSection::Output(_) => Ok(FfmpegEvent::ParsedOutputStream(stream)), - LogSection::Other | LogSection::StreamMapping => Err(Error::msg(format!( + LogSection::Other | LogSection::StreamMapping => Err(anyhow::Error::msg(format!( "Unexpected stream specification: {}", line ))), @@ -107,7 +106,6 @@ impl FfmpegLogParser { Ok(FfmpegEvent::Log(LogLevel::Unknown, line.to_string())) } } - Err(e) => Err(Error::from_std(e)), } } diff --git a/src/metadata.rs b/src/metadata.rs index 6eeca7b..8a5bb08 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,4 +1,3 @@ -use crate::error::Result; use crate::event::{AVStream, FfmpegEvent, FfmpegInput, FfmpegOutput}; #[derive(Debug, Clone, PartialEq)] @@ -44,9 +43,9 @@ impl FfmpegMetadata { self.inputs[0].duration } - pub fn handle_event(&mut self, item: &Option) -> Result<()> { + pub fn handle_event(&mut self, item: &Option) -> anyhow::Result<()> { if self.is_completed() { - return Err("Metadata is already completed".into()); + anyhow::bail!("Metadata is already completed") } match item { diff --git a/src/paths.rs b/src/paths.rs index 0721584..c36adfb 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -1,9 +1,10 @@ -use crate::error::Result; use std::{ env::current_exe, path::{Path, PathBuf}, }; +use anyhow::Context; + /// Returns the default path of the FFmpeg executable, to be used as the /// argument to `Command::new`. It should first attempt to locate an FFmpeg /// binary adjacent to the Rust executable. If that fails, it should invoke @@ -25,10 +26,10 @@ pub fn ffmpeg_path() -> PathBuf { /// /// The extension between platforms, with Windows using `.exe`, while Mac and /// Linux have no extension. -pub fn sidecar_path() -> Result { +pub fn sidecar_path() -> anyhow::Result { let mut path = current_exe()? .parent() - .ok_or("Can't get parent of current_exe")? + .context("Can't get parent of current_exe")? .join("ffmpeg"); if cfg!(windows) { path.set_extension("exe"); @@ -37,11 +38,11 @@ pub fn sidecar_path() -> Result { } /// By default, downloads all temporary files to the same directory as the Rust executable. -pub fn sidecar_dir() -> Result { +pub fn sidecar_dir() -> anyhow::Result { Ok( sidecar_path()? .parent() - .ok_or("invalid sidecar path")? + .context("invalid sidecar path")? .to_path_buf(), ) } diff --git a/src/version.rs b/src/version.rs index 4066fe6..0216f67 100644 --- a/src/version.rs +++ b/src/version.rs @@ -1,5 +1,6 @@ +use anyhow::Context; + use crate::{ - error::{Error, Result}, event::FfmpegEvent, log_parser::FfmpegLogParser, paths::ffmpeg_path, @@ -8,18 +9,18 @@ use std::ffi::OsStr; use std::process::{Command, Stdio}; /// Alias for `ffmpeg -version`, parsing the version number and returning it. -pub fn ffmpeg_version() -> Result { +pub fn ffmpeg_version() -> anyhow::Result { ffmpeg_version_with_path(ffmpeg_path()) } /// Lower level variant of `ffmpeg_version` that exposes a customized the path /// to the ffmpeg binary. -pub fn ffmpeg_version_with_path>(path: S) -> Result { +pub fn ffmpeg_version_with_path>(path: S) -> anyhow::Result { let mut cmd = Command::new(&path) .arg("-version") .stdout(Stdio::piped()) // not stderr when calling `-version` .spawn()?; - let stdout = cmd.stdout.take().ok_or("No standard output channel")?; + let stdout = cmd.stdout.take().context("No standard output channel")?; let mut parser = FfmpegLogParser::new(stdout); let mut version: Option = None; @@ -32,7 +33,7 @@ pub fn ffmpeg_version_with_path>(path: S) -> Result { } let exit_status = cmd.wait()?; if !exit_status.success() { - return Err(Error::msg("ffmpeg -version exited with non-zero status")); + anyhow::bail!("ffmpeg -version exited with non-zero status"); } - version.ok_or_else(|| Error::msg("Failed to parse ffmpeg version")) + version.context("Failed to parse ffmpeg version") }