mecomp_core/
errors.rs

1use std::{fmt::Debug, path::PathBuf};
2
3use mecomp_storage::errors::Error;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7/// An error in the UDP stack.
8#[derive(Error, Debug)]
9#[cfg(feature = "rpc")]
10pub enum UdpError {
11    #[error("IO error: {0}")]
12    IO(#[from] std::io::Error),
13    #[error("Ciborium deserialization error: {0}")]
14    CiboriumDeserialization(#[from] ciborium::de::Error<std::io::Error>),
15    #[error("Ciborium serialization error: {0}")]
16    CiboriumSerialization(#[from] ciborium::ser::Error<std::io::Error>),
17}
18
19/// Errors that can occur with finding the config or data directories.
20#[derive(Error, Debug)]
21pub enum DirectoryError {
22    #[error("Unable to find the config directory for mecomp.")]
23    Config,
24    #[error("Unable to find the data directory for mecomp.")]
25    Data,
26}
27
28/// Errors that can occur when importing or exporting playlists or dynamic playlists
29#[derive(Error, Debug, Deserialize, Serialize, PartialEq, Eq)]
30pub enum BackupError {
31    #[error("The file \"{0}\" already exists")]
32    FileExists(PathBuf),
33    #[error("The file \"{0}\" does not exist")]
34    FileNotFound(PathBuf),
35    #[error("The file \"{0}\" has the wrong extension, expected: {1}")]
36    WrongExtension(PathBuf, String),
37    #[error("{0} is a directory, not a file")]
38    PathIsDirectory(PathBuf),
39    #[error("CSV error: {0}")]
40    CsvError(String),
41    #[error("IO Error: {0}")]
42    IoError(String),
43    #[error("Invalid playlist format")]
44    InvalidDynamicPlaylistFormat,
45    #[error("Error parsing dynamic playlist query in record {1}: {0}")]
46    InvalidDynamicPlaylistQuery(String, usize),
47    #[error("Error parsing playlist name in line {0}, name is empty or already set")]
48    PlaylistNameInvalidOrAlreadySet(usize),
49    #[error(
50        "Out of {0} entries, no valid songs were found in the playlist, consult the logs for more information"
51    )]
52    NoValidSongs(usize),
53    #[error("No valid playlists were found in the csv file.")]
54    NoValidPlaylists,
55}
56
57impl From<csv::Error> for BackupError {
58    #[inline]
59    fn from(value: csv::Error) -> Self {
60        Self::CsvError(format!("{value}"))
61    }
62}
63
64impl From<std::io::Error> for BackupError {
65    #[inline]
66    fn from(e: std::io::Error) -> Self {
67        Self::IoError(e.to_string())
68    }
69}
70
71/// Errors that can occur with the library.
72#[derive(Error, Debug)]
73pub enum LibraryError {
74    #[error("Database error: {0}")]
75    Database(#[from] Error),
76    #[error("IO error: {0}")]
77    IO(#[from] std::io::Error),
78    #[error("Decoder error: {0}")]
79    #[cfg(feature = "audio")]
80    Decoder(#[from] rodio::decoder::DecoderError),
81    #[error("UdpError: {0}")]
82    #[cfg(feature = "rpc")]
83    Udp(#[from] UdpError),
84}
85
86#[derive(Error, Debug, Deserialize, Serialize, PartialEq, Eq)]
87pub enum SerializableLibraryError {
88    #[error("Database error: {0}")]
89    Database(String),
90    #[error("IO error: {0}")]
91    IO(String),
92    #[error("Decoder error: {0}")]
93    Decoder(String),
94    #[error("Library Rescan already in progress.")]
95    RescanInProgress,
96    #[error("Library Analysis already in progress.")]
97    AnalysisInProgress,
98    #[error("Collection Reclustering already in progress.")]
99    ReclusterInProgress,
100    #[error("UdpError: {0}")]
101    #[cfg(feature = "rpc")]
102    Udp(String),
103    #[error("Backup Error: {0}")]
104    BackupError(#[from] BackupError),
105}
106
107impl From<Error> for SerializableLibraryError {
108    #[inline]
109    fn from(e: Error) -> Self {
110        Self::Database(e.to_string())
111    }
112}
113
114impl From<std::io::Error> for SerializableLibraryError {
115    #[inline]
116    fn from(e: std::io::Error) -> Self {
117        Self::IO(e.to_string())
118    }
119}
120
121#[cfg(feature = "audio")]
122impl From<rodio::decoder::DecoderError> for SerializableLibraryError {
123    #[inline]
124    fn from(e: rodio::decoder::DecoderError) -> Self {
125        Self::Decoder(e.to_string())
126    }
127}
128
129#[cfg(feature = "rpc")]
130impl From<UdpError> for SerializableLibraryError {
131    #[inline]
132    fn from(e: UdpError) -> Self {
133        Self::Udp(e.to_string())
134    }
135}
136
137impl From<LibraryError> for SerializableLibraryError {
138    #[inline]
139    fn from(e: LibraryError) -> Self {
140        match e {
141            LibraryError::Database(e) => Self::Database(e.to_string()),
142            LibraryError::IO(e) => Self::IO(e.to_string()),
143            #[cfg(feature = "audio")]
144            LibraryError::Decoder(e) => Self::Decoder(e.to_string()),
145            #[cfg(feature = "rpc")]
146            LibraryError::Udp(e) => Self::Udp(e.to_string()),
147        }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154    use pretty_assertions::assert_str_eq;
155    use rstest::rstest;
156
157    #[rstest]
158    #[case(
159        LibraryError::from(Error::NoId),
160        "Database error: Item is missing an Id."
161    )]
162    #[case(
163        LibraryError::from(std::io::Error::new(std::io::ErrorKind::Other, "test")),
164        "IO error: test"
165    )]
166    #[case(
167        LibraryError::from(rodio::decoder::DecoderError::DecodeError("test")),
168        "Decoder error: test"
169    )]
170    fn test_serializable_library_error(#[case] input: LibraryError, #[case] expected: String) {
171        let actual = SerializableLibraryError::from(input).to_string();
172        assert_str_eq!(actual, expected);
173    }
174
175    #[rstest]
176    #[case(Error::NoId, LibraryError::Database(Error::NoId).into())]
177    #[case(std::io::Error::new(std::io::ErrorKind::Other, "test"), LibraryError::IO(std::io::Error::new(std::io::ErrorKind::Other, "test")).into())]
178    #[case(rodio::decoder::DecoderError::DecodeError("test"), LibraryError::Decoder(rodio::decoder::DecoderError::DecodeError("test")).into())]
179    fn test_serializable_library_error_from<T: Into<SerializableLibraryError>>(
180        #[case] from: T,
181        #[case] to: SerializableLibraryError,
182    ) {
183        let actual = from.into();
184        assert_eq!(actual, to);
185    }
186}