1use std::{fmt::Debug, path::PathBuf};
2
3use mecomp_storage::errors::Error;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7#[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#[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#[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#[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}