1use crate::file::FileType;
7use crate::id3::v2::FrameId;
8use crate::tag::ItemKey;
9
10use std::collections::TryReserveError;
11use std::fmt::{Debug, Display, Formatter};
12
13use ogg_pager::PageError;
14
15pub type Result<T> = std::result::Result<T, LoftyError>;
17
18#[derive(Debug)]
20#[non_exhaustive]
21pub enum ErrorKind {
22 UnknownFormat,
25
26 TooMuchData,
29 SizeMismatch,
35 FileDecoding(FileDecodingError),
37 FileEncoding(FileEncodingError),
39
40 NotAPicture,
43 UnsupportedPicture,
45
46 UnsupportedTag,
49 FakeTag,
51 TextDecode(&'static str),
53 BadTimestamp(&'static str),
55 Id3v2(Id3v2Error),
57
58 BadAtom(&'static str),
60 AtomMismatch,
62
63 OggPage(ogg_pager::PageError),
66 StringFromUtf8(std::string::FromUtf8Error),
68 StrFromUtf8(std::str::Utf8Error),
70 Io(std::io::Error),
72 Fmt(std::fmt::Error),
74 Alloc(TryReserveError),
76 Infallible(std::convert::Infallible),
78}
79
80#[derive(Debug)]
82#[non_exhaustive]
83pub enum Id3v2ErrorKind {
84 BadId3v2Version(u8, u8),
87 V2Compression,
92 BadExtendedHeaderSize,
94
95 BadFrameId(Vec<u8>),
99 UnsupportedFrameId(ItemKey),
102 BadFrameLength,
104 EmptyFrame(FrameId<'static>),
106 MissingDataLengthIndicator,
108 InvalidUnsynchronisation,
112 V2InvalidTextEncoding,
114 BadPictureFormat(String),
116 BadSyncText,
118 MissingUfidOwner,
120 BadRva2ChannelType,
122 BadTimestampFormat,
124
125 #[cfg(feature = "id3v2_compression_support")]
127 Decompression(flate2::DecompressError),
129 #[cfg(not(feature = "id3v2_compression_support"))]
130 CompressedFrameEncountered,
132
133 InvalidEncryptionMethodSymbol(u8),
136 BadFrame(String, &'static str),
138 InvalidLanguage([u8; 3]),
140}
141
142impl Display for Id3v2ErrorKind {
143 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
144 match self {
145 Self::BadId3v2Version(major, minor) => write!(
147 f,
148 "Found an invalid version (v{major}.{minor}), expected any major revision in: (2, \
149 3, 4)"
150 ),
151 Self::V2Compression => write!(f, "Encountered a compressed ID3v2.2 tag"),
152 Self::BadExtendedHeaderSize => {
153 write!(f, "Found an extended header with an invalid size")
154 },
155
156 Self::BadFrameId(frame_id) => write!(f, "Failed to parse a frame ID: 0x{frame_id:x?}"),
158 Self::UnsupportedFrameId(item_key) => {
159 write!(f, "Unsupported frame ID for item key {item_key:?}")
160 },
161 Self::BadFrameLength => write!(
162 f,
163 "Frame isn't long enough to extract the necessary information"
164 ),
165 Self::EmptyFrame(id) => write!(f, "Frame `{id}` is empty"),
166 Self::MissingDataLengthIndicator => write!(
167 f,
168 "Encountered an encrypted frame without a data length indicator"
169 ),
170 Self::InvalidUnsynchronisation => write!(f, "Encountered an invalid unsynchronisation"),
171 Self::V2InvalidTextEncoding => {
172 write!(f, "ID3v2.2 only supports Latin-1 and UTF-16 encodings")
173 },
174 Self::BadPictureFormat(format) => {
175 write!(f, "Picture: Found unexpected format \"{format}\"")
176 },
177 Self::BadSyncText => write!(f, "Encountered invalid data in SYLT frame"),
178 Self::MissingUfidOwner => write!(f, "Missing owner in UFID frame"),
179 Self::BadRva2ChannelType => write!(f, "Encountered invalid channel type in RVA2 frame"),
180 Self::BadTimestampFormat => write!(
181 f,
182 "Encountered an invalid timestamp format in a synchronized frame"
183 ),
184
185 #[cfg(feature = "id3v2_compression_support")]
187 Self::Decompression(err) => write!(f, "Failed to decompress frame: {err}"),
188 #[cfg(not(feature = "id3v2_compression_support"))]
189 Self::CompressedFrameEncountered => write!(
190 f,
191 "Encountered a compressed ID3v2 frame, support is disabled"
192 ),
193
194 Self::InvalidEncryptionMethodSymbol(symbol) => write!(
196 f,
197 "Attempted to write an encrypted frame with an invalid method symbol ({symbol})"
198 ),
199 Self::BadFrame(frame_id, frame_value) => write!(
200 f,
201 "Attempted to write an invalid frame. ID: \"{frame_id}\", Value: \"{frame_value}\"",
202 ),
203 Self::InvalidLanguage(lang) => write!(
204 f,
205 "Invalid frame language found: {lang:?} (expected 3 ascii characters)"
206 ),
207 }
208 }
209}
210
211pub struct Id3v2Error {
213 kind: Id3v2ErrorKind,
214}
215
216impl Id3v2Error {
217 #[must_use]
219 pub const fn new(kind: Id3v2ErrorKind) -> Self {
220 Self { kind }
221 }
222
223 pub fn kind(&self) -> &Id3v2ErrorKind {
225 &self.kind
226 }
227}
228
229impl Debug for Id3v2Error {
230 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
231 write!(f, "ID3v2: {:?}", self.kind)
232 }
233}
234
235impl Display for Id3v2Error {
236 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
237 write!(f, "ID3v2: {}", self.kind)
238 }
239}
240
241pub struct FileDecodingError {
243 format: Option<FileType>,
244 description: &'static str,
245}
246
247impl FileDecodingError {
248 #[must_use]
250 pub const fn new(format: FileType, description: &'static str) -> Self {
251 Self {
252 format: Some(format),
253 description,
254 }
255 }
256
257 pub fn from_description(description: &'static str) -> Self {
259 Self {
260 format: None,
261 description,
262 }
263 }
264
265 pub fn format(&self) -> Option<FileType> {
267 self.format
268 }
269
270 pub fn description(&self) -> &str {
272 self.description
273 }
274}
275
276impl Debug for FileDecodingError {
277 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
278 if let Some(format) = self.format {
279 write!(f, "{:?}: {:?}", format, self.description)
280 } else {
281 write!(f, "{:?}", self.description)
282 }
283 }
284}
285
286impl Display for FileDecodingError {
287 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
288 if let Some(format) = self.format {
289 write!(f, "{:?}: {}", format, self.description)
290 } else {
291 write!(f, "{}", self.description)
292 }
293 }
294}
295
296pub struct FileEncodingError {
298 format: Option<FileType>,
299 description: &'static str,
300}
301
302impl FileEncodingError {
303 #[must_use]
316 pub const fn new(format: FileType, description: &'static str) -> Self {
317 Self {
318 format: Some(format),
319 description,
320 }
321 }
322
323 pub fn from_description(description: &'static str) -> Self {
336 Self {
337 format: None,
338 description,
339 }
340 }
341
342 pub fn format(&self) -> Option<FileType> {
356 self.format
357 }
358
359 pub fn description(&self) -> &str {
376 self.description
377 }
378}
379
380impl Debug for FileEncodingError {
381 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
382 if let Some(format) = self.format {
383 write!(f, "{:?}: {:?}", format, self.description)
384 } else {
385 write!(f, "{:?}", self.description)
386 }
387 }
388}
389
390impl Display for FileEncodingError {
391 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
392 if let Some(format) = self.format {
393 write!(f, "{:?}: {:?}", format, self.description)
394 } else {
395 write!(f, "{}", self.description)
396 }
397 }
398}
399
400pub struct LoftyError {
402 pub(crate) kind: ErrorKind,
403}
404
405impl LoftyError {
406 #[must_use]
416 pub const fn new(kind: ErrorKind) -> Self {
417 Self { kind }
418 }
419
420 pub fn kind(&self) -> &ErrorKind {
433 &self.kind
434 }
435}
436
437impl std::error::Error for LoftyError {}
438
439impl Debug for LoftyError {
440 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
441 write!(f, "{:?}", self.kind)
442 }
443}
444
445impl From<Id3v2Error> for LoftyError {
446 fn from(input: Id3v2Error) -> Self {
447 Self {
448 kind: ErrorKind::Id3v2(input),
449 }
450 }
451}
452
453impl From<FileDecodingError> for LoftyError {
454 fn from(input: FileDecodingError) -> Self {
455 Self {
456 kind: ErrorKind::FileDecoding(input),
457 }
458 }
459}
460
461impl From<FileEncodingError> for LoftyError {
462 fn from(input: FileEncodingError) -> Self {
463 Self {
464 kind: ErrorKind::FileEncoding(input),
465 }
466 }
467}
468
469impl From<ogg_pager::PageError> for LoftyError {
470 fn from(input: PageError) -> Self {
471 Self {
472 kind: ErrorKind::OggPage(input),
473 }
474 }
475}
476
477impl From<std::io::Error> for LoftyError {
478 fn from(input: std::io::Error) -> Self {
479 Self {
480 kind: ErrorKind::Io(input),
481 }
482 }
483}
484
485impl From<std::fmt::Error> for LoftyError {
486 fn from(input: std::fmt::Error) -> Self {
487 Self {
488 kind: ErrorKind::Fmt(input),
489 }
490 }
491}
492
493impl From<std::string::FromUtf8Error> for LoftyError {
494 fn from(input: std::string::FromUtf8Error) -> Self {
495 Self {
496 kind: ErrorKind::StringFromUtf8(input),
497 }
498 }
499}
500
501impl From<std::str::Utf8Error> for LoftyError {
502 fn from(input: std::str::Utf8Error) -> Self {
503 Self {
504 kind: ErrorKind::StrFromUtf8(input),
505 }
506 }
507}
508
509impl From<std::collections::TryReserveError> for LoftyError {
510 fn from(input: TryReserveError) -> Self {
511 Self {
512 kind: ErrorKind::Alloc(input),
513 }
514 }
515}
516
517impl From<std::convert::Infallible> for LoftyError {
518 fn from(input: std::convert::Infallible) -> Self {
519 Self {
520 kind: ErrorKind::Infallible(input),
521 }
522 }
523}
524
525impl Display for LoftyError {
526 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
527 match self.kind {
528 ErrorKind::OggPage(ref err) => write!(f, "{err}"),
530 ErrorKind::StringFromUtf8(ref err) => write!(f, "{err}"),
531 ErrorKind::StrFromUtf8(ref err) => write!(f, "{err}"),
532 ErrorKind::Io(ref err) => write!(f, "{err}"),
533 ErrorKind::Fmt(ref err) => write!(f, "{err}"),
534 ErrorKind::Alloc(ref err) => write!(f, "{err}"),
535
536 ErrorKind::UnknownFormat => {
537 write!(f, "No format could be determined from the provided file")
538 },
539 ErrorKind::NotAPicture => write!(f, "Picture: Encountered invalid data"),
540 ErrorKind::UnsupportedPicture => {
541 write!(f, "Picture: attempted to write an unsupported picture")
542 },
543 ErrorKind::UnsupportedTag => write!(
544 f,
545 "Attempted to write a tag to a format that does not support it"
546 ),
547 ErrorKind::FakeTag => write!(f, "Reading: Expected a tag, found invalid data"),
548 ErrorKind::TextDecode(message) => write!(f, "Text decoding: {message}"),
549 ErrorKind::BadTimestamp(message) => {
550 write!(f, "Encountered an invalid timestamp: {message}")
551 },
552 ErrorKind::Id3v2(ref id3v2_err) => write!(f, "{id3v2_err}"),
553 ErrorKind::BadAtom(message) => write!(f, "MP4 Atom: {message}"),
554 ErrorKind::AtomMismatch => write!(
555 f,
556 "MP4 Atom: Attempted to use `Atom::merge()` with mismatching identifiers"
557 ),
558
559 ErrorKind::TooMuchData => write!(
561 f,
562 "Attempted to read/write an abnormally large amount of data"
563 ),
564 ErrorKind::SizeMismatch => write!(
565 f,
566 "Encountered an invalid item size, either too big or too small to be valid"
567 ),
568 ErrorKind::FileDecoding(ref file_decode_err) => write!(f, "{file_decode_err}"),
569 ErrorKind::FileEncoding(ref file_encode_err) => write!(f, "{file_encode_err}"),
570
571 ErrorKind::Infallible(_) => write!(f, "A expected condition was not upheld"),
572 }
573 }
574}