diff --git a/Cargo.lock b/Cargo.lock index b73576e..6cb6068 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1361,7 +1361,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -1470,6 +1470,7 @@ dependencies = [ "reqwest", "rodio", "scraper", + "thiserror 2.0.12", "tokio", "unicode-segmentation", "url", @@ -1593,7 +1594,7 @@ dependencies = [ "log", "ndk-sys", "num_enum", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2018,7 +2019,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2607,7 +2608,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -2621,6 +2631,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinystr" version = "0.7.6" diff --git a/Cargo.toml b/Cargo.toml index 2d9e191..a474669 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,3 +51,4 @@ lazy_static = "1.5.0" libc = "0.2.167" url = "2.5.4" unicode-segmentation = "1.12.0" +thiserror = "2.0.12" diff --git a/src/player.rs b/src/player.rs index 1a9cb4f..670e03d 100644 --- a/src/player.rs +++ b/src/player.rs @@ -31,7 +31,7 @@ use mpris_server::{PlaybackStatus, PlayerInterface, Property}; use crate::{ messages::Messages, play::{PersistentVolume, SendableOutputStream}, - tracks::{self, bookmark, list::List, TrackError}, + tracks::{self, bookmark, list::List}, Args, }; @@ -209,7 +209,7 @@ impl Player { self.list.random(&self.client).await? }; - let decoded = track.decode().map_err(|x| TrackError::new_eyre(false, x))?; + let decoded = track.decode()?; // Set the current track. self.set_current(decoded.info.clone()); @@ -249,7 +249,7 @@ impl Player { tx.send(Messages::NewSong).await?; } Err(error) => { - if !error.timeout { + if !error.is_timeout() { if debug { panic!("{:?}", error) } diff --git a/src/player/downloader.rs b/src/player/downloader.rs index 4a1a450..48f86a2 100644 --- a/src/player/downloader.rs +++ b/src/player/downloader.rs @@ -62,15 +62,14 @@ impl Downloader { let data = self.player.list.random(&self.player.client).await; match data { Ok(track) => self.player.tracks.write().await.push_back(track), - Err(error) => { - if !error.timeout { - if debug { - panic!("{}", error) - } - - sleep(TIMEOUT).await; + Err(error) if !error.is_timeout() => { + if debug { + panic!("{}", error) } + + sleep(TIMEOUT).await; } + _ => {} } } } diff --git a/src/tracks.rs b/src/tracks.rs index cceb860..8ef9f29 100644 --- a/src/tracks.rs +++ b/src/tracks.rs @@ -15,18 +15,46 @@ //! 2. [`Info`] created from decoded data. //! 3. [`Decoded`] made from [`Info`] and the original decoded data. -use std::{error::Error, io::Cursor, time::Duration}; +use std::{io::Cursor, time::Duration}; use bytes::Bytes; -use eyre::OptionExt as _; use inflector::Inflector as _; use rodio::{Decoder, Source as _}; +use thiserror::Error; +use tokio::io; use unicode_segmentation::UnicodeSegmentation; use url::form_urlencoded; pub mod bookmark; pub mod list; +#[derive(Debug, Error)] +pub enum TrackError { + #[error("timeout")] + Timeout, + + #[error("unable to decode")] + Decode(#[from] rodio::decoder::DecoderError), + + #[error("invalid name")] + InvalidName, + + #[error("invalid file path")] + InvalidPath, + + #[error("unable to read file")] + File(#[from] io::Error), + + #[error("unable to fetch data")] + Request(#[from] reqwest::Error), +} + +impl TrackError { + pub fn is_timeout(&self) -> bool { + return matches!(self, TrackError::Timeout); + } +} + /// Just a shorthand for a decoded [Bytes]. pub type DecodedData = Decoder>; @@ -61,7 +89,7 @@ impl Track { /// This will actually decode and format the track, /// returning a [`DecodedTrack`] which can be played /// and also has a duration & formatted name. - pub fn decode(self) -> eyre::Result { + pub fn decode(self) -> eyre::Result { Decoded::new(self) } } @@ -105,11 +133,8 @@ impl Info { /// Formats a name with [Inflector]. /// This will also strip the first few numbers that are /// usually present on most lofi tracks. - fn format_name(name: &str) -> eyre::Result { - let split = name - .split('/') - .last() - .ok_or_eyre("split is never supposed to return nothing")?; + fn format_name(name: &str) -> eyre::Result { + let split = name.split('/').last().ok_or(TrackError::InvalidName)?; let stripped = split.strip_suffix(".mp3").unwrap_or(split); let formatted = Self::decode_url(stripped) @@ -150,7 +175,11 @@ impl Info { } /// Creates a new [`TrackInfo`] from a possibly raw name & decoded data. - pub fn new(name: TrackName, full_path: String, decoded: &DecodedData) -> eyre::Result { + pub fn new( + name: TrackName, + full_path: String, + decoded: &DecodedData, + ) -> eyre::Result { let (display_name, custom_name) = match name { TrackName::Raw(raw) => (Self::format_name(&raw)?, false), TrackName::Formatted(custom) => (custom, true), @@ -179,55 +208,10 @@ pub struct Decoded { impl Decoded { /// Creates a new track. /// This is equivalent to [`Track::decode`]. - pub fn new(track: Track) -> eyre::Result { + pub fn new(track: Track) -> eyre::Result { let data = Decoder::new(Cursor::new(track.data))?; let info = Info::new(track.name, track.full_path, &data)?; Ok(Self { info, data }) } } - -#[derive(Debug)] -pub struct TrackError { - pub timeout: bool, - inner: Option, -} - -impl<'a> std::fmt::Display for TrackError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "TrackError (timeout: {}): {:?}", - self.timeout, self.inner - ) - } -} - -impl<'a> std::error::Error for TrackError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - self.inner.as_ref().map(|e| e.as_ref()) - } -} - -impl TrackError { - pub fn new(timeout: bool, inner: T) -> Self { - Self { - inner: Some(eyre::eyre!(inner)), - timeout, - } - } - - pub fn new_eyre(timeout: bool, inner: eyre::Error) -> Self { - Self { - inner: Some(inner), - timeout, - } - } - - pub fn empty(timeout: bool) -> Self { - Self { - inner: None, - timeout, - } - } -} diff --git a/src/tracks/list.rs b/src/tracks/list.rs index 02a73e0..181a151 100644 --- a/src/tracks/list.rs +++ b/src/tracks/list.rs @@ -62,28 +62,28 @@ impl List { let data: Bytes = if let Some(x) = full_path.strip_prefix("file://") { let path = if x.starts_with("~") { - let home_path = dirs::home_dir().ok_or(TrackError::empty(false))?; - let home = home_path.to_str().ok_or(TrackError::empty(false))?; + let home_path = dirs::home_dir().ok_or(TrackError::InvalidPath)?; + let home = home_path.to_str().ok_or(TrackError::InvalidPath)?; x.replace("~", home) } else { x.to_owned() }; - let result = tokio::fs::read(path) - .await - .map_err(|x| TrackError::new(false, x))?; + let result = tokio::fs::read(path).await?; result.into() } else { - let response = client - .get(full_path.clone()) - .send() - .await - .map_err(|x| TrackError::new(x.is_timeout(), x))?; - response - .bytes() - .await - .map_err(|x| TrackError::new(false, x))? + let response = match client.get(full_path.clone()).send().await { + Ok(x) => Ok(x), + Err(x) => { + if x.is_timeout() { + Err(TrackError::Timeout) + } else { + Err(TrackError::Request(x)) + } + } + }?; + response.bytes().await? }; Ok((data, full_path))