use crate::file::FileType;
use std::collections::TryReserveError;
use std::fmt::{Debug, Display, Formatter};
use ogg_pager::PageError;
pub type Result<T> = std::result::Result<T, LoftyError>;
#[derive(Debug)]
#[non_exhaustive]
pub enum ErrorKind {
UnknownFormat,
TooMuchData,
SizeMismatch,
FileDecoding(FileDecodingError),
FileEncoding(FileEncodingError),
NotAPicture,
UnsupportedPicture,
UnsupportedTag,
FakeTag,
TextDecode(&'static str),
ID3v2(ID3v2Error),
BadAtom(&'static str),
OggPage(ogg_pager::PageError),
StringFromUtf8(std::string::FromUtf8Error),
StrFromUtf8(std::str::Utf8Error),
Io(std::io::Error),
Alloc(TryReserveError),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum ID3v2ErrorKind {
BadPictureFormat(String),
BadId3v2Version(u8, u8),
BadFrameID,
BadFrameLength,
BadSyncText,
BadFrame(String, &'static str),
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{major}.{minor}), expected any major revision in: (2, \
3, 4)"
),
ID3v2ErrorKind::BadFrameID => write!(f, "Failed to parse a frame ID"),
ID3v2ErrorKind::BadFrameLength => write!(
f,
"Frame isn't long enough to extract the necessary information"
),
ID3v2ErrorKind::BadSyncText => write!(f, "Encountered invalid data in SYLT frame"),
ID3v2ErrorKind::BadFrame(ref frame_id, frame_value) => write!(
f,
"Attempted to write an invalid frame. ID: \"{}\", Value: \"{}\"",
frame_id, frame_value
),
ID3v2ErrorKind::BadPictureFormat(format) => {
write!(f, "Picture: Found unexpected format \"{format}\"")
},
ID3v2ErrorKind::Other(message) => write!(f, "{message}"),
}
}
}
pub struct ID3v2Error {
kind: ID3v2ErrorKind,
}
impl ID3v2Error {
#[must_use]
pub const fn new(kind: ID3v2ErrorKind) -> Self {
Self { kind }
}
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)
}
}
pub struct FileDecodingError {
format: Option<FileType>,
description: &'static str,
}
impl FileDecodingError {
#[must_use]
pub const fn new(format: FileType, description: &'static str) -> Self {
Self {
format: Some(format),
description,
}
}
pub fn from_description(description: &'static str) -> Self {
Self {
format: None,
description,
}
}
pub fn format(&self) -> Option<FileType> {
self.format
}
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)
}
}
}
pub struct FileEncodingError {
format: Option<FileType>,
description: &'static str,
}
impl FileEncodingError {
#[must_use]
pub const fn new(format: FileType, description: &'static str) -> Self {
Self {
format: Some(format),
description,
}
}
pub fn from_description(description: &'static str) -> Self {
Self {
format: None,
description,
}
}
pub fn format(&self) -> Option<FileType> {
self.format
}
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)
}
}
}
pub struct LoftyError {
kind: ErrorKind,
}
impl LoftyError {
#[must_use]
pub const fn new(kind: ErrorKind) -> Self {
Self { kind }
}
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<ID3v2Error> for LoftyError {
fn from(input: ID3v2Error) -> Self {
Self {
kind: ErrorKind::ID3v2(input),
}
}
}
impl From<FileDecodingError> for LoftyError {
fn from(input: FileDecodingError) -> Self {
Self {
kind: ErrorKind::FileDecoding(input),
}
}
}
impl From<FileEncodingError> for LoftyError {
fn from(input: FileEncodingError) -> Self {
Self {
kind: ErrorKind::FileEncoding(input),
}
}
}
impl From<ogg_pager::PageError> for LoftyError {
fn from(input: PageError) -> Self {
Self {
kind: ErrorKind::OggPage(input),
}
}
}
impl From<std::io::Error> for LoftyError {
fn from(input: std::io::Error) -> Self {
Self {
kind: ErrorKind::Io(input),
}
}
}
impl From<std::string::FromUtf8Error> for LoftyError {
fn from(input: std::string::FromUtf8Error) -> Self {
Self {
kind: ErrorKind::StringFromUtf8(input),
}
}
}
impl From<std::str::Utf8Error> for LoftyError {
fn from(input: std::str::Utf8Error) -> Self {
Self {
kind: ErrorKind::StrFromUtf8(input),
}
}
}
impl From<std::collections::TryReserveError> 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 {
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::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}"),
ErrorKind::TooMuchData => write!(
f,
"An abnormally large amount of data was provided, and an overflow occurred"
),
ErrorKind::SizeMismatch => write!(
f,
"Encountered an invalid item size, either too big or too small to be valid"
),
ErrorKind::FileDecoding(ref file_decode_err) => write!(f, "{file_decode_err}"),
ErrorKind::FileEncoding(ref file_encode_err) => write!(f, "{file_encode_err}"),
}
}
}