diff --git a/src/app.rs b/src/app.rs index e34bfb9..08653bb 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,7 +1,7 @@ use std::fs; use std::path::PathBuf; -use crate::db::{self, DB}; +use crate::db::DB; use crate::paths::Paths; pub mod config; @@ -22,15 +22,105 @@ pub mod test; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] - Collection(#[from] index::Error), + ThreadPoolBuilder(#[from] rayon::ThreadPoolBuildError), #[error(transparent)] - Config(#[from] config::Error), - #[error(transparent)] - Database(#[from] db::Error), + ThreadJoining(#[from] tokio::task::JoinError), + #[error("Filesystem error for `{0}`: `{1}`")] Io(PathBuf, std::io::Error), #[error(transparent)] - Settings(#[from] settings::Error), + Ape(#[from] ape::Error), + #[error("ID3 error in `{0}`: `{1}`")] + Id3(PathBuf, id3::Error), + #[error("Metaflac error in `{0}`: `{1}`")] + Metaflac(PathBuf, metaflac::Error), + #[error("Mp4aMeta error in `{0}`: `{1}`")] + Mp4aMeta(PathBuf, mp4ameta::Error), + #[error(transparent)] + Opus(#[from] opus_headers::ParseError), + #[error(transparent)] + Vorbis(#[from] lewton::VorbisError), + #[error("Could not find a Vorbis comment within flac file")] + VorbisCommentNotFoundInFlacFile, + #[error("Could not read thumbnail image in `{0}`:\n\n{1}")] + Image(PathBuf, image::error::ImageError), + #[error("This file format is not supported: {0}")] + UnsupportedFormat(&'static str), + + #[error(transparent)] + Database(#[from] sqlx::Error), + #[error("Could not initialize database connection pool")] + ConnectionPoolBuild, + #[error("Could not acquire database connection from pool")] + ConnectionPool, + #[error("Could not apply database migrations: {0}")] + Migration(sqlx::migrate::MigrateError), + + #[error("DDNS update query failed with HTTP status code `{0}`")] + UpdateQueryFailed(u16), + #[error("DDNS update query failed due to a transport error")] + UpdateQueryTransport, + + #[error("Auth secret does not have the expected format")] + AuthenticationSecretInvalid, + #[error("Missing auth secret")] + AuthenticationSecretNotFound, + #[error("Missing settings")] + MiscSettingsNotFound, + #[error("Index album art pattern is not a valid regex")] + IndexAlbumArtPatternInvalid, + + #[error(transparent)] + Toml(#[from] toml::de::Error), + #[error("Could not deserialize collection")] + IndexDeserializationError, + #[error("Could not serialize collection")] + IndexSerializationError, + + #[error("The following virtual path could not be mapped to a real path: `{0}`")] + CouldNotMapToRealPath(PathBuf), + #[error("User not found")] + UserNotFound, + #[error("Directory not found: {0}")] + DirectoryNotFound(PathBuf), + #[error("Artist not found")] + ArtistNotFound, + #[error("Album not found")] + AlbumNotFound, + #[error("Song not found")] + SongNotFound, + #[error("Playlist not found")] + PlaylistNotFound, + #[error("No embedded artwork was found in `{0}`")] + EmbeddedArtworkNotFound(PathBuf), + + #[error("Cannot use empty username")] + EmptyUsername, + #[error("Cannot use empty password")] + EmptyPassword, + #[error("Username does not exist")] + IncorrectUsername, + #[error("Password does not match username")] + IncorrectPassword, + #[error("Invalid auth token")] + InvalidAuthToken, + #[error("Incorrect authorization scope")] + IncorrectAuthorizationScope, + #[error("Last.fm session key is missing")] + MissingLastFMSessionKey, + #[error("Failed to hash password")] + PasswordHashing, + #[error("Failed to encode authorization token")] + AuthorizationTokenEncoding, + #[error("Failed to encode Branca token")] + BrancaTokenEncoding, + + #[error("Failed to authenticate with last.fm")] + ScrobblerAuthentication(rustfm_scrobble::ScrobblerError), + #[error("Failed to emit last.fm scrobble")] + Scrobble(rustfm_scrobble::ScrobblerError), + #[error("Failed to emit last.fm now playing update")] + NowPlaying(rustfm_scrobble::ScrobblerError), } #[derive(Clone)] diff --git a/src/app/config.rs b/src/app/config.rs index d527ee4..d0e9ff2 100644 --- a/src/app/config.rs +++ b/src/app/config.rs @@ -1,24 +1,7 @@ use serde::Deserialize; -use std::io::Read; -use std::path::{Path, PathBuf}; +use std::{io::Read, path::Path}; -use crate::app::{ddns, settings, user, vfs}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Ddns(#[from] ddns::Error), - #[error("Filesystem error for `{0}`: `{1}`")] - Io(PathBuf, std::io::Error), - #[error(transparent)] - Settings(#[from] settings::Error), - #[error(transparent)] - Toml(#[from] toml::de::Error), - #[error(transparent)] - User(#[from] user::Error), - #[error(transparent)] - Vfs(#[from] vfs::Error), -} +use crate::app::{ddns, settings, user, vfs, Error}; #[derive(Default, Deserialize)] pub struct Config { diff --git a/src/app/ddns.rs b/src/app/ddns.rs index ba08099..fe297dc 100644 --- a/src/app/ddns.rs +++ b/src/app/ddns.rs @@ -3,22 +3,11 @@ use log::{debug, error}; use serde::{Deserialize, Serialize}; use std::time::Duration; -use crate::db::{self, DB}; +use crate::app::Error; +use crate::db::DB; const DDNS_UPDATE_URL: &str = "https://ydns.io/api/v1/update/"; -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("DDNS update query failed with HTTP status code `{0}`")] - UpdateQueryFailed(u16), - #[error("DDNS update query failed due to a transport error")] - UpdateQueryTransport, - #[error(transparent)] - DatabaseConnection(#[from] db::Error), - #[error(transparent)] - Database(#[from] sqlx::Error), -} - #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct Config { pub ddns_host: String, diff --git a/src/app/formats.rs b/src/app/formats.rs index f14506b..1e8b43f 100644 --- a/src/app/formats.rs +++ b/src/app/formats.rs @@ -2,31 +2,12 @@ use id3::TagLike; use lewton::inside_ogg::OggStreamReader; use log::error; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; +use crate::app::Error; use crate::utils; use crate::utils::AudioFormat; -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Ape(#[from] ape::Error), - #[error(transparent)] - Id3(#[from] id3::Error), - #[error("Filesystem error for `{0}`: `{1}`")] - Io(PathBuf, std::io::Error), - #[error(transparent)] - Metaflac(#[from] metaflac::Error), - #[error(transparent)] - Mp4aMeta(#[from] mp4ameta::Error), - #[error(transparent)] - Opus(#[from] opus_headers::ParseError), - #[error(transparent)] - Vorbis(#[from] lewton::VorbisError), - #[error("Could not find a Vorbis comment within flac file")] - VorbisCommentNotFoundInFlacFile, -} - #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct SongMetadata { pub disc_number: Option, @@ -83,13 +64,15 @@ impl ID3Ext for id3::Tag { } fn read_id3>(path: P) -> Result { - let tag = id3::Tag::read_from_path(path).or_else(|error| { - if let Some(tag) = error.partial_tag { - Ok(tag) - } else { - Err(error) - } - })?; + let tag = id3::Tag::read_from_path(&path) + .or_else(|error| { + if let Some(tag) = error.partial_tag { + Ok(tag) + } else { + Err(error) + } + }) + .map_err(|e| Error::Id3(path.as_ref().to_owned(), e))?; let artists = tag.get_text_values("TPE1"); let album_artists = tag.get_text_values("TPE2"); @@ -255,7 +238,8 @@ fn read_opus>(path: P) -> Result { } fn read_flac>(path: P) -> Result { - let tag = metaflac::Tag::read_from_path(path)?; + let tag = metaflac::Tag::read_from_path(&path) + .map_err(|e| Error::Metaflac(path.as_ref().to_owned(), e))?; let vorbis = tag .vorbis_comments() .ok_or(Error::VorbisCommentNotFoundInFlacFile)?; @@ -290,7 +274,8 @@ fn read_flac>(path: P) -> Result { } fn read_mp4>(path: P) -> Result { - let mut tag = mp4ameta::Tag::read_from_path(path)?; + let mut tag = mp4ameta::Tag::read_from_path(&path) + .map_err(|e| Error::Mp4aMeta(path.as_ref().to_owned(), e))?; let label_ident = mp4ameta::FreeformIdent::new("com.apple.iTunes", "Label"); Ok(SongMetadata { diff --git a/src/app/index.rs b/src/app/index.rs index bf2bb0d..19cb2d2 100644 --- a/src/app/index.rs +++ b/src/app/index.rs @@ -7,9 +7,8 @@ use log::{error, info}; use serde::{Deserialize, Serialize}; use tokio::task::spawn_blocking; -use crate::app::scanner; -use crate::app::vfs; -use crate::db::{self, DB}; +use crate::app::{scanner, Error}; +use crate::db::DB; mod browser; mod collection; @@ -18,32 +17,6 @@ mod search; pub use browser::File; pub use collection::{Album, AlbumKey, Artist, ArtistKey, Song, SongKey}; -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Directory not found: {0}")] - DirectoryNotFound(PathBuf), - #[error("Artist not found")] - ArtistNotFound, - #[error("Album not found")] - AlbumNotFound, - #[error("Song not found")] - SongNotFound, - #[error(transparent)] - Database(#[from] sqlx::Error), - #[error(transparent)] - DatabaseConnection(#[from] db::Error), - #[error(transparent)] - Vfs(#[from] vfs::Error), - #[error("Could not deserialize collection")] - IndexDeserializationError, - #[error("Could not serialize collection")] - IndexSerializationError, - #[error(transparent)] - ThreadPoolBuilder(#[from] rayon::ThreadPoolBuildError), - #[error(transparent)] - ThreadJoining(#[from] tokio::task::JoinError), -} - #[derive(Clone)] pub struct Manager { db: DB, diff --git a/src/app/index/browser.rs b/src/app/index/browser.rs index cbc332d..af62ee4 100644 --- a/src/app/index/browser.rs +++ b/src/app/index/browser.rs @@ -7,7 +7,7 @@ use std::{ use serde::{Deserialize, Serialize}; use trie_rs::{Trie, TrieBuilder}; -use crate::app::{index::Error, scanner}; +use crate::app::{scanner, Error}; #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum File { diff --git a/src/app/lastfm.rs b/src/app/lastfm.rs index a602877..edeaa5a 100644 --- a/src/app/lastfm.rs +++ b/src/app/lastfm.rs @@ -2,25 +2,11 @@ use rustfm_scrobble::{Scrobble, Scrobbler}; use std::path::Path; use user::AuthToken; -use crate::app::{index, user}; +use crate::app::{index, user, Error}; const LASTFM_API_KEY: &str = "02b96c939a2b451c31dfd67add1f696e"; const LASTFM_API_SECRET: &str = "0f25a80ceef4b470b5cb97d99d4b3420"; -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Failed to authenticate with last.fm")] - ScrobblerAuthentication(rustfm_scrobble::ScrobblerError), - #[error("Failed to emit last.fm scrobble")] - Scrobble(rustfm_scrobble::ScrobblerError), - #[error("Failed to emit last.fm now playing update")] - NowPlaying(rustfm_scrobble::ScrobblerError), - #[error(transparent)] - Query(#[from] index::Error), - #[error(transparent)] - User(#[from] user::Error), -} - #[derive(Clone)] pub struct Manager { index_manager: index::Manager, diff --git a/src/app/playlist.rs b/src/app/playlist.rs index 3bb9d37..96810f3 100644 --- a/src/app/playlist.rs +++ b/src/app/playlist.rs @@ -2,22 +2,8 @@ use core::clone::Clone; use sqlx::{Acquire, QueryBuilder, Sqlite}; use std::path::PathBuf; -use crate::app::vfs; -use crate::db::{self, DB}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Database(#[from] sqlx::Error), - #[error(transparent)] - DatabaseConnection(#[from] db::Error), - #[error("User not found")] - UserNotFound, - #[error("Playlist not found")] - PlaylistNotFound, - #[error(transparent)] - Vfs(#[from] vfs::Error), -} +use crate::app::{vfs, Error}; +use crate::db::DB; #[derive(Clone)] pub struct Manager { @@ -191,7 +177,7 @@ impl Manager { #[cfg(test)] mod test { - use std::path::{Path, PathBuf}; + use std::path::PathBuf; use crate::app::test; use crate::test_name; diff --git a/src/app/scanner.rs b/src/app/scanner.rs index 17fd071..667b7e3 100644 --- a/src/app/scanner.rs +++ b/src/app/scanner.rs @@ -11,7 +11,7 @@ use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; use tokio::sync::Notify; use tokio::time::Instant; -use crate::app::{formats, index, settings, vfs}; +use crate::app::{formats, index, settings, vfs, Error}; #[derive(Debug, PartialEq, Eq)] pub struct Directory { @@ -53,7 +53,7 @@ impl Scanner { index_manager: index::Manager, settings_manager: settings::Manager, vfs_manager: vfs::Manager, - ) -> Result { + ) -> Result { let scanner = Self { index_manager, vfs_manager, @@ -100,7 +100,7 @@ impl Scanner { }); } - pub async fn update(&mut self) -> Result<(), index::Error> { + pub async fn update(&mut self) -> Result<(), Error> { let start = Instant::now(); info!("Beginning collection scan"); @@ -185,7 +185,7 @@ impl Scan { } } - pub async fn start(self) -> Result<(), index::Error> { + pub async fn start(self) -> Result<(), Error> { let vfs = self.vfs_manager.get_vfs().await?; let roots = vfs.mounts().clone(); diff --git a/src/app/settings.rs b/src/app/settings.rs index 9aca6ef..bd5bb1d 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -2,23 +2,8 @@ use regex::Regex; use serde::Deserialize; use std::time::Duration; -use crate::db::{self, DB}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Auth secret does not have the expected format")] - AuthenticationSecretInvalid, - #[error("Missing auth secret")] - AuthenticationSecretNotFound, - #[error(transparent)] - DatabaseConnection(#[from] db::Error), - #[error("Missing settings")] - MiscSettingsNotFound, - #[error("Index album art pattern is not a valid regex")] - IndexAlbumArtPatternInvalid, - #[error(transparent)] - Database(#[from] sqlx::Error), -} +use crate::app::Error; +use crate::db::DB; #[derive(Clone, Default)] pub struct AuthSecret { diff --git a/src/app/thumbnail.rs b/src/app/thumbnail.rs index edad43f..6cc2723 100644 --- a/src/app/thumbnail.rs +++ b/src/app/thumbnail.rs @@ -6,26 +6,9 @@ use std::fs::{self, File}; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; +use crate::app::Error; use crate::utils::{get_audio_format, AudioFormat}; -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("No embedded artwork was found in `{0}`")] - EmbeddedArtworkNotFound(PathBuf), - #[error("Could not read thumbnail from ID3 tag in `{0}`:\n\n{1}")] - Id3(PathBuf, id3::Error), - #[error("Could not read thumbnail image in `{0}`:\n\n{1}")] - Image(PathBuf, image::error::ImageError), - #[error("Filesystem error for `{0}`: `{1}`")] - Io(PathBuf, std::io::Error), - #[error("Could not read thumbnail from flac file in `{0}`:\n\n{1}")] - Metaflac(PathBuf, metaflac::Error), - #[error("Could not read thumbnail from mp4 file in `{0}`:\n\n{1}")] - Mp4aMeta(PathBuf, mp4ameta::Error), - #[error("This file format is not supported: {0}")] - UnsupportedFormat(&'static str), -} - #[derive(Debug, Hash)] pub struct Options { pub max_dimension: Option, diff --git a/src/app/user.rs b/src/app/user.rs index 16e8234..c64dd2d 100644 --- a/src/app/user.rs +++ b/src/app/user.rs @@ -5,35 +5,8 @@ use serde::{Deserialize, Serialize}; use std::time::{SystemTime, UNIX_EPOCH}; use crate::app::settings::AuthSecret; -use crate::db::{self, DB}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Database(#[from] sqlx::Error), - #[error(transparent)] - DatabaseConnection(#[from] db::Error), - #[error("Cannot use empty username")] - EmptyUsername, - #[error("Cannot use empty password")] - EmptyPassword, - #[error("Username does not exist")] - IncorrectUsername, - #[error("Password does not match username")] - IncorrectPassword, - #[error("Invalid auth token")] - InvalidAuthToken, - #[error("Incorrect authorization scope")] - IncorrectAuthorizationScope, - #[error("Last.fm session key is missing")] - MissingLastFMSessionKey, - #[error("Failed to hash password")] - PasswordHashing, - #[error("Failed to encode authorization token")] - AuthorizationTokenEncoding, - #[error("Failed to encode Branca token")] - BrancaTokenEncoding, -} +use crate::app::Error; +use crate::db::DB; #[derive(Debug)] pub struct User { diff --git a/src/app/vfs.rs b/src/app/vfs.rs index 7cbed95..8eb5006 100644 --- a/src/app/vfs.rs +++ b/src/app/vfs.rs @@ -4,17 +4,8 @@ use serde::{Deserialize, Serialize}; use sqlx::{Acquire, QueryBuilder, Sqlite}; use std::path::{self, Path, PathBuf}; -use crate::db::{self, DB}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("The following virtual path could not be mapped to a real path: `{0}`")] - CouldNotMapToRealPath(PathBuf), - #[error(transparent)] - DatabaseConnection(#[from] db::Error), - #[error(transparent)] - Database(#[from] sqlx::Error), -} +use crate::app::Error; +use crate::db::DB; #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct MountDir { diff --git a/src/db.rs b/src/db.rs index d52428d..b5c5706 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use sqlx::{ migrate::Migrator, @@ -7,19 +7,9 @@ use sqlx::{ Sqlite, }; -static MIGRATOR: Migrator = sqlx::migrate!("src/db"); +use crate::app::Error; -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Could not initialize database connection pool")] - ConnectionPoolBuild, - #[error("Could not acquire database connection from pool")] - ConnectionPool, - #[error("Filesystem error for `{0}`: `{1}`")] - Io(PathBuf, std::io::Error), - #[error("Could not apply database migrations: {0}")] - Migration(sqlx::migrate::MigrateError), -} +static MIGRATOR: Migrator = sqlx::migrate!("src/db"); #[derive(Clone)] pub struct DB { diff --git a/src/server/axum/error.rs b/src/server/axum/error.rs index 630bfac..79a733f 100644 --- a/src/server/axum/error.rs +++ b/src/server/axum/error.rs @@ -39,13 +39,11 @@ impl IntoResponse for APIError { APIError::OwnAdminPrivilegeRemoval => StatusCode::CONFLICT, APIError::PasswordHashing => StatusCode::INTERNAL_SERVER_ERROR, APIError::PlaylistNotFound => StatusCode::NOT_FOUND, - APIError::Settings(_) => StatusCode::INTERNAL_SERVER_ERROR, APIError::ThumbnailFlacDecoding(_, _) => StatusCode::INTERNAL_SERVER_ERROR, APIError::ThumbnailFileIOError => StatusCode::NOT_FOUND, APIError::ThumbnailId3Decoding(_, _) => StatusCode::INTERNAL_SERVER_ERROR, APIError::ThumbnailImageDecoding(_, _) => StatusCode::INTERNAL_SERVER_ERROR, APIError::ThumbnailMp4Decoding(_, _) => StatusCode::INTERNAL_SERVER_ERROR, - APIError::TomlDeserialization(_) => StatusCode::INTERNAL_SERVER_ERROR, APIError::UnsupportedThumbnailFormat(_) => StatusCode::INTERNAL_SERVER_ERROR, APIError::UserNotFound => StatusCode::NOT_FOUND, APIError::VFSPathNotFound => StatusCode::NOT_FOUND, diff --git a/src/server/error.rs b/src/server/error.rs index 44faf6c..444ccd6 100644 --- a/src/server/error.rs +++ b/src/server/error.rs @@ -1,8 +1,7 @@ use std::path::PathBuf; use thiserror::Error; -use crate::app::{config, ddns, index, lastfm, playlist, settings, thumbnail, user, vfs}; -use crate::db; +use crate::app; #[derive(Error, Debug)] pub enum APIError { @@ -66,8 +65,6 @@ pub enum APIError { PasswordHashing, #[error("Playlist not found")] PlaylistNotFound, - #[error("Settings error:\n\n{0}")] - Settings(settings::Error), #[error("Could not decode thumbnail from flac file `{0}`:\n\n{1}")] ThumbnailFlacDecoding(PathBuf, metaflac::Error), #[error("Thumbnail file could not be opened")] @@ -78,8 +75,6 @@ pub enum APIError { ThumbnailImageDecoding(PathBuf, image::error::ImageError), #[error("Could not decode thumbnail from mp4 file `{0}`:\n\n{1}")] ThumbnailMp4Decoding(PathBuf, mp4ameta::Error), - #[error("Toml deserialization error:\n\n{0}")] - TomlDeserialization(toml::de::Error), #[error("Unsupported thumbnail format: `{0}`")] UnsupportedThumbnailFormat(&'static str), #[error("User not found")] @@ -88,135 +83,63 @@ pub enum APIError { VFSPathNotFound, } -impl From for APIError { - fn from(error: index::Error) -> APIError { +impl From for APIError { + fn from(error: app::Error) -> APIError { match error { - index::Error::DirectoryNotFound(d) => APIError::DirectoryNotFound(d), - index::Error::ArtistNotFound => APIError::ArtistNotFound, - index::Error::AlbumNotFound => APIError::AlbumNotFound, - index::Error::SongNotFound => APIError::SongNotFound, - index::Error::Database(e) => APIError::Database(e), - index::Error::DatabaseConnection(e) => e.into(), - index::Error::Vfs(e) => e.into(), - index::Error::IndexDeserializationError => APIError::Internal, - index::Error::IndexSerializationError => APIError::Internal, - index::Error::ThreadPoolBuilder(_) => APIError::Internal, - index::Error::ThreadJoining(_) => APIError::Internal, - } - } -} + app::Error::ThreadPoolBuilder(_) => APIError::Internal, + app::Error::ThreadJoining(_) => APIError::Internal, -impl From for APIError { - fn from(error: config::Error) -> APIError { - match error { - config::Error::Ddns(e) => e.into(), - config::Error::Io(p, e) => APIError::Io(p, e), - config::Error::Settings(e) => e.into(), - config::Error::Toml(e) => APIError::TomlDeserialization(e), - config::Error::User(e) => e.into(), - config::Error::Vfs(e) => e.into(), - } - } -} + app::Error::Io(p, e) => APIError::Io(p, e), + app::Error::Ape(_) => APIError::Internal, + app::Error::Id3(p, e) => APIError::ThumbnailId3Decoding(p, e), + app::Error::Metaflac(p, e) => APIError::ThumbnailFlacDecoding(p, e), + app::Error::Mp4aMeta(p, e) => APIError::ThumbnailMp4Decoding(p, e), + app::Error::Opus(_) => APIError::Internal, + app::Error::Vorbis(_) => APIError::Internal, + app::Error::VorbisCommentNotFoundInFlacFile => APIError::Internal, + app::Error::Image(p, e) => APIError::ThumbnailImageDecoding(p, e), + app::Error::UnsupportedFormat(f) => APIError::UnsupportedThumbnailFormat(f), -impl From for APIError { - fn from(error: playlist::Error) -> APIError { - match error { - playlist::Error::Database(e) => APIError::Database(e), - playlist::Error::DatabaseConnection(e) => e.into(), - playlist::Error::PlaylistNotFound => APIError::PlaylistNotFound, - playlist::Error::UserNotFound => APIError::UserNotFound, - playlist::Error::Vfs(e) => e.into(), - } - } -} + app::Error::Database(e) => APIError::Database(e), + app::Error::ConnectionPoolBuild => APIError::Internal, + app::Error::ConnectionPool => APIError::Internal, + app::Error::Migration(_) => APIError::Internal, -impl From for APIError { - fn from(error: settings::Error) -> APIError { - match error { - settings::Error::AuthenticationSecretNotFound => APIError::Settings(error), - settings::Error::DatabaseConnection(e) => e.into(), - settings::Error::AuthenticationSecretInvalid => APIError::Settings(error), - settings::Error::MiscSettingsNotFound => APIError::Settings(error), - settings::Error::IndexAlbumArtPatternInvalid => APIError::Settings(error), - settings::Error::Database(e) => APIError::Database(e), - } - } -} + app::Error::UpdateQueryFailed(s) => APIError::DdnsUpdateQueryFailed(s), + app::Error::UpdateQueryTransport => APIError::DdnsUpdateQueryFailed(0), -impl From for APIError { - fn from(error: user::Error) -> APIError { - match error { - user::Error::AuthorizationTokenEncoding => APIError::AuthorizationTokenEncoding, - user::Error::BrancaTokenEncoding => APIError::BrancaTokenEncoding, - user::Error::Database(e) => APIError::Database(e), - user::Error::DatabaseConnection(e) => e.into(), - user::Error::EmptyPassword => APIError::EmptyPassword, - user::Error::EmptyUsername => APIError::EmptyUsername, - user::Error::IncorrectAuthorizationScope => APIError::IncorrectCredentials, - user::Error::IncorrectPassword => APIError::IncorrectCredentials, - user::Error::IncorrectUsername => APIError::IncorrectCredentials, - user::Error::InvalidAuthToken => APIError::IncorrectCredentials, - user::Error::MissingLastFMSessionKey => APIError::IncorrectCredentials, - user::Error::PasswordHashing => APIError::PasswordHashing, - } - } -} + app::Error::AuthenticationSecretNotFound => APIError::Internal, + app::Error::AuthenticationSecretInvalid => APIError::Internal, + app::Error::MiscSettingsNotFound => APIError::Internal, + app::Error::IndexAlbumArtPatternInvalid => APIError::Internal, -impl From for APIError { - fn from(error: vfs::Error) -> APIError { - match error { - vfs::Error::CouldNotMapToRealPath(_) => APIError::VFSPathNotFound, - vfs::Error::Database(e) => APIError::Database(e), - vfs::Error::DatabaseConnection(e) => e.into(), - } - } -} + app::Error::Toml(_) => APIError::Internal, + app::Error::IndexDeserializationError => APIError::Internal, + app::Error::IndexSerializationError => APIError::Internal, -impl From for APIError { - fn from(error: ddns::Error) -> APIError { - match error { - ddns::Error::Database(e) => APIError::Database(e), - ddns::Error::DatabaseConnection(e) => e.into(), - ddns::Error::UpdateQueryFailed(s) => APIError::DdnsUpdateQueryFailed(s), - ddns::Error::UpdateQueryTransport => APIError::DdnsUpdateQueryFailed(0), - } - } -} + app::Error::CouldNotMapToRealPath(_) => APIError::VFSPathNotFound, + app::Error::UserNotFound => APIError::UserNotFound, + app::Error::DirectoryNotFound(d) => APIError::DirectoryNotFound(d), + app::Error::ArtistNotFound => APIError::ArtistNotFound, + app::Error::AlbumNotFound => APIError::AlbumNotFound, + app::Error::SongNotFound => APIError::SongNotFound, + app::Error::PlaylistNotFound => APIError::PlaylistNotFound, + app::Error::EmbeddedArtworkNotFound(_) => APIError::EmbeddedArtworkNotFound, -impl From for APIError { - fn from(error: db::Error) -> APIError { - match error { - db::Error::ConnectionPoolBuild => APIError::Internal, - db::Error::ConnectionPool => APIError::Internal, - db::Error::Io(p, e) => APIError::Io(p, e), - db::Error::Migration(_) => APIError::Internal, - } - } -} + app::Error::EmptyUsername => APIError::EmptyUsername, + app::Error::EmptyPassword => APIError::EmptyPassword, + app::Error::IncorrectUsername => APIError::IncorrectCredentials, + app::Error::IncorrectPassword => APIError::IncorrectCredentials, + app::Error::InvalidAuthToken => APIError::IncorrectCredentials, + app::Error::IncorrectAuthorizationScope => APIError::IncorrectCredentials, + app::Error::MissingLastFMSessionKey => APIError::IncorrectCredentials, + app::Error::PasswordHashing => APIError::PasswordHashing, + app::Error::AuthorizationTokenEncoding => APIError::AuthorizationTokenEncoding, + app::Error::BrancaTokenEncoding => APIError::BrancaTokenEncoding, -impl From for APIError { - fn from(error: lastfm::Error) -> APIError { - match error { - lastfm::Error::ScrobblerAuthentication(e) => APIError::LastFMScrobblerAuthentication(e), - lastfm::Error::Scrobble(e) => APIError::LastFMScrobble(e), - lastfm::Error::NowPlaying(e) => APIError::LastFMNowPlaying(e), - lastfm::Error::Query(e) => e.into(), - lastfm::Error::User(e) => e.into(), - } - } -} - -impl From for APIError { - fn from(error: thumbnail::Error) -> APIError { - match error { - thumbnail::Error::EmbeddedArtworkNotFound(_) => APIError::EmbeddedArtworkNotFound, - thumbnail::Error::Id3(p, e) => APIError::ThumbnailId3Decoding(p, e), - thumbnail::Error::Image(p, e) => APIError::ThumbnailImageDecoding(p, e), - thumbnail::Error::Io(p, e) => APIError::Io(p, e), - thumbnail::Error::Metaflac(p, e) => APIError::ThumbnailFlacDecoding(p, e), - thumbnail::Error::Mp4aMeta(p, e) => APIError::ThumbnailMp4Decoding(p, e), - thumbnail::Error::UnsupportedFormat(f) => APIError::UnsupportedThumbnailFormat(f), + app::Error::ScrobblerAuthentication(e) => APIError::LastFMScrobblerAuthentication(e), + app::Error::Scrobble(e) => APIError::LastFMScrobble(e), + app::Error::NowPlaying(e) => APIError::LastFMNowPlaying(e), } } }