//! Contains the errors that can arise within Lofty //! //! The primary error is [`LoftyError`]. The type of error is determined by [`ErrorKind`], //! which can be extended at any time. use crate::file::FileType; use std::collections::TryReserveError; use std::fmt::{Debug, Display, Formatter}; use ogg_pager::PageError; /// Alias for `Result` pub type Result = std::result::Result; #[derive(Debug)] #[non_exhaustive] /// The types of errors that can occur pub enum ErrorKind { // File format related errors /// Unable to guess the format UnknownFormat, // File data related errors /// Attempting to read/write an abnormally large amount of data TooMuchData, /// Errors that occur while decoding a file FileDecoding(FileDecodingError), /// Errors that occur while encoding a file FileEncoding(FileEncodingError), // Picture related errors /// Provided an invalid picture NotAPicture, /// Attempted to write a picture that the format does not support UnsupportedPicture, // Tag related errors /// Arises when writing a tag to a file type that doesn't support it UnsupportedTag, /// Arises when a tag is expected (Ex. found an "ID3 " chunk in a WAV file), but isn't found FakeTag, /// Errors that arise while decoding text TextDecode(&'static str), /// Errors that arise while reading/writing ID3v2 tags Id3v2(Id3v2Error), /// Arises when an atom contains invalid data BadAtom(&'static str), // Conversions for external errors /// Errors that arise while parsing OGG pages OggPage(ogg_pager::PageError), /// Unable to convert bytes to a String StringFromUtf8(std::string::FromUtf8Error), /// Unable to convert bytes to a str StrFromUtf8(std::str::Utf8Error), /// Represents all cases of [`std::io::Error`]. Io(std::io::Error), /// Failure to allocate enough memory Alloc(TryReserveError), } #[derive(Debug, Clone)] #[non_exhaustive] /// The types of errors that can occur while interacting with ID3v2 tags pub enum Id3v2ErrorKind { #[cfg(feature = "id3v2")] /// Arises when an invalid picture format is parsed. Only applicable to [`Id3v2Version::V2`](crate::id3::v2::Id3v2Version::V2) BadPictureFormat(String), /// Arises when an invalid ID3v2 version is found BadId3v2Version(u8, u8), #[cfg(feature = "id3v2")] /// Arises when a frame ID contains invalid characters (must be within `'A'..'Z'` or `'0'..'9'`) BadFrameID, #[cfg(feature = "id3v2")] /// Arises when a frame doesn't have enough data BadFrameLength, #[cfg(feature = "id3v2")] /// Arises when invalid data is encountered while reading an ID3v2 synchronized text frame BadSyncText, #[cfg(feature = "id3v2")] /// Arises when attempting to write an invalid Frame (Bad `FrameID`/`FrameValue` pairing) BadFrame(String, &'static str), /// A catch-all for all remaining errors /// /// NOTE: This will likely be deprecated in the future Other(&'static str), } impl Display for Id3v2ErrorKind { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Id3v2ErrorKind::BadId3v2Version(major, minor) => write!( f, "Found an invalid version (v{}.{}), expected any major revision in: (2, 3, 4)", major, minor ), #[cfg(feature = "id3v2")] Id3v2ErrorKind::BadFrameID => write!(f, "Failed to parse a frame ID"), #[cfg(feature = "id3v2")] Id3v2ErrorKind::BadFrameLength => write!( f, "Frame isn't long enough to extract the necessary information" ), #[cfg(feature = "id3v2")] Id3v2ErrorKind::BadSyncText => write!(f, "Encountered invalid data in SYLT frame"), #[cfg(feature = "id3v2")] Id3v2ErrorKind::BadFrame(ref frame_id, frame_value) => write!( f, "Attempted to write an invalid frame. ID: \"{}\", Value: \"{}\"", frame_id, frame_value ), #[cfg(feature = "id3v2")] Id3v2ErrorKind::BadPictureFormat(format) => { write!(f, "Picture: Found unexpected format \"{}\"", format) }, Id3v2ErrorKind::Other(message) => write!(f, "{}", message), } } } /// An error that arises while interacting with an ID3v2 tag pub struct Id3v2Error { kind: Id3v2ErrorKind, } impl Id3v2Error { /// Create a new `Id3v2Error` from an [`Id3v2ErrorKind`] pub fn new(kind: Id3v2ErrorKind) -> Self { Self { kind } } /// Returns the [`Id3v2ErrorKind`] pub fn kind(&self) -> Id3v2ErrorKind { self.kind.clone() } } impl Debug for Id3v2Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "ID3v2: {:?}", self.kind) } } impl Display for Id3v2Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "ID3v2: {}", self.kind) } } /// An error that arises while decoding a file pub struct FileDecodingError { format: Option, description: &'static str, } impl FileDecodingError { /// Create a `FileDecodingError` from a [`FileType`] and description pub fn new(format: FileType, description: &'static str) -> Self { Self { format: Some(format), description, } } /// Create a `FileDecodingError` without binding it to a [`FileType`] pub fn from_description(description: &'static str) -> Self { Self { format: None, description, } } /// Returns the associated [`FileType`], if one exists pub fn format(&self) -> Option { self.format } /// Returns the error description pub fn description(&self) -> &str { self.description } } impl Debug for FileDecodingError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if let Some(format) = self.format { write!(f, "{:?}: {:?}", format, self.description) } else { write!(f, "{:?}", self.description) } } } impl Display for FileDecodingError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if let Some(format) = self.format { write!(f, "{:?}: {}", format, self.description) } else { write!(f, "{}", self.description) } } } /// An error that arises while encoding a file pub struct FileEncodingError { format: Option, description: &'static str, } impl FileEncodingError { /// Create a `FileEncodingError` from a [`FileType`] and description pub fn new(format: FileType, description: &'static str) -> Self { Self { format: Some(format), description, } } /// Create a `FileEncodingError` without binding it to a [`FileType`] pub fn from_description(description: &'static str) -> Self { Self { format: None, description, } } /// Returns the associated [`FileType`], if one exists pub fn format(&self) -> Option { self.format } /// Returns the error description pub fn description(&self) -> &str { self.description } } impl Debug for FileEncodingError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if let Some(format) = self.format { write!(f, "{:?}: {:?}", format, self.description) } else { write!(f, "{:?}", self.description) } } } impl Display for FileEncodingError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if let Some(format) = self.format { write!(f, "{:?}: {:?}", format, self.description) } else { write!(f, "{}", self.description) } } } /// Errors that could occur within Lofty pub struct LoftyError { kind: ErrorKind, } impl LoftyError { /// Create a `LoftyError` from an [`ErrorKind`] pub fn new(kind: ErrorKind) -> Self { Self { kind } } /// Returns the [`ErrorKind`] pub fn kind(&self) -> &ErrorKind { &self.kind } } impl std::error::Error for LoftyError {} impl Debug for LoftyError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.kind) } } impl From for LoftyError { fn from(input: Id3v2Error) -> Self { Self { kind: ErrorKind::Id3v2(input), } } } impl From for LoftyError { fn from(input: FileDecodingError) -> Self { Self { kind: ErrorKind::FileDecoding(input), } } } impl From for LoftyError { fn from(input: FileEncodingError) -> Self { Self { kind: ErrorKind::FileEncoding(input), } } } impl From for LoftyError { fn from(input: PageError) -> Self { Self { kind: ErrorKind::OggPage(input), } } } impl From for LoftyError { fn from(input: std::io::Error) -> Self { Self { kind: ErrorKind::Io(input), } } } impl From for LoftyError { fn from(input: std::string::FromUtf8Error) -> Self { Self { kind: ErrorKind::StringFromUtf8(input), } } } impl From for LoftyError { fn from(input: std::str::Utf8Error) -> Self { Self { kind: ErrorKind::StrFromUtf8(input), } } } impl From for LoftyError { fn from(input: TryReserveError) -> Self { Self { kind: ErrorKind::Alloc(input), } } } impl Display for LoftyError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self.kind { // Conversions ErrorKind::OggPage(ref err) => write!(f, "{}", err), ErrorKind::StringFromUtf8(ref err) => write!(f, "{}", err), ErrorKind::StrFromUtf8(ref err) => write!(f, "{}", err), ErrorKind::Io(ref err) => write!(f, "{}", err), ErrorKind::Alloc(ref err) => write!(f, "{}", err), ErrorKind::UnknownFormat => { write!(f, "No format could be determined from the provided file") }, ErrorKind::TooMuchData => write!( f, "An abnormally large amount of data was provided, and an overflow occurred" ), ErrorKind::NotAPicture => write!(f, "Picture: Encountered invalid data"), ErrorKind::UnsupportedPicture => { write!(f, "Picture: attempted to write an unsupported picture") }, ErrorKind::UnsupportedTag => write!( f, "Attempted to write a tag to a format that does not support it" ), ErrorKind::FakeTag => write!(f, "Reading: Expected a tag, found invalid data"), ErrorKind::TextDecode(message) => write!(f, "Text decoding: {}", message), ErrorKind::Id3v2(ref id3v2_err) => write!(f, "{}", id3v2_err), ErrorKind::BadAtom(message) => write!(f, "MP4 Atom: {}", message), // Files ErrorKind::FileDecoding(ref file_decode_err) => write!(f, "{}", file_decode_err), ErrorKind::FileEncoding(ref file_encode_err) => write!(f, "{}", file_encode_err), } } }