Misc: Add decode_err! macro to cleanup FileDecodingError creation

This commit is contained in:
Serial 2022-09-11 23:03:32 -04:00
parent ddffb169f7
commit 899f4e4664
No known key found for this signature in database
GPG key ID: DA95198DC17C4568
23 changed files with 142 additions and 290 deletions

View file

@ -1,5 +1,5 @@
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::decode_err;
use crate::traits::SeekStreamLen; use crate::traits::SeekStreamLen;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -25,9 +25,7 @@ where
if size < 32 { if size < 32 {
// If the size is < 32, something went wrong during encoding // If the size is < 32, something went wrong during encoding
// The size includes the footer and all items // The size includes the footer and all items
return Err( decode_err!(@BAIL APE, "APE tag has an invalid size (< 32)");
FileDecodingError::new(FileType::APE, "APE tag has an invalid size (< 32)").into(),
);
} }
#[cfg(feature = "ape")] #[cfg(feature = "ape")]
@ -53,11 +51,7 @@ where
#[allow(unstable_name_collisions)] #[allow(unstable_name_collisions)]
if u64::from(size) > data.stream_len()? { if u64::from(size) > data.stream_len()? {
return Err(FileDecodingError::new( decode_err!(@BAIL APE, "APE tag has an invalid size (> file size)");
FileType::APE,
"APE tag has an invalid size (> file size)",
)
.into());
} }
Ok(ApeHeader { Ok(ApeHeader {

View file

@ -1,5 +1,5 @@
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::decode_err;
use crate::properties::FileProperties; use crate::properties::FileProperties;
use std::convert::TryInto; use std::convert::TryInto;
@ -81,7 +81,7 @@ where
{ {
let version = data let version = data
.read_u16::<LittleEndian>() .read_u16::<LittleEndian>()
.map_err(|_| FileDecodingError::new(FileType::APE, "Unable to read APE tag version"))?; .map_err(|_| decode_err!(APE, "Unable to read APE tag version"))?;
// Property reading differs between versions // Property reading differs between versions
if version >= 3980 { if version >= 3980 {
@ -103,9 +103,9 @@ where
// First read the file descriptor // First read the file descriptor
let mut descriptor = [0; 46]; let mut descriptor = [0; 46];
data.read_exact(&mut descriptor).map_err(|_| { data.read_exact(&mut descriptor).map_err(|_| {
FileDecodingError::new( decode_err!(
FileType::APE, APE,
"Not enough data left in reader to finish file descriptor", "Not enough data left in reader to finish file descriptor"
) )
})?; })?;
@ -122,12 +122,8 @@ where
// Move on to the header // Move on to the header
let mut header = [0; 24]; let mut header = [0; 24];
data.read_exact(&mut header).map_err(|_| { data.read_exact(&mut header)
FileDecodingError::new( .map_err(|_| decode_err!(APE, "Not enough data left in reader to finish MAC header"))?;
FileType::APE,
"Not enough data left in reader to finish MAC header",
)
})?;
// Skip the first 4 bytes of the header // Skip the first 4 bytes of the header
// Compression type (2) // Compression type (2)
@ -139,7 +135,7 @@ where
let total_frames = header_read.read_u32::<LittleEndian>()?; let total_frames = header_read.read_u32::<LittleEndian>()?;
if total_frames == 0 { if total_frames == 0 {
return Err(FileDecodingError::new(FileType::APE, "File contains no frames").into()); decode_err!(@BAIL APE, "File contains no frames");
} }
let bits_per_sample = header_read.read_u16::<LittleEndian>()?; let bits_per_sample = header_read.read_u16::<LittleEndian>()?;
@ -147,11 +143,7 @@ where
let channels = header_read.read_u16::<LittleEndian>()?; let channels = header_read.read_u16::<LittleEndian>()?;
if !(1..=32).contains(&channels) { if !(1..=32).contains(&channels) {
return Err(FileDecodingError::new( decode_err!(@BAIL APE, "File has an invalid channel count (must be between 1 and 32 inclusive)");
FileType::APE,
"File has an invalid channel count (must be between 1 and 32 inclusive)",
)
.into());
} }
let sample_rate = header_read.read_u32::<LittleEndian>()?; let sample_rate = header_read.read_u32::<LittleEndian>()?;
@ -187,12 +179,8 @@ where
{ {
// Versions < 3980 don't have a descriptor // Versions < 3980 don't have a descriptor
let mut header = [0; 26]; let mut header = [0; 26];
data.read_exact(&mut header).map_err(|_| { data.read_exact(&mut header)
FileDecodingError::new( .map_err(|_| decode_err!(APE, "Not enough data left in reader to finish MAC header"))?;
FileType::APE,
"Not enough data left in reader to finish MAC header",
)
})?;
// We don't need all the header data, so just make 2 slices // We don't need all the header data, so just make 2 slices
let header_first = &mut &header[..8]; let header_first = &mut &header[..8];
@ -223,11 +211,7 @@ where
let channels = header_first.read_u16::<LittleEndian>()?; let channels = header_first.read_u16::<LittleEndian>()?;
if !(1..=32).contains(&channels) { if !(1..=32).contains(&channels) {
return Err(FileDecodingError::new( decode_err!(@BAIL APE, "File has an invalid channel count (must be between 1 and 32 inclusive)");
FileType::APE,
"File has an invalid channel count (must be between 1 and 32 inclusive)",
)
.into());
} }
let sample_rate = header_first.read_u32::<LittleEndian>()?; let sample_rate = header_first.read_u32::<LittleEndian>()?;
@ -236,7 +220,7 @@ where
let total_frames = header_second.read_u32::<LittleEndian>()?; let total_frames = header_second.read_u32::<LittleEndian>()?;
if total_frames == 0 { if total_frames == 0 {
return Err(FileDecodingError::new(FileType::APE, "File contains no frames").into()); decode_err!(@BAIL APE, "File contains no frames");
} }
let final_frame_blocks = data.read_u32::<LittleEndian>()?; let final_frame_blocks = data.read_u32::<LittleEndian>()?;

View file

@ -3,13 +3,13 @@ use super::header::read_ape_header;
#[cfg(feature = "ape")] #[cfg(feature = "ape")]
use super::tag::{read::read_ape_tag, ApeTag}; use super::tag::{read::read_ape_tag, ApeTag};
use super::{ApeFile, ApeProperties}; use super::{ApeFile, ApeProperties};
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType;
#[cfg(feature = "id3v1")] #[cfg(feature = "id3v1")]
use crate::id3::v1::tag::ID3v1Tag; use crate::id3::v1::tag::ID3v1Tag;
#[cfg(feature = "id3v2")] #[cfg(feature = "id3v2")]
use crate::id3::v2::{read::parse_id3v2, tag::ID3v2Tag}; use crate::id3::v2::{read::parse_id3v2, tag::ID3v2Tag};
use crate::id3::{find_id3v1, find_id3v2, find_lyrics3v2, ID3FindResults}; use crate::id3::{find_id3v1, find_id3v2, find_lyrics3v2, ID3FindResults};
use crate::macros::decode_err;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -69,16 +69,14 @@ where
// Get the remaining part of the ape tag // Get the remaining part of the ape tag
let mut remaining = [0; 4]; let mut remaining = [0; 4];
data.read_exact(&mut remaining).map_err(|_| { data.read_exact(&mut remaining).map_err(|_| {
FileDecodingError::new( decode_err!(
FileType::APE, APE,
"Found partial APE tag, but there isn't enough data left in the reader", "Found partial APE tag, but there isn't enough data left in the reader"
) )
})?; })?;
if &remaining[..4] != b"AGEX" { if &remaining[..4] != b"AGEX" {
return Err( decode_err!(@BAIL APE, "Found incomplete APE tag");
FileDecodingError::new(FileType::APE, "Found incomplete APE tag").into(),
);
} }
let ape_header = read_ape_header(data, false)?; let ape_header = read_ape_header(data, false)?;
@ -94,12 +92,7 @@ where
data.seek(SeekFrom::Current(ape_header.size as i64))?; data.seek(SeekFrom::Current(ape_header.size as i64))?;
}, },
_ => { _ => {
return Err(FileDecodingError::new( decode_err!(@BAIL APE, "Invalid data found while reading header, expected any of [\"MAC \", \"APETAGEX\", \"ID3\"]")
FileType::APE,
"Invalid data found while reading header, expected any of [\"MAC \", \
\"APETAGEX\", \"ID3\"]",
)
.into())
}, },
} }
} }

View file

@ -1,6 +1,6 @@
use crate::ape::constants::INVALID_KEYS; use crate::ape::constants::INVALID_KEYS;
use crate::error::{FileDecodingError, LoftyError, Result}; use crate::error::{LoftyError, Result};
use crate::file::FileType; use crate::macros::decode_err;
use crate::tag::item::{ItemValue, ItemValueRef, TagItem}; use crate::tag::item::{ItemValue, ItemValueRef, TagItem};
use crate::tag::TagType; use crate::tag::TagType;
@ -28,27 +28,15 @@ impl ApeItem {
/// * `key` contains invalid characters (must be in the range 0x20 to 0x7E, inclusive) /// * `key` contains invalid characters (must be in the range 0x20 to 0x7E, inclusive)
pub fn new(key: String, value: ItemValue) -> Result<Self> { pub fn new(key: String, value: ItemValue) -> Result<Self> {
if INVALID_KEYS.contains(&&*key.to_uppercase()) { if INVALID_KEYS.contains(&&*key.to_uppercase()) {
return Err(FileDecodingError::new( decode_err!(@BAIL APE, "APE tag item contains an illegal key");
FileType::APE,
"APE tag item contains an illegal key",
)
.into());
} }
if !(2..=255).contains(&key.len()) { if !(2..=255).contains(&key.len()) {
return Err(FileDecodingError::new( decode_err!(@BAIL APE, "APE tag item key has an invalid length (< 2 || > 255)");
FileType::APE,
"APE tag item key has an invalid length (< 2 || > 255)",
)
.into());
} }
if key.chars().any(|c| !(0x20..=0x7E).contains(&(c as u32))) { if key.chars().any(|c| !(0x20..=0x7E).contains(&(c as u32))) {
return Err(FileDecodingError::new( decode_err!(@BAIL APE, "APE tag item key contains invalid characters");
FileType::APE,
"APE tag item key contains invalid characters",
)
.into());
} }
Ok(Self { Ok(Self {
@ -86,12 +74,7 @@ impl TryFrom<TagItem> for ApeItem {
value value
.item_key .item_key
.map_key(TagType::APE, false) .map_key(TagType::APE, false)
.ok_or_else(|| { .ok_or_else(|| decode_err!(APE, "Attempted to convert an unsupported item key"))?
FileDecodingError::new(
FileType::APE,
"Attempted to convert an unsupported item key",
)
})?
.to_string(), .to_string(),
value.item_value, value.item_value,
) )

View file

@ -2,9 +2,8 @@ use super::item::ApeItem;
use super::ApeTag; use super::ApeTag;
use crate::ape::constants::INVALID_KEYS; use crate::ape::constants::INVALID_KEYS;
use crate::ape::header::ApeHeader; use crate::ape::header::ApeHeader;
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::{decode_err, err, try_vec};
use crate::macros::{err, try_vec};
use crate::tag::item::ItemValue; use crate::tag::item::ItemValue;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -39,16 +38,11 @@ where
key_char = data.read_u8()?; key_char = data.read_u8()?;
} }
let key = String::from_utf8(key).map_err(|_| { let key = String::from_utf8(key)
FileDecodingError::new(FileType::APE, "APE tag item contains a non UTF-8 key") .map_err(|_| decode_err!(APE, "APE tag item contains a non UTF-8 key"))?;
})?;
if INVALID_KEYS.contains(&&*key.to_uppercase()) { if INVALID_KEYS.contains(&&*key.to_uppercase()) {
return Err(FileDecodingError::new( decode_err!(@BAIL APE, "APE tag item contains an illegal key");
FileType::APE,
"APE tag item contains an illegal key",
)
.into());
} }
let read_only = (flags & 1) == 1; let read_only = (flags & 1) == 1;
@ -64,25 +58,13 @@ where
let parsed_value = match item_type { let parsed_value = match item_type {
0 => ItemValue::Text(String::from_utf8(value).map_err(|_| { 0 => ItemValue::Text(String::from_utf8(value).map_err(|_| {
FileDecodingError::new( decode_err!(APE, "Failed to convert text item into a UTF-8 string")
FileType::APE,
"Failed to convert text item into a UTF-8 string",
)
})?), })?),
1 => ItemValue::Binary(value), 1 => ItemValue::Binary(value),
2 => ItemValue::Locator(String::from_utf8(value).map_err(|_| { 2 => ItemValue::Locator(String::from_utf8(value).map_err(|_| {
FileDecodingError::new( decode_err!(APE, "Failed to convert locator item into a UTF-8 string")
FileType::APE,
"Failed to convert locator item into a UTF-8 string",
)
})?), })?),
_ => { _ => decode_err!(@BAIL APE, "APE tag item contains an invalid item type"),
return Err(FileDecodingError::new(
FileType::APE,
"APE tag item contains an invalid item type",
)
.into())
},
}; };
let mut item = ApeItem::new(key, parsed_value)?; let mut item = ApeItem::new(key, parsed_value)?;

View file

@ -3,10 +3,10 @@ use super::read::read_ape_tag;
use super::ApeTagRef; use super::ApeTagRef;
use crate::ape::constants::APE_PREAMBLE; use crate::ape::constants::APE_PREAMBLE;
use crate::ape::header::read_ape_header; use crate::ape::header::read_ape_header;
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::file::FileType;
use crate::id3::{find_id3v1, find_id3v2, find_lyrics3v2}; use crate::id3::{find_id3v1, find_id3v2, find_lyrics3v2};
use crate::macros::err; use crate::macros::{decode_err, err};
use crate::probe::Probe; use crate::probe::Probe;
use crate::tag::item::ItemValueRef; use crate::tag::item::ItemValueRef;
@ -97,11 +97,7 @@ where
if let Some(start) = start.checked_sub(size as usize) { if let Some(start) = start.checked_sub(size as usize) {
ape_tag_location = Some(start..start + size as usize); ape_tag_location = Some(start..start + size as usize);
} else { } else {
return Err(FileDecodingError::new( decode_err!(@BAIL APE, "File has a tag with an invalid size");
FileType::APE,
"File has a tag with an invalid size",
)
.into());
} }
} }

View file

@ -1,10 +1,10 @@
use super::block::Block; use super::block::Block;
use super::FlacFile; use super::FlacFile;
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType;
#[cfg(feature = "id3v2")] #[cfg(feature = "id3v2")]
use crate::id3::v2::read::parse_id3v2; use crate::id3::v2::read::parse_id3v2;
use crate::id3::{find_id3v2, ID3FindResults}; use crate::id3::{find_id3v2, ID3FindResults};
use crate::macros::decode_err;
use crate::properties::FileProperties; use crate::properties::FileProperties;
#[cfg(feature = "vorbis_comments")] #[cfg(feature = "vorbis_comments")]
use crate::{ use crate::{
@ -22,19 +22,13 @@ where
data.read_exact(&mut marker)?; data.read_exact(&mut marker)?;
if &marker != b"fLaC" { if &marker != b"fLaC" {
return Err( decode_err!(@BAIL FLAC, "File missing \"fLaC\" stream marker");
FileDecodingError::new(FileType::FLAC, "File missing \"fLaC\" stream marker").into(),
);
} }
let block = Block::read(data)?; let block = Block::read(data)?;
if block.ty != 0 { if block.ty != 0 {
return Err(FileDecodingError::new( decode_err!(@BAIL FLAC, "File missing mandatory STREAMINFO block");
FileType::FLAC,
"File missing mandatory STREAMINFO block",
)
.into());
} }
Ok(block) Ok(block)
@ -67,11 +61,7 @@ where
let stream_info_len = (stream_info.end - stream_info.start) as u32; let stream_info_len = (stream_info.end - stream_info.start) as u32;
if stream_info_len < 18 { if stream_info_len < 18 {
return Err(FileDecodingError::new( decode_err!(@BAIL FLAC, "File has an invalid STREAMINFO block size (< 18)");
FileType::FLAC,
"File has an invalid STREAMINFO block size (< 18)",
)
.into());
} }
let mut last_block = stream_info.last; let mut last_block = stream_info.last;
@ -88,11 +78,7 @@ where
last_block = block.last; last_block = block.last;
if block.content.is_empty() && (block.ty != 1 && block.ty != 3) { if block.content.is_empty() && (block.ty != 1 && block.ty != 3) {
return Err(FileDecodingError::new( decode_err!(@BAIL FLAC, "Encountered a zero-sized metadata block");
FileType::FLAC,
"Encountered a zero-sized metadata block",
)
.into());
} }
match block.ty { match block.ty {

View file

@ -1,5 +1,5 @@
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::decode_err;
use crate::properties::FileProperties; use crate::properties::FileProperties;
use std::io::Read; use std::io::Read;
@ -15,7 +15,7 @@ pub(super) fn read_properties(
let channels = comm.read_u16::<BigEndian>()? as u8; let channels = comm.read_u16::<BigEndian>()? as u8;
if channels == 0 { if channels == 0 {
return Err(FileDecodingError::new(FileType::AIFF, "File contains 0 channels").into()); decode_err!(@BAIL AIFF, "File contains 0 channels");
} }
let sample_frames = comm.read_u32::<BigEndian>()?; let sample_frames = comm.read_u32::<BigEndian>()?;

View file

@ -1,12 +1,11 @@
#[cfg(feature = "aiff_text_chunks")] #[cfg(feature = "aiff_text_chunks")]
use super::tag::{AIFFTextChunks, Comment}; use super::tag::{AIFFTextChunks, Comment};
use super::AiffFile; use super::AiffFile;
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType;
#[cfg(feature = "id3v2")] #[cfg(feature = "id3v2")]
use crate::id3::v2::tag::ID3v2Tag; use crate::id3::v2::tag::ID3v2Tag;
use crate::iff::chunk::Chunks; use crate::iff::chunk::Chunks;
use crate::macros::err; use crate::macros::{decode_err, err};
use crate::properties::FileProperties; use crate::properties::FileProperties;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -63,11 +62,7 @@ where
b"ID3 " | b"id3 " => id3v2_tag = Some(chunks.id3_chunk(data)?), b"ID3 " | b"id3 " => id3v2_tag = Some(chunks.id3_chunk(data)?),
b"COMM" if read_properties && comm.is_none() => { b"COMM" if read_properties && comm.is_none() => {
if chunks.size < 18 { if chunks.size < 18 {
return Err(FileDecodingError::new( decode_err!(@BAIL AIFF, "File has an invalid \"COMM\" chunk size (< 18)");
FileType::AIFF,
"File has an invalid \"COMM\" chunk size (< 18)",
)
.into());
} }
comm = Some(chunks.content(data)?); comm = Some(chunks.content(data)?);
@ -139,11 +134,7 @@ where
match comm { match comm {
Some(comm) => { Some(comm) => {
if stream_len == 0 { if stream_len == 0 {
return Err(FileDecodingError::new( decode_err!(@BAIL AIFF, "File does not contain a \"SSND\" chunk");
FileType::AIFF,
"File does not contain a \"SSND\" chunk",
)
.into());
} }
properties = super::properties::read_properties( properties = super::properties::read_properties(
@ -152,13 +143,7 @@ where
data.stream_position()?, data.stream_position()?,
)?; )?;
}, },
None => { None => decode_err!(@BAIL AIFF, "File does not contain a \"COMM\" chunk"),
return Err(FileDecodingError::new(
FileType::AIFF,
"File does not contain a \"COMM\" chunk",
)
.into());
},
} }
} else { } else {
properties = FileProperties::default(); properties = FileProperties::default();

View file

@ -1,5 +1,5 @@
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::decode_err;
use crate::properties::FileProperties; use crate::properties::FileProperties;
use std::time::Duration; use std::time::Duration;
@ -98,7 +98,7 @@ pub(super) fn read_properties(
let channels = fmt.read_u16::<LittleEndian>()? as u8; let channels = fmt.read_u16::<LittleEndian>()? as u8;
if channels == 0 { if channels == 0 {
return Err(FileDecodingError::new(FileType::WAV, "File contains 0 channels").into()); decode_err!(@BAIL WAV, "File contains 0 channels");
} }
let sample_rate = fmt.read_u32::<LittleEndian>()?; let sample_rate = fmt.read_u32::<LittleEndian>()?;
@ -117,11 +117,7 @@ pub(super) fn read_properties(
if format_tag == EXTENSIBLE { if format_tag == EXTENSIBLE {
if fmt.len() + 16 < 40 { if fmt.len() + 16 < 40 {
return Err(FileDecodingError::new( decode_err!(@BAIL WAV, "Extensible format identified, invalid \"fmt \" chunk size found (< 40)");
FileType::WAV,
"Extensible format identified, invalid \"fmt \" chunk size found (< 40)",
)
.into());
} }
// cbSize (Size of extra format information) (2) // cbSize (Size of extra format information) (2)
@ -140,11 +136,7 @@ pub(super) fn read_properties(
let non_pcm = format_tag != PCM && format_tag != IEEE_FLOAT; let non_pcm = format_tag != PCM && format_tag != IEEE_FLOAT;
if non_pcm && total_samples == 0 { if non_pcm && total_samples == 0 {
return Err(FileDecodingError::new( decode_err!(@BAIL WAV, "Non-PCM format identified, no \"fact\" chunk found");
FileType::WAV,
"Non-PCM format identified, no \"fact\" chunk found",
)
.into());
} }
if bits_per_sample > 0 { if bits_per_sample > 0 {

View file

@ -2,11 +2,11 @@ use super::properties::WavProperties;
#[cfg(feature = "riff_info_list")] #[cfg(feature = "riff_info_list")]
use super::tag::RIFFInfoList; use super::tag::RIFFInfoList;
use super::WavFile; use super::WavFile;
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType;
#[cfg(feature = "id3v2")] #[cfg(feature = "id3v2")]
use crate::id3::v2::tag::ID3v2Tag; use crate::id3::v2::tag::ID3v2Tag;
use crate::iff::chunk::Chunks; use crate::iff::chunk::Chunks;
use crate::macros::decode_err;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -20,15 +20,11 @@ where
data.read_exact(&mut id)?; data.read_exact(&mut id)?;
if &id[..4] != b"RIFF" { if &id[..4] != b"RIFF" {
return Err( decode_err!(@BAIL WAV, "WAV file doesn't contain a RIFF chunk");
FileDecodingError::new(FileType::WAV, "WAV file doesn't contain a RIFF chunk").into(),
);
} }
if &id[8..] != b"WAVE" { if &id[8..] != b"WAVE" {
return Err( decode_err!(@BAIL WAV, "Found RIFF file, format is not WAVE");
FileDecodingError::new(FileType::WAV, "Found RIFF file, format is not WAVE").into(),
);
} }
Ok(()) Ok(())
@ -103,19 +99,11 @@ where
let properties = if read_properties { let properties = if read_properties {
if fmt.len() < 16 { if fmt.len() < 16 {
return Err(FileDecodingError::new( decode_err!(@BAIL WAV, "File does not contain a valid \"fmt \" chunk");
FileType::WAV,
"File does not contain a valid \"fmt \" chunk",
)
.into());
} }
if stream_len == 0 { if stream_len == 0 {
return Err(FileDecodingError::new( decode_err!(@BAIL WAV, "File does not contain a \"data\" chunk");
FileType::WAV,
"File does not contain a \"data\" chunk",
)
.into());
} }
let file_length = data.stream_position()?; let file_length = data.stream_position()?;

View file

@ -1,7 +1,7 @@
use super::RIFFInfoList; use super::RIFFInfoList;
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType;
use crate::iff::chunk::Chunks; use crate::iff::chunk::Chunks;
use crate::macros::decode_err;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -17,23 +17,18 @@ where
R: Read + Seek, R: Read + Seek,
{ {
while data.stream_position()? != end && chunks.next(data).is_ok() { while data.stream_position()? != end && chunks.next(data).is_ok() {
let key_str = String::from_utf8(chunks.fourcc.to_vec()).map_err(|_| { let key_str = String::from_utf8(chunks.fourcc.to_vec())
FileDecodingError::new(FileType::WAV, "Non UTF-8 item key found in RIFF INFO") .map_err(|_| decode_err!(WAV, "Non UTF-8 item key found in RIFF INFO"))?;
})?;
if !verify_key(&key_str) { if !verify_key(&key_str) {
return Err(FileDecodingError::new( decode_err!(@BAIL WAV, "RIFF INFO item key contains invalid characters");
FileType::WAV,
"RIFF INFO item key contains invalid characters",
)
.into());
} }
tag.items.push(( tag.items.push((
key_str, key_str,
chunks.read_cstring(data).map_err(|_| { chunks
FileDecodingError::new(FileType::WAV, "Failed to read RIFF INFO item value") .read_cstring(data)
})?, .map_err(|_| decode_err!(WAV, "Failed to read RIFF INFO item value"))?,
)); ));
} }

View file

@ -40,4 +40,32 @@ macro_rules! err {
}; };
} }
pub(crate) use {err, try_vec}; // Shorthand for FileDecodingError::new(FileType::Foo, "Message")
//
// Usage:
//
// - decode_err!(Variant, Message)
// - decode_err!(Message)
//
// or bail:
//
// - decode_err!(@BAIL Variant, Message)
// - decode_err!(@BAIL Message)
macro_rules! decode_err {
($file_ty:ident, $reason:literal) => {
Into::<crate::error::LoftyError>::into(crate::error::FileDecodingError::new(
crate::file::FileType::$file_ty,
$reason,
))
};
($reason:literal) => {
Into::<crate::error::LoftyError>::into(crate::error::FileDecodingError::from_description(
$reason,
))
};
(@BAIL $($file_ty:ident,)? $reason:literal) => {
return Err(decode_err!($($file_ty,)? $reason))
};
}
pub(crate) use {decode_err, err, try_vec};

View file

@ -6,8 +6,8 @@ use super::{
ilst::{read::parse_ilst, Ilst}, ilst::{read::parse_ilst, Ilst},
read::{meta_is_full, AtomReader}, read::{meta_is_full, AtomReader},
}; };
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::decode_err;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -37,7 +37,7 @@ impl Moov {
if let Some(moov) = moov { if let Some(moov) = moov {
Ok(moov) Ok(moov)
} else { } else {
Err(FileDecodingError::new(FileType::MP4, "No \"moov\" atom found").into()) decode_err!(@BAIL MP4, "No \"moov\" atom found");
} }
} }

View file

@ -1,9 +1,8 @@
use super::atom_info::{AtomIdent, AtomInfo}; use super::atom_info::{AtomIdent, AtomInfo};
use super::read::{nested_atom, skip_unneeded, AtomReader}; use super::read::{nested_atom, skip_unneeded, AtomReader};
use super::trak::Trak; use super::trak::Trak;
use crate::error::{FileDecodingError, LoftyError, Result}; use crate::error::{LoftyError, Result};
use crate::file::FileType; use crate::macros::{decode_err, err, try_vec};
use crate::macros::{err, try_vec};
use crate::properties::FileProperties; use crate::properties::FileProperties;
use std::io::{Cursor, Read, Seek, SeekFrom}; use std::io::{Cursor, Read, Seek, SeekFrom};
@ -135,11 +134,7 @@ impl TryFrom<u8> for AudioObjectType {
44 => Ok(Self::LowDelayMpegSurround), 44 => Ok(Self::LowDelayMpegSurround),
45 => Ok(Self::SpatialAudioObjectCodingDialogueEnhancement), 45 => Ok(Self::SpatialAudioObjectCodingDialogueEnhancement),
46 => Ok(Self::AudioSync), 46 => Ok(Self::AudioSync),
_ => Err(FileDecodingError::new( _ => decode_err!(@BAIL MP4, "Encountered an invalid audio object type"),
FileType::MP4,
"Encountered an invalid audio object type",
)
.into()),
} }
} }
} }
@ -276,7 +271,7 @@ where
} }
if !audio_track { if !audio_track {
return Err(FileDecodingError::new(FileType::MP4, "File contains no audio tracks").into()); decode_err!(@BAIL MP4, "File contains no audio tracks");
} }
let mdhd = match mdhd { let mdhd = match mdhd {
@ -657,7 +652,7 @@ where
skip_unneeded(reader, atom.extended, atom.len)?; skip_unneeded(reader, atom.extended, atom.len)?;
} }
Err(FileDecodingError::new(FileType::MP4, "Failed to find \"mdat\" atom").into()) decode_err!(@BAIL MP4, "Failed to find \"mdat\" atom");
} }
struct Descriptor { struct Descriptor {

View file

@ -2,9 +2,8 @@ use super::atom_info::{AtomIdent, AtomInfo};
use super::moov::Moov; use super::moov::Moov;
use super::properties::Mp4Properties; use super::properties::Mp4Properties;
use super::Mp4File; use super::Mp4File;
use crate::error::{ErrorKind, FileDecodingError, LoftyError, Result}; use crate::error::{ErrorKind, LoftyError, Result};
use crate::file::FileType; use crate::macros::{decode_err, err};
use crate::macros::err;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -136,7 +135,7 @@ where
// size + identifier + major brand // size + identifier + major brand
// There *should* be more, but this is all we need from it // There *should* be more, but this is all we need from it
if atom.len < 12 { if atom.len < 12 {
return Err(FileDecodingError::new(FileType::MP4, "\"ftyp\" atom too short").into()); decode_err!(@BAIL MP4, "\"ftyp\" atom too short");
} }
let mut major_brand = vec![0; 4]; let mut major_brand = vec![0; 4];

View file

@ -1,6 +1,6 @@
use super::constants::{BITRATES, PADDING_SIZES, SAMPLES, SAMPLE_RATES, SIDE_INFORMATION_SIZES}; use super::constants::{BITRATES, PADDING_SIZES, SAMPLES, SAMPLE_RATES, SIDE_INFORMATION_SIZES};
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::decode_err;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -307,11 +307,7 @@ impl XingHeader {
match &header { match &header {
b"Xing" | b"Info" => { b"Xing" | b"Info" => {
if reader_len < 16 { if reader_len < 16 {
return Err(FileDecodingError::new( decode_err!(@BAIL MPEG, "Xing header has an invalid size (< 16)");
FileType::MPEG,
"Xing header has an invalid size (< 16)",
)
.into());
} }
let mut flags = [0; 4]; let mut flags = [0; 4];
@ -334,11 +330,7 @@ impl XingHeader {
}, },
b"VBRI" => { b"VBRI" => {
if reader_len < 32 { if reader_len < 32 {
return Err(FileDecodingError::new( decode_err!(@BAIL MPEG, "VBRI header has an invalid size (< 32)");
FileType::MPEG,
"VBRI header has an invalid size (< 32)",
)
.into());
} }
// Skip 6 bytes // Skip 6 bytes

View file

@ -4,13 +4,12 @@ use crate::ape::constants::APE_PREAMBLE;
use crate::ape::header::read_ape_header; use crate::ape::header::read_ape_header;
#[cfg(feature = "ape")] #[cfg(feature = "ape")]
use crate::ape::tag::read::read_ape_tag; use crate::ape::tag::read::read_ape_tag;
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType;
#[cfg(feature = "id3v2")] #[cfg(feature = "id3v2")]
use crate::id3::v2::read::parse_id3v2; use crate::id3::v2::read::parse_id3v2;
use crate::id3::v2::read_id3v2_header; use crate::id3::v2::read_id3v2_header;
use crate::id3::{find_id3v1, find_lyrics3v2, ID3FindResults}; use crate::id3::{find_id3v1, find_lyrics3v2, ID3FindResults};
use crate::macros::err; use crate::macros::{decode_err, err};
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -138,18 +137,12 @@ where
if read_properties { if read_properties {
let first_frame_header = match first_frame_header { let first_frame_header = match first_frame_header {
Some(header) => header, Some(header) => header,
None => { // The search for sync bits was unsuccessful
// The search for sync bits was unsuccessful None => decode_err!(@BAIL MPEG, "File contains an invalid frame"),
return Err(FileDecodingError::new(
FileType::MPEG,
"File contains an invalid frame",
)
.into());
},
}; };
if first_frame_header.sample_rate == 0 { if first_frame_header.sample_rate == 0 {
return Err(FileDecodingError::new(FileType::MPEG, "Sample rate is 0").into()); decode_err!(@BAIL MPEG, "Sample rate is 0");
} }
let first_frame_offset = first_frame_offset; let first_frame_offset = first_frame_offset;

View file

@ -9,8 +9,8 @@ pub(crate) mod read;
pub(crate) mod speex; pub(crate) mod speex;
pub(crate) mod vorbis; pub(crate) mod vorbis;
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::decode_err;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -38,9 +38,7 @@ pub(self) fn verify_signature(page: &Page, sig: &[u8]) -> Result<()> {
let sig_len = sig.len(); let sig_len = sig.len();
if page.content().len() < sig_len || &page.content()[..sig_len] != sig { if page.content().len() < sig_len || &page.content()[..sig_len] != sig {
return Err( decode_err!(@BAIL Vorbis, "File missing magic signature");
FileDecodingError::new(FileType::Vorbis, "File missing magic signature").into(),
);
} }
Ok(()) Ok(())

View file

@ -1,6 +1,6 @@
use super::find_last_page; use super::find_last_page;
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::decode_err;
use crate::properties::FileProperties; use crate::properties::FileProperties;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -100,11 +100,7 @@ where
if (channel_mapping_family == 0 && properties.channels > 2) if (channel_mapping_family == 0 && properties.channels > 2)
|| (channel_mapping_family == 1 && properties.channels > 8) || (channel_mapping_family == 1 && properties.channels > 8)
{ {
return Err(FileDecodingError::new( decode_err!(@BAIL Opus, "Invalid channel count for mapping family");
FileType::Opus,
"Invalid channel count for mapping family",
)
.into());
} }
// Subtract the identification and metadata packet length from the total // Subtract the identification and metadata packet length from the total

View file

@ -2,7 +2,7 @@
use super::tag::VorbisComments; use super::tag::VorbisComments;
use super::verify_signature; use super::verify_signature;
use crate::error::Result; use crate::error::Result;
use crate::macros::err; use crate::macros::{decode_err, err};
#[cfg(feature = "vorbis_comments")] #[cfg(feature = "vorbis_comments")]
use crate::picture::Picture; use crate::picture::Picture;
@ -23,7 +23,6 @@ pub(crate) fn read_comments<R>(data: &mut R, mut len: u64, tag: &mut VorbisComme
where where
R: Read, R: Read,
{ {
use crate::error::FileDecodingError;
use crate::macros::try_vec; use crate::macros::try_vec;
let vendor_len = data.read_u32::<LittleEndian>()?; let vendor_len = data.read_u32::<LittleEndian>()?;
@ -50,12 +49,7 @@ where
match String::from_utf16(&s) { match String::from_utf16(&s) {
Ok(vendor) => vendor, Ok(vendor) => vendor,
Err(_) => { Err(_) => decode_err!(@BAIL "OGG: File has an invalid vendor string"),
return Err(FileDecodingError::from_description(
"OGG: File has an invalid vendor string",
)
.into())
},
} }
}, },
}; };

View file

@ -1,5 +1,5 @@
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::decode_err;
use crate::ogg::find_last_page; use crate::ogg::find_last_page;
use crate::properties::FileProperties; use crate::properties::FileProperties;
@ -91,7 +91,7 @@ where
let first_page_abgp = first_page.abgp; let first_page_abgp = first_page.abgp;
if first_page.content().len() < 80 { if first_page.content().len() < 80 {
return Err(FileDecodingError::new(FileType::Speex, "Header packet too small").into()); decode_err!(@BAIL Speex, "Header packet too small");
} }
let mut properties = SpeexProperties::default(); let mut properties = SpeexProperties::default();
@ -117,11 +117,7 @@ where
let channels = first_page_content.read_u32::<LittleEndian>()?; let channels = first_page_content.read_u32::<LittleEndian>()?;
if channels != 1 && channels != 2 { if channels != 1 && channels != 2 {
return Err(FileDecodingError::new( decode_err!(@BAIL Speex, "Found invalid channel count, must be mono or stereo");
FileType::Speex,
"Found invalid channel count, must be mono or stereo",
)
.into());
} }
properties.channels = channels as u8; properties.channels = channels as u8;

View file

@ -1,6 +1,5 @@
use crate::error::{FileDecodingError, Result}; use crate::error::Result;
use crate::file::FileType; use crate::macros::{decode_err, err, try_vec};
use crate::macros::{err, try_vec};
use crate::properties::FileProperties; use crate::properties::FileProperties;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
@ -210,9 +209,7 @@ where
let block_size = reader.read_u32::<LittleEndian>()?; let block_size = reader.read_u32::<LittleEndian>()?;
if !(24..=WV_BLOCK_MAX_SIZE).contains(&block_size) { if !(24..=WV_BLOCK_MAX_SIZE).contains(&block_size) {
return Err( decode_err!(@BAIL WavPack, "WavPack block has an invalid size");
FileDecodingError::new(FileType::WavPack, "WavPack block has an invalid size").into(),
);
} }
let version = reader.read_u16::<LittleEndian>()?; let version = reader.read_u16::<LittleEndian>()?;
@ -267,11 +264,7 @@ where
}, },
ID_DSD => { ID_DSD => {
if size <= 1 { if size <= 1 {
return Err(FileDecodingError::new( decode_err!(@BAIL WavPack, "Encountered an invalid DSD block size");
FileType::WavPack,
"Encountered an invalid DSD block size",
)
.into());
} }
let rate_multiplier = u32::from(reader.read_u8()?); let rate_multiplier = u32::from(reader.read_u8()?);
@ -285,11 +278,7 @@ where
}, },
ID_MULTICHANNEL => { ID_MULTICHANNEL => {
if size <= 1 { if size <= 1 {
return Err(FileDecodingError::new( decode_err!(@BAIL WavPack, "Unable to extract channel information");
FileType::WavPack,
"Unable to extract channel information",
)
.into());
} }
properties.channels = reader.read_u8()?; properties.channels = reader.read_u8()?;
@ -300,13 +289,7 @@ where
continue; continue;
}, },
4 | 5 => {}, 4 | 5 => {},
_ => { _ => decode_err!(@BAIL WavPack, "Encountered invalid channel info size"),
return Err(FileDecodingError::new(
FileType::WavPack,
"Encountered invalid channel info size",
)
.into())
},
} }
reader.seek(SeekFrom::Current(1))?; reader.seek(SeekFrom::Current(1))?;