mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2025-03-04 14:57:17 +00:00
Reduce memory allocations for id3v2::FrameID
This commit is contained in:
parent
a5b1ccaff0
commit
738d47fd4f
6 changed files with 111 additions and 86 deletions
|
@ -3,9 +3,12 @@ use crate::error::{ID3v2Error, ID3v2ErrorKind, Result};
|
|||
use crate::id3::v2::util::upgrade::{upgrade_v2, upgrade_v3};
|
||||
use crate::id3::v2::FrameID;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io::Read;
|
||||
|
||||
pub(crate) fn parse_v2_header<R>(reader: &mut R) -> Result<Option<(FrameID, u32, FrameFlags)>>
|
||||
pub(crate) fn parse_v2_header<R>(
|
||||
reader: &mut R,
|
||||
) -> Result<Option<(FrameID<'static>, u32, FrameFlags)>>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
|
@ -22,7 +25,7 @@ where
|
|||
|
||||
let id_str = std::str::from_utf8(&frame_header[..3])
|
||||
.map_err(|_| ID3v2Error::new(ID3v2ErrorKind::BadFrameID))?;
|
||||
let id = upgrade_v2(id_str).unwrap_or(id_str);
|
||||
let id = upgrade_v2(id_str).map_or_else(|| Cow::Owned(id_str.to_owned()), Cow::Borrowed);
|
||||
|
||||
let frame_id = FrameID::new(id)?;
|
||||
|
||||
|
@ -35,7 +38,7 @@ where
|
|||
pub(crate) fn parse_header<R>(
|
||||
reader: &mut R,
|
||||
synchsafe: bool,
|
||||
) -> Result<Option<(FrameID, u32, FrameFlags)>>
|
||||
) -> Result<Option<(FrameID<'static>, u32, FrameFlags)>>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
|
@ -59,7 +62,7 @@ where
|
|||
frame_id_end = 3;
|
||||
}
|
||||
|
||||
let mut id_str = std::str::from_utf8(&frame_header[..frame_id_end])
|
||||
let id_str = std::str::from_utf8(&frame_header[..frame_id_end])
|
||||
.map_err(|_| ID3v2Error::new(ID3v2ErrorKind::BadFrameID))?;
|
||||
|
||||
let mut size = u32::from_be_bytes([
|
||||
|
@ -70,21 +73,24 @@ where
|
|||
]);
|
||||
|
||||
// Now upgrade the FrameID
|
||||
if invalid_v2_frame {
|
||||
let id = if invalid_v2_frame {
|
||||
if let Some(id) = upgrade_v2(id_str) {
|
||||
id_str = id;
|
||||
Cow::Borrowed(id)
|
||||
} else {
|
||||
Cow::Owned(id_str.to_owned())
|
||||
}
|
||||
} else if !synchsafe {
|
||||
id_str = upgrade_v3(id_str).unwrap_or(id_str);
|
||||
}
|
||||
upgrade_v3(id_str).map_or_else(|| Cow::Owned(id_str.to_owned()), Cow::Borrowed)
|
||||
} else {
|
||||
Cow::Owned(id_str.to_owned())
|
||||
};
|
||||
let frame_id = FrameID::new(id)?;
|
||||
|
||||
// unsynch the frame size if necessary
|
||||
if synchsafe {
|
||||
size = crate::id3::v2::util::unsynch_u32(size);
|
||||
}
|
||||
|
||||
let frame_id = FrameID::new(id_str)?;
|
||||
|
||||
let flags = u16::from_be_bytes([frame_header[8], frame_header[9]]);
|
||||
let flags = parse_flags(flags, synchsafe);
|
||||
|
||||
|
|
|
@ -1,33 +1,35 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::error::{ID3v2Error, ID3v2ErrorKind, LoftyError, Result};
|
||||
use crate::tag::item::ItemKey;
|
||||
use crate::tag::TagType;
|
||||
|
||||
/// An `ID3v2` frame ID
|
||||
#[derive(PartialEq, Clone, Debug, Eq, Hash)]
|
||||
pub enum FrameID {
|
||||
pub enum FrameID<'a> {
|
||||
/// A valid `ID3v2.3/4` frame
|
||||
Valid(String),
|
||||
Valid(Cow<'a, str>),
|
||||
/// When an `ID3v2.2` key couldn't be upgraded
|
||||
///
|
||||
/// This **will not** be written. It is up to the user to upgrade and store the key as [`Id3v2Frame::Valid`](Self::Valid).
|
||||
///
|
||||
/// The entire frame is stored as [`ItemValue::Binary`](crate::ItemValue::Binary).
|
||||
Outdated(String),
|
||||
Outdated(Cow<'a, str>),
|
||||
}
|
||||
|
||||
impl FrameID {
|
||||
impl<'a> FrameID<'a> {
|
||||
/// Attempts to create a `FrameID` from an ID string
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * `id` contains invalid characters (must be 'A'..='Z' and '0'..='9')
|
||||
/// * `id` is an invalid length (must be 3 or 4)
|
||||
pub fn new(id: &str) -> Result<Self> {
|
||||
Self::verify_id(id)?;
|
||||
pub fn new(id: Cow<'a, str>) -> Result<Self> {
|
||||
Self::verify_id(&id)?;
|
||||
|
||||
match id.len() {
|
||||
3 => Ok(FrameID::Outdated(id.to_string())),
|
||||
4 => Ok(FrameID::Valid(id.to_string())),
|
||||
3 => Ok(FrameID::Outdated(id)),
|
||||
4 => Ok(FrameID::Valid(id)),
|
||||
_ => Err(ID3v2Error::new(ID3v2ErrorKind::BadFrameID).into()),
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +37,11 @@ impl FrameID {
|
|||
/// Extracts the string from the ID
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
FrameID::Valid(v) | FrameID::Outdated(v) => v.as_str(),
|
||||
FrameID::Valid(v) | FrameID::Outdated(v) => &v,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn verify_id(id_str: &str) -> Result<()> {
|
||||
pub(super) fn verify_id(id_str: &str) -> Result<()> {
|
||||
for c in id_str.chars() {
|
||||
if !c.is_ascii_uppercase() && !c.is_ascii_digit() {
|
||||
return Err(ID3v2Error::new(ID3v2ErrorKind::BadFrameID).into());
|
||||
|
@ -48,12 +50,19 @@ impl FrameID {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn into_owned(self) -> FrameID<'static> {
|
||||
match self {
|
||||
Self::Valid(inner) => FrameID::Valid(Cow::Owned(inner.into_owned())),
|
||||
Self::Outdated(inner) => FrameID::Outdated(Cow::Owned(inner.into_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&ItemKey> for FrameID {
|
||||
impl<'a> TryFrom<&'a ItemKey> for FrameID<'a> {
|
||||
type Error = LoftyError;
|
||||
|
||||
fn try_from(value: &ItemKey) -> std::prelude::rust_2015::Result<Self, Self::Error> {
|
||||
fn try_from(value: &'a ItemKey) -> std::prelude::rust_2015::Result<Self, Self::Error> {
|
||||
match value {
|
||||
ItemKey::Unknown(unknown)
|
||||
if unknown.len() == 4
|
||||
|
@ -61,11 +70,11 @@ impl TryFrom<&ItemKey> for FrameID {
|
|||
.chars()
|
||||
.all(|c| c.is_ascii_uppercase() || c.is_ascii_digit()) =>
|
||||
{
|
||||
Ok(Self::Valid(unknown.clone()))
|
||||
Ok(Self::Valid(Cow::Borrowed(unknown)))
|
||||
},
|
||||
k => k.map_key(TagType::ID3v2, false).map_or(
|
||||
Err(ID3v2Error::new(ID3v2ErrorKind::BadFrameID).into()),
|
||||
|id| Ok(Self::Valid(id.to_string())),
|
||||
|id| Ok(Self::Valid(Cow::Borrowed(id))),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,13 +37,13 @@ use std::hash::{Hash, Hasher};
|
|||
/// `ID3v2.3`, unlike `ID3v2.2`, stores frame IDs in 4 characters like `ID3v2.4`. There are some IDs that need upgrading (See [`upgrade_v3`]),
|
||||
/// but anything that fails to be upgraded **will not** be stored as [`FrameID::Outdated`], as it is likely not an issue to write.
|
||||
#[derive(Clone, Debug, Eq)]
|
||||
pub struct Frame {
|
||||
pub(super) id: FrameID,
|
||||
pub struct Frame<'a> {
|
||||
pub(super) id: FrameID<'a>,
|
||||
pub(super) value: FrameValue,
|
||||
pub(super) flags: FrameFlags,
|
||||
}
|
||||
|
||||
impl PartialEq for Frame {
|
||||
impl<'a> PartialEq for Frame<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match self.value {
|
||||
FrameValue::Text { .. } => self.id == other.id,
|
||||
|
@ -52,7 +52,7 @@ impl PartialEq for Frame {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hash for Frame {
|
||||
impl<'a> Hash for Frame<'a> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
match self.value {
|
||||
FrameValue::Text { .. } => self.id.hash(state),
|
||||
|
@ -64,7 +64,7 @@ impl Hash for Frame {
|
|||
}
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
impl<'a> Frame<'a> {
|
||||
/// Create a new frame
|
||||
///
|
||||
/// NOTE: This will accept both `ID3v2.2` and `ID3v2.3/4` frame IDs
|
||||
|
@ -73,16 +73,16 @@ impl Frame {
|
|||
///
|
||||
/// * `id` is less than 3 or greater than 4 bytes
|
||||
/// * `id` contains non-ascii characters
|
||||
pub fn new(id: &str, value: FrameValue, flags: FrameFlags) -> Result<Self> {
|
||||
pub fn new(id: Cow<'a, str>, value: FrameValue, flags: FrameFlags) -> Result<Self> {
|
||||
let id_updated = match id.len() {
|
||||
// An ID with a length of 4 could be either V3 or V4.
|
||||
4 => match upgrade_v3(id) {
|
||||
4 => match upgrade_v3(&id) {
|
||||
None => id,
|
||||
Some(upgraded) => upgraded,
|
||||
Some(upgraded) => Cow::Borrowed(upgraded),
|
||||
},
|
||||
3 => match upgrade_v2(id) {
|
||||
3 => match upgrade_v2(&id) {
|
||||
None => id,
|
||||
Some(upgraded) => upgraded,
|
||||
Some(upgraded) => Cow::Borrowed(upgraded),
|
||||
},
|
||||
_ => return Err(ID3v2Error::new(ID3v2ErrorKind::BadFrameID).into()),
|
||||
};
|
||||
|
@ -113,9 +113,9 @@ impl Frame {
|
|||
}
|
||||
|
||||
// Used internally, has no correctness checks
|
||||
pub(crate) fn text(id: &str, content: String) -> Self {
|
||||
pub(crate) fn text(id: Cow<'a, str>, content: String) -> Self {
|
||||
Self {
|
||||
id: FrameID::Valid(String::from(id)),
|
||||
id: FrameID::Valid(id),
|
||||
value: FrameValue::Text {
|
||||
encoding: TextEncoding::UTF8,
|
||||
value: content,
|
||||
|
@ -256,11 +256,11 @@ pub struct FrameFlags {
|
|||
pub data_length_indicator: Option<u32>,
|
||||
}
|
||||
|
||||
impl From<TagItem> for Option<Frame> {
|
||||
impl From<TagItem> for Option<Frame<'static>> {
|
||||
fn from(input: TagItem) -> Self {
|
||||
let frame_id;
|
||||
let value;
|
||||
match input.key().try_into() {
|
||||
match input.key().try_into().map(FrameID::into_owned) {
|
||||
Ok(id) => {
|
||||
// We make the VERY bold assumption the language is English
|
||||
value = match (&id, input.item_value) {
|
||||
|
@ -307,7 +307,7 @@ impl From<TagItem> for Option<Frame> {
|
|||
Err(_) => match input.item_key.map_key(TagType::ID3v2, true) {
|
||||
Some(desc) => match input.item_value {
|
||||
ItemValue::Text(text) => {
|
||||
frame_id = FrameID::Valid(String::from("TXXX"));
|
||||
frame_id = FrameID::Valid(Cow::Borrowed("TXXX"));
|
||||
value = FrameValue::UserText(EncodedTextFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: String::from(desc),
|
||||
|
@ -315,7 +315,7 @@ impl From<TagItem> for Option<Frame> {
|
|||
})
|
||||
},
|
||||
ItemValue::Locator(locator) => {
|
||||
frame_id = FrameID::Valid(String::from("WXXX"));
|
||||
frame_id = FrameID::Valid(Cow::Borrowed("WXXX"));
|
||||
value = FrameValue::UserURL(EncodedTextFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: String::from(desc),
|
||||
|
@ -342,7 +342,7 @@ pub(crate) struct FrameRef<'a> {
|
|||
pub flags: FrameFlags,
|
||||
}
|
||||
|
||||
impl<'a> Frame {
|
||||
impl<'a> Frame<'a> {
|
||||
pub(crate) fn as_opt_ref(&'a self) -> Option<FrameRef<'a>> {
|
||||
if let FrameID::Valid(id) = &self.id {
|
||||
Some(FrameRef {
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::io::Read;
|
|||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
impl Frame {
|
||||
impl<'a> Frame<'a> {
|
||||
pub(crate) fn read<R>(reader: &mut R, version: ID3v2Version) -> Result<(Option<Self>, bool)>
|
||||
where
|
||||
R: Read,
|
||||
|
|
|
@ -35,7 +35,7 @@ macro_rules! impl_accessor {
|
|||
}
|
||||
|
||||
self.insert(Frame {
|
||||
id: FrameID::Valid(String::from($id)),
|
||||
id: FrameID::Valid(String::from($id).into()),
|
||||
value: FrameValue::Text {
|
||||
encoding: TextEncoding::UTF8,
|
||||
value,
|
||||
|
@ -89,12 +89,12 @@ macro_rules! impl_accessor {
|
|||
pub struct ID3v2Tag {
|
||||
flags: ID3v2TagFlags,
|
||||
pub(super) original_version: ID3v2Version,
|
||||
pub(crate) frames: Vec<Frame>,
|
||||
pub(crate) frames: Vec<Frame<'static>>,
|
||||
}
|
||||
|
||||
impl IntoIterator for ID3v2Tag {
|
||||
type Item = Frame;
|
||||
type IntoIter = std::vec::IntoIter<Frame>;
|
||||
type Item = Frame<'static>;
|
||||
type IntoIter = std::vec::IntoIter<Frame<'static>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.frames.into_iter()
|
||||
|
@ -133,7 +133,7 @@ impl ID3v2Tag {
|
|||
|
||||
impl ID3v2Tag {
|
||||
/// Returns an iterator over the tag's frames
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Frame> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Frame<'static>> {
|
||||
self.frames.iter()
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ impl ID3v2Tag {
|
|||
/// Gets a [`Frame`] from an id
|
||||
///
|
||||
/// NOTE: This is *not* case-sensitive
|
||||
pub fn get(&self, id: &str) -> Option<&Frame> {
|
||||
pub fn get(&self, id: &str) -> Option<&Frame<'static>> {
|
||||
self.frames
|
||||
.iter()
|
||||
.find(|f| f.id_str().eq_ignore_ascii_case(id))
|
||||
|
@ -175,7 +175,7 @@ impl ID3v2Tag {
|
|||
/// Inserts a [`Frame`]
|
||||
///
|
||||
/// This will replace any frame of the same id (**or description!** See [`EncodedTextFrame`])
|
||||
pub fn insert(&mut self, frame: Frame) -> Option<Frame> {
|
||||
pub fn insert(&mut self, frame: Frame<'static>) -> Option<Frame<'static>> {
|
||||
let replaced = self
|
||||
.frames
|
||||
.iter()
|
||||
|
@ -195,7 +195,7 @@ impl ID3v2Tag {
|
|||
///
|
||||
/// According to spec, there can only be one picture of type [`PictureType::Icon`] and [`PictureType::OtherIcon`].
|
||||
/// When attempting to insert these types, if another is found it will be removed and returned.
|
||||
pub fn insert_picture(&mut self, picture: Picture) -> Option<Frame> {
|
||||
pub fn insert_picture(&mut self, picture: Picture) -> Option<Frame<'static>> {
|
||||
let ret = if picture.pic_type == PictureType::Icon
|
||||
|| picture.pic_type == PictureType::OtherIcon
|
||||
{
|
||||
|
@ -225,7 +225,7 @@ impl ID3v2Tag {
|
|||
};
|
||||
|
||||
let picture_frame = Frame {
|
||||
id: FrameID::Valid(String::from("APIC")),
|
||||
id: FrameID::Valid(Cow::Borrowed("APIC")),
|
||||
value: FrameValue::Picture {
|
||||
encoding: TextEncoding::UTF8,
|
||||
picture,
|
||||
|
@ -305,7 +305,7 @@ impl Accessor for ID3v2Tag {
|
|||
}
|
||||
|
||||
fn set_track(&mut self, value: u32) {
|
||||
self.insert(Frame::text("TRCK", value.to_string()));
|
||||
self.insert(Frame::text(Cow::Borrowed("TRCK"), value.to_string()));
|
||||
}
|
||||
|
||||
fn remove_track(&mut self) {
|
||||
|
@ -319,7 +319,10 @@ impl Accessor for ID3v2Tag {
|
|||
fn set_track_total(&mut self, value: u32) {
|
||||
let current_track = self.split_num_pair("TRCK").0.unwrap_or(1);
|
||||
|
||||
self.insert(Frame::text("TRCK", format!("{current_track}/{value}")));
|
||||
self.insert(Frame::text(
|
||||
Cow::Borrowed("TRCK"),
|
||||
format!("{current_track}/{value}"),
|
||||
));
|
||||
}
|
||||
|
||||
fn remove_track_total(&mut self) {
|
||||
|
@ -327,7 +330,7 @@ impl Accessor for ID3v2Tag {
|
|||
self.remove("TRCK");
|
||||
|
||||
if let Some(track) = existing_track_number {
|
||||
self.insert(Frame::text("TRCK", track.to_string()));
|
||||
self.insert(Frame::text(Cow::Borrowed("TRCK"), track.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,7 +339,7 @@ impl Accessor for ID3v2Tag {
|
|||
}
|
||||
|
||||
fn set_disk(&mut self, value: u32) {
|
||||
self.insert(Frame::text("TPOS", value.to_string()));
|
||||
self.insert(Frame::text(Cow::Borrowed("TPOS"), value.to_string()));
|
||||
}
|
||||
|
||||
fn remove_disk(&mut self) {
|
||||
|
@ -350,7 +353,10 @@ impl Accessor for ID3v2Tag {
|
|||
fn set_disk_total(&mut self, value: u32) {
|
||||
let current_disk = self.split_num_pair("TPOS").0.unwrap_or(1);
|
||||
|
||||
self.insert(Frame::text("TPOS", format!("{current_disk}/{value}")));
|
||||
self.insert(Frame::text(
|
||||
Cow::Borrowed("TPOS"),
|
||||
format!("{current_disk}/{value}"),
|
||||
));
|
||||
}
|
||||
|
||||
fn remove_disk_total(&mut self) {
|
||||
|
@ -358,7 +364,7 @@ impl Accessor for ID3v2Tag {
|
|||
self.remove("TPOS");
|
||||
|
||||
if let Some(track) = existing_track_number {
|
||||
self.insert(Frame::text("TPOS", track.to_string()));
|
||||
self.insert(Frame::text(Cow::Borrowed("TPOS"), track.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,7 +386,7 @@ impl Accessor for ID3v2Tag {
|
|||
}
|
||||
|
||||
fn set_year(&mut self, value: u32) {
|
||||
self.insert(Frame::text("TDRC", value.to_string()));
|
||||
self.insert(Frame::text(Cow::Borrowed("TDRC"), value.to_string()));
|
||||
}
|
||||
|
||||
fn remove_year(&mut self) {
|
||||
|
@ -413,7 +419,7 @@ impl Accessor for ID3v2Tag {
|
|||
|
||||
if !value.is_empty() {
|
||||
self.insert(Frame {
|
||||
id: FrameID::Valid(String::from("COMM")),
|
||||
id: FrameID::Valid(Cow::Borrowed("COMM")),
|
||||
value: FrameValue::Comment(LanguageFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
language: *b"eng",
|
||||
|
@ -432,7 +438,7 @@ impl Accessor for ID3v2Tag {
|
|||
|
||||
impl TagExt for ID3v2Tag {
|
||||
type Err = LoftyError;
|
||||
type RefKey<'a> = &'a FrameID;
|
||||
type RefKey<'a> = &'a FrameID<'a>;
|
||||
|
||||
fn contains<'a>(&'a self, key: Self::RefKey<'a>) -> bool {
|
||||
self.frames.iter().any(|frame| &frame.id == key)
|
||||
|
@ -633,7 +639,7 @@ impl From<Tag> for ID3v2Tag {
|
|||
id3v2_tag.set_artist(artists);
|
||||
|
||||
for item in input.items {
|
||||
let frame: Frame = match item.into() {
|
||||
let frame: Frame<'_> = match item.into() {
|
||||
Some(frame) => frame,
|
||||
None => continue,
|
||||
};
|
||||
|
@ -643,7 +649,7 @@ impl From<Tag> for ID3v2Tag {
|
|||
|
||||
for picture in input.pictures {
|
||||
id3v2_tag.frames.push(Frame {
|
||||
id: FrameID::Valid(String::from("APIC")),
|
||||
id: FrameID::Valid(Cow::Borrowed("APIC")),
|
||||
value: FrameValue::Picture {
|
||||
encoding: TextEncoding::UTF8,
|
||||
picture,
|
||||
|
@ -705,6 +711,8 @@ impl<'a, I: Iterator<Item = FrameRef<'a>> + Clone + 'a> Id3v2TagRef<'a, I> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::id3::v2::items::popularimeter::Popularimeter;
|
||||
use crate::id3::v2::{
|
||||
read_id3v2_header, EncodedTextFrame, Frame, FrameFlags, FrameID, FrameValue, ID3v2Tag,
|
||||
|
@ -734,7 +742,7 @@ mod tests {
|
|||
|
||||
expected_tag.insert(
|
||||
Frame::new(
|
||||
"TPE1",
|
||||
Cow::Borrowed("TPE1"),
|
||||
FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("Bar artist"),
|
||||
|
@ -746,7 +754,7 @@ mod tests {
|
|||
|
||||
expected_tag.insert(
|
||||
Frame::new(
|
||||
"TIT2",
|
||||
Cow::Borrowed("TIT2"),
|
||||
FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("Foo title"),
|
||||
|
@ -758,7 +766,7 @@ mod tests {
|
|||
|
||||
expected_tag.insert(
|
||||
Frame::new(
|
||||
"TALB",
|
||||
Cow::Borrowed("TALB"),
|
||||
FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("Baz album"),
|
||||
|
@ -770,7 +778,7 @@ mod tests {
|
|||
|
||||
expected_tag.insert(
|
||||
Frame::new(
|
||||
"COMM",
|
||||
Cow::Borrowed("COMM"),
|
||||
FrameValue::Comment(LanguageFrame {
|
||||
encoding,
|
||||
language: *b"eng",
|
||||
|
@ -784,7 +792,7 @@ mod tests {
|
|||
|
||||
expected_tag.insert(
|
||||
Frame::new(
|
||||
"TDRC",
|
||||
Cow::Borrowed("TDRC"),
|
||||
FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("1984"),
|
||||
|
@ -796,7 +804,7 @@ mod tests {
|
|||
|
||||
expected_tag.insert(
|
||||
Frame::new(
|
||||
"TRCK",
|
||||
Cow::Borrowed("TRCK"),
|
||||
FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("1"),
|
||||
|
@ -808,7 +816,7 @@ mod tests {
|
|||
|
||||
expected_tag.insert(
|
||||
Frame::new(
|
||||
"TCON",
|
||||
Cow::Borrowed("TCON"),
|
||||
FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("Classical"),
|
||||
|
@ -886,7 +894,7 @@ mod tests {
|
|||
assert_eq!(converted_tag.frames.len(), 1);
|
||||
let actual_frame = converted_tag.frames.first().unwrap();
|
||||
|
||||
assert_eq!(actual_frame.id, FrameID::Valid("POPM".to_string()));
|
||||
assert_eq!(actual_frame.id, FrameID::Valid(Cow::Borrowed("POPM")));
|
||||
// Note: as POPM frames are considered equal by email alone, each field must
|
||||
// be separately validated
|
||||
match actual_frame.content() {
|
||||
|
@ -903,7 +911,7 @@ mod tests {
|
|||
fn fail_write_bad_frame() {
|
||||
let mut tag = ID3v2Tag::default();
|
||||
tag.insert(Frame {
|
||||
id: FrameID::Valid(String::from("ABCD")),
|
||||
id: FrameID::Valid(Cow::Borrowed("ABCD")),
|
||||
value: FrameValue::URL(String::from("FOO URL")),
|
||||
flags: FrameFlags::default(),
|
||||
});
|
||||
|
@ -969,7 +977,7 @@ mod tests {
|
|||
let flags = FrameFlags::default();
|
||||
|
||||
tag.insert(Frame {
|
||||
id: FrameID::Valid(String::from("TIT2")),
|
||||
id: FrameID::Valid(Cow::Borrowed("TIT2")),
|
||||
value: FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("TempleOS Hymn Risen (Remix)"),
|
||||
|
@ -978,7 +986,7 @@ mod tests {
|
|||
});
|
||||
|
||||
tag.insert(Frame {
|
||||
id: FrameID::Valid(String::from("TPE1")),
|
||||
id: FrameID::Valid(Cow::Borrowed("TPE1")),
|
||||
value: FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("Dave Eddy"),
|
||||
|
@ -987,7 +995,7 @@ mod tests {
|
|||
});
|
||||
|
||||
tag.insert(Frame {
|
||||
id: FrameID::Valid(String::from("TRCK")),
|
||||
id: FrameID::Valid(Cow::Borrowed("TRCK")),
|
||||
value: FrameValue::Text {
|
||||
encoding: TextEncoding::Latin1,
|
||||
value: String::from("1"),
|
||||
|
@ -996,7 +1004,7 @@ mod tests {
|
|||
});
|
||||
|
||||
tag.insert(Frame {
|
||||
id: FrameID::Valid(String::from("TALB")),
|
||||
id: FrameID::Valid(Cow::Borrowed("TALB")),
|
||||
value: FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("Summer"),
|
||||
|
@ -1005,7 +1013,7 @@ mod tests {
|
|||
});
|
||||
|
||||
tag.insert(Frame {
|
||||
id: FrameID::Valid(String::from("TDRC")),
|
||||
id: FrameID::Valid(Cow::Borrowed("TDRC")),
|
||||
value: FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("2017"),
|
||||
|
@ -1014,7 +1022,7 @@ mod tests {
|
|||
});
|
||||
|
||||
tag.insert(Frame {
|
||||
id: FrameID::Valid(String::from("TCON")),
|
||||
id: FrameID::Valid(Cow::Borrowed("TCON")),
|
||||
value: FrameValue::Text {
|
||||
encoding,
|
||||
value: String::from("Electronic"),
|
||||
|
@ -1023,7 +1031,7 @@ mod tests {
|
|||
});
|
||||
|
||||
tag.insert(Frame {
|
||||
id: FrameID::Valid(String::from("TLEN")),
|
||||
id: FrameID::Valid(Cow::Borrowed("TLEN")),
|
||||
value: FrameValue::Text {
|
||||
encoding: TextEncoding::UTF16,
|
||||
value: String::from("213017"),
|
||||
|
@ -1032,7 +1040,7 @@ mod tests {
|
|||
});
|
||||
|
||||
tag.insert(Frame {
|
||||
id: FrameID::Valid(String::from("APIC")),
|
||||
id: FrameID::Valid(Cow::Borrowed("APIC")),
|
||||
value: FrameValue::Picture {
|
||||
encoding: TextEncoding::Latin1,
|
||||
picture: Picture {
|
||||
|
@ -1114,7 +1122,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
tag.frames.first(),
|
||||
Some(&Frame {
|
||||
id: FrameID::Valid(String::from("APIC")),
|
||||
id: FrameID::Valid(Cow::Borrowed("APIC")),
|
||||
value: FrameValue::Picture {
|
||||
encoding: TextEncoding::UTF8,
|
||||
picture
|
||||
|
@ -1131,7 +1139,7 @@ mod tests {
|
|||
assert_eq!(parsed_tag.frames.len(), 1);
|
||||
let popm_frame = parsed_tag.frames.first().unwrap();
|
||||
|
||||
assert_eq!(popm_frame.id, FrameID::Valid(String::from("POPM")));
|
||||
assert_eq!(popm_frame.id, FrameID::Valid(Cow::Borrowed("POPM")));
|
||||
assert_eq!(
|
||||
popm_frame.value,
|
||||
FrameValue::Popularimeter(Popularimeter {
|
||||
|
@ -1186,7 +1194,7 @@ mod tests {
|
|||
let mut tag = ID3v2Tag::default();
|
||||
tag.insert(
|
||||
Frame::new(
|
||||
"TXXX",
|
||||
Cow::Borrowed("TXXX"),
|
||||
FrameValue::UserText(EncodedTextFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: String::from("REPLAYGAIN_ALBUM_GAIN"),
|
||||
|
@ -1212,7 +1220,7 @@ mod tests {
|
|||
#[test]
|
||||
fn txxx_wxxx_tag_conversion() {
|
||||
let txxx_frame = Frame::new(
|
||||
"TXXX",
|
||||
Cow::Borrowed("TXXX"),
|
||||
FrameValue::UserText(EncodedTextFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: String::from("FOO_TEXT_FRAME"),
|
||||
|
@ -1223,7 +1231,7 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let wxxx_frame = Frame::new(
|
||||
"WXXX",
|
||||
Cow::Borrowed("WXXX"),
|
||||
FrameValue::UserURL(EncodedTextFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: String::from("BAR_URL_FRAME"),
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// Tests for special case conversions
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use lofty::id3::v2::{Frame, FrameFlags, FrameValue, ID3v2Tag, LanguageFrame};
|
||||
use lofty::{ItemKey, Tag, TagType, TextEncoding};
|
||||
|
||||
|
@ -14,7 +16,7 @@ fn tag_to_id3v2_lang_frame() {
|
|||
assert_eq!(
|
||||
id3.get("USLT"),
|
||||
Frame::new(
|
||||
"USLT",
|
||||
Cow::Borrowed("USLT"),
|
||||
FrameValue::UnSyncText(LanguageFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
language: *b"eng",
|
||||
|
@ -30,7 +32,7 @@ fn tag_to_id3v2_lang_frame() {
|
|||
assert_eq!(
|
||||
id3.get("COMM"),
|
||||
Frame::new(
|
||||
"COMM",
|
||||
Cow::Borrowed("COMM"),
|
||||
FrameValue::Comment(LanguageFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
language: *b"eng",
|
||||
|
|
Loading…
Add table
Reference in a new issue