From a3115b08a00923c3cd84363f9849c2348159b882 Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Sun, 18 Jun 2023 01:55:00 +0200 Subject: [PATCH] improve Error handling by providing better errors --- Cargo.toml | 5 ++- src/lib.rs | 96 ++++++++++++++++++++++++++---------------------------- 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1fef104..e99ff0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "twitch_data" -version = "0.2.3" +version = "0.2.4" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -20,6 +20,9 @@ twitch_oauth2 = "0.12.4" serde = "1.0.152" serde_json = "1.0.91" +thiserror = "1.0" +anyhow = "1.0" + chrono = "0.4" indicatif = "0.17" futures = "0.3" diff --git a/src/lib.rs b/src/lib.rs index 77fdbf5..ce2b878 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,57 +1,54 @@ -use std::collections::HashMap; -use std::error::Error; -use std::fmt; -use std::fmt::{Debug, Display, Formatter}; -use std::path::{Path, PathBuf}; - +#[allow(unused, dead_code)] +//^^ hides some warnings while developing TODO: remove at release +use crate::prelude::*; use chrono::{DateTime, Utc}; use downloader_config::load_config; use exponential_backoff::twitch::{ check_backoff_twitch_get, check_backoff_twitch_get_with_client, check_backoff_twitch_with_client, }; - use futures::StreamExt; -use reqwest; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::error::Error as StdError; +use std::fmt::Debug; +use std::path::{Path, PathBuf}; +use std::result::Result as StdResult; +use thiserror::Error; use tokio::fs::File; use tokio::io::AsyncWriteExt; use tokio::process::Command; +use tokio::time::Duration; use tokio::time::Instant; -use twitch_api; use twitch_api::helix::channels::ChannelInformation; use twitch_api::helix::videos::Video as TwitchVideo; use twitch_api::types::{Timestamp, VideoPrivacy}; use twitch_oauth2::{ClientId, ClientSecret}; -pub use twitch_types::{UserId, VideoId}; -use crate::prelude::*; +pub use twitch_types::{UserId, VideoId}; pub mod prelude; //region DownloadError -#[derive(Debug, Clone)] -pub struct DownloadError { - message: String, +#[derive(Debug, Error)] +pub enum DownloadError { + #[error("Error while downloading all parts")] + DownloadAllParts, + #[error("Error with the canonicalization of the path")] + Canonicalization, + #[error("No parts found")] + NoParts, + #[error("No channel info found")] + NoChannelFound, + #[error("Error while running ffmpeg command after {0:?}: {1}")] + Ffmpeg(Duration, anyhow::Error), + #[error(transparent)] + Other(#[from] Box), } -impl Display for DownloadError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}: {}", stringify!(DownloadError), self.message) - } -} - -impl Error for DownloadError {} - -impl DownloadError { - pub fn new>(message: S) -> DownloadError { - let message = message.into(); - DownloadError { message } - } -} //endregion -type Result = std::result::Result>; +type Result = std::result::Result>; //region Proxies #[derive(Debug)] @@ -167,7 +164,7 @@ impl<'a> TwitchClient<'a> { if let Some(channel_info) = result { Ok(channel_info.title.clone()) } else { - Err("No channel info found".into()) + Err(Box::new(DownloadError::NoChannelFound)) } } @@ -447,23 +444,28 @@ impl<'a> TwitchClient<'a> { .await?; info!("downloading all parts of video: {}", url); - let mut files = self.download_all_parts(&url, &folder_path).await?; + let files = self.download_all_parts(&url, &folder_path).await?; info!("downloaded all parts of video: {}", files.len()); //combine parts - for file in files.iter_mut() { - let file = file; - if file.is_some() { - let x = file.as_mut().unwrap(); - let y = x.canonicalize()?; - *x = y; - } else { - return Err(DownloadError::new("Error while downloading all parts").into()); - } - } - - let mut files: Vec = files.into_iter().map(|f| f.unwrap()).collect(); + let mut files = files + .into_iter() + .enumerate() + .map(|(i, file)| { + trace!("file: {} {:?}", i, file.as_ref().map(|x| x.display())); + if let Some(file) = file { + let file = file.canonicalize(); + if let Ok(file) = file { + Ok(file) + } else { + Err(DownloadError::Canonicalization) + } + } else { + Err(DownloadError::DownloadAllParts) + } + }) + .collect::, DownloadError>>()?; sort_video_part_filenames(&video_id, &mut files); @@ -499,7 +501,7 @@ impl<'a> TwitchClient<'a> { let amount_of_parts = amount_of_parts as u64; info!("part count: {}", amount_of_parts); if amount_of_parts < 1 { - return Err(DownloadError::new("No parts found").into()); + return Err(Box::new(DownloadError::NoParts)); } //download parts @@ -699,11 +701,7 @@ pub async fn convert_ts_to_mp4(video_mp4: &PathBuf, video_ts: &PathBuf) -> Resul //stop the time how long it takes to convert let duration = Instant::now().duration_since(convert_start_time); if let Err(e) = result { - error!( - "Error while running ffmpeg command after {:?}: {}", - duration, e - ); - return Err(e.into()); + return Err(Box::new(DownloadError::Ffmpeg(duration, e.into()))); } debug!("ffmpeg command finished"); info!("duration: {:?}", duration);