ID3v2: Ignore empty timestamp frames

I had a file with an empty timestamp frame that errored with `ParsingMode::BestAttempt`. Now that's only an error case with `ParsingMode::Strict`.
This commit is contained in:
Serial 2024-07-04 12:10:22 -04:00 committed by Alex
parent d4e58ea15d
commit fcb5446922
2 changed files with 32 additions and 8 deletions

View file

@ -96,7 +96,12 @@ impl<'a> TimestampFrame<'a> {
let reader = &mut value.as_bytes(); let reader = &mut value.as_bytes();
frame.timestamp = Timestamp::parse(reader, parse_mode)?; let Some(timestamp) = Timestamp::parse(reader, parse_mode)? else {
// Timestamp is empty
return Ok(None);
};
frame.timestamp = timestamp;
Ok(Some(frame)) Ok(Some(frame))
} }

View file

@ -70,7 +70,8 @@ impl FromStr for Timestamp {
type Err = LoftyError; type Err = LoftyError;
fn from_str(s: &str) -> Result<Self> { fn from_str(s: &str) -> Result<Self> {
Timestamp::parse(&mut s.as_bytes(), ParsingMode::BestAttempt) Timestamp::parse(&mut s.as_bytes(), ParsingMode::BestAttempt)?
.ok_or_else(|| LoftyError::new(ErrorKind::BadTimestamp("Timestamp frame is empty")))
} }
} }
@ -86,7 +87,7 @@ impl Timestamp {
/// ///
/// * Failure to read from `reader` /// * Failure to read from `reader`
/// * The timestamp is invalid /// * The timestamp is invalid
pub fn parse<R>(reader: &mut R, parse_mode: ParsingMode) -> Result<Self> pub fn parse<R>(reader: &mut R, parse_mode: ParsingMode) -> Result<Option<Self>>
where where
R: Read, R: Read,
{ {
@ -109,6 +110,14 @@ impl Timestamp {
.take(Self::MAX_LENGTH as u64) .take(Self::MAX_LENGTH as u64)
.read_to_end(&mut content)?; .read_to_end(&mut content)?;
if content.is_empty() {
if parse_mode == ParsingMode::Strict {
err!(BadTimestamp("Timestamp frame is empty"))
}
return Ok(None);
}
let reader = &mut &content[..]; let reader = &mut &content[..];
// We need to very that the year is exactly 4 bytes long. This doesn't matter for other segments. // We need to very that the year is exactly 4 bytes long. This doesn't matter for other segments.
@ -131,7 +140,7 @@ impl Timestamp {
break; break;
} }
Ok(timestamp) Ok(Some(timestamp))
} }
fn segment<const SIZE: usize>( fn segment<const SIZE: usize>(
@ -237,7 +246,7 @@ mod tests {
let parsed_timestamp = let parsed_timestamp =
Timestamp::parse(&mut content.as_bytes(), ParsingMode::Strict).unwrap(); Timestamp::parse(&mut content.as_bytes(), ParsingMode::Strict).unwrap();
assert_eq!(parsed_timestamp, expected()); assert_eq!(parsed_timestamp, Some(expected()));
} }
#[test] #[test]
@ -248,7 +257,7 @@ mod tests {
let parsed_timestamp = let parsed_timestamp =
Timestamp::parse(&mut content.as_bytes(), ParsingMode::BestAttempt).unwrap(); Timestamp::parse(&mut content.as_bytes(), ParsingMode::BestAttempt).unwrap();
assert_eq!(parsed_timestamp, expected()); assert_eq!(parsed_timestamp, Some(expected()));
} }
#[test] #[test]
@ -259,7 +268,7 @@ mod tests {
let parsed_timestamp = let parsed_timestamp =
Timestamp::parse(&mut content.as_bytes(), ParsingMode::BestAttempt).unwrap(); Timestamp::parse(&mut content.as_bytes(), ParsingMode::BestAttempt).unwrap();
assert_eq!(parsed_timestamp, expected()); assert_eq!(parsed_timestamp, Some(expected()));
} }
#[test] #[test]
@ -348,7 +357,17 @@ mod tests {
for (data, expected) in partial_timestamps { for (data, expected) in partial_timestamps {
let parsed_timestamp = Timestamp::parse(&mut &data[..], ParsingMode::Strict).unwrap(); let parsed_timestamp = Timestamp::parse(&mut &data[..], ParsingMode::Strict).unwrap();
assert_eq!(parsed_timestamp, expected); assert_eq!(parsed_timestamp, Some(expected));
} }
} }
#[test]
fn empty_timestamp() {
let empty_timestamp =
Timestamp::parse(&mut "".as_bytes(), ParsingMode::BestAttempt).unwrap();
assert!(empty_timestamp.is_none());
let empty_timestamp_strict = Timestamp::parse(&mut "".as_bytes(), ParsingMode::Strict);
assert!(empty_timestamp_strict.is_err());
}
} }