use super::Frame;
use crate::TextEncoding;
use crate::id3::v2::tag::new_picture_frame;
use crate::id3::v2::{AttachedPictureFrame, ExtendedTextFrame, FrameId, TextInformationFrame};
use crate::picture::{Picture, PictureType};
use std::borrow::Cow;
use std::ops::Deref;
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct FrameList<'a>(Cow<'a, [Frame<'a>]>);
impl<'a> FrameList<'a> {
pub fn new() -> Self {
Self(Cow::Owned(Vec::new()))
}
pub fn get(&self, id: &FrameId<'_>) -> Option<&Frame<'a>> {
self.0.iter().find(|f| f.id() == id)
}
pub fn get_text(&self, id: &FrameId<'_>) -> Option<&str> {
let frame = self.get(id);
if let Some(Frame::Text(TextInformationFrame { value, .. })) = frame {
return Some(value);
}
None
}
pub fn get_texts(&self, id: &FrameId<'_>) -> Option<impl Iterator<Item = &'_ str>> {
if let Some(Frame::Text(TextInformationFrame { value, .. })) = self.get(id) {
return Some(value.split(crate::id3::v2::tag::V4_MULTI_VALUE_SEPARATOR));
}
None
}
pub fn get_user_text(&self, description: &str) -> Option<&str> {
self.0
.iter()
.filter(|frame| frame.id().as_str() == "TXXX")
.find_map(|frame| match frame {
Frame::UserText(ExtendedTextFrame {
description: desc,
content,
..
}) if desc == description => Some(&**content),
_ => None,
})
}
pub fn insert_user_text(&mut self, description: String, content: String) -> Option<Frame<'a>> {
self.insert(Frame::UserText(ExtendedTextFrame::new(
TextEncoding::UTF8,
description,
content,
)))
}
pub fn remove_user_text(&mut self, description: &str) -> Option<Frame<'a>> {
self.0
.iter()
.position(|frame| {
matches!(frame, Frame::UserText(ExtendedTextFrame {
description: desc, ..
}) if desc == description)
})
.map(|pos| self.0.to_mut().remove(pos))
}
pub fn insert(&mut self, frame: Frame<'a>) -> Option<Frame<'a>> {
const ONE_PER_TAG: [&str; 11] = [
"MCDI", "ETCO", "MLLT", "SYTC", "RVRB", "PCNT", "RBUF", "POSS", "OWNE", "SEEK", "ASPI",
];
if ONE_PER_TAG.contains(&frame.id_str()) {
let ret = self.remove(frame.id()).next();
self.push(frame);
return ret;
}
let replaced = self
.0
.iter()
.position(|f| f == &frame)
.map(|pos| self.0.to_mut().remove(pos));
self.push(frame);
replaced
}
pub(crate) fn push(&mut self, frame: Frame<'a>) {
self.0.to_mut().push(frame);
}
pub(crate) fn reserve(&mut self, additional: usize) {
self.0.to_mut().reserve(additional);
}
pub(crate) fn take_first(&mut self, id: &FrameId<'_>) -> Option<Frame<'a>> {
self.0
.iter()
.position(|f| f.id() == id)
.map(|pos| self.0.to_mut().remove(pos))
}
#[cfg(test)]
pub(crate) fn sort_by_key<K, F>(&mut self, f: F)
where
F: FnMut(&Frame<'a>) -> K,
K: Ord,
{
self.0.to_mut().sort_by_key(f);
}
pub fn remove(&mut self, id: &FrameId<'_>) -> impl Iterator<Item = Frame<'a>> {
let mut split_idx = 0_usize;
for read_idx in 0..self.0.len() {
if self.0[read_idx].id() == id {
self.0.to_mut().swap(split_idx, read_idx);
split_idx += 1;
}
}
self.0.to_mut().drain(..split_idx)
}
pub fn remove_picture_type(&mut self, picture_type: PictureType) {
self.0.to_mut().retain(|f| {
!matches!(f, Frame::Picture(AttachedPictureFrame {
picture: Cow::Owned(Picture {
pic_type: p_ty,
..
}), ..
}) if p_ty == &picture_type)
})
}
pub fn clear(&mut self) {
self.0.to_mut().clear();
}
pub fn retain<P>(&mut self, predicate: P)
where
P: FnMut(&Frame<'a>) -> bool,
{
self.0.to_mut().retain(predicate);
}
pub fn retain_mut<P>(&mut self, predicate: P)
where
P: FnMut(&mut Frame<'a>) -> bool,
{
self.0.to_mut().retain_mut(predicate);
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl FrameList<'static> {
pub fn insert_picture(&mut self, picture: Picture) -> Option<Frame<'static>> {
let ret = if picture.pic_type == PictureType::Icon
|| picture.pic_type == PictureType::OtherIcon
{
let mut pos = None;
for (i, frame) in self.0.iter().enumerate() {
match frame {
Frame::Picture(AttachedPictureFrame {
picture: Cow::Owned(Picture { pic_type, .. }),
..
}) if pic_type == &picture.pic_type => {
pos = Some(i);
break;
},
_ => {},
}
}
pos.map(|p| self.0.to_mut().remove(p))
} else {
None
};
self.0.to_mut().push(new_picture_frame(picture));
ret
}
}
impl FrameList<'_> {
pub(crate) fn borrow(&self) -> FrameList<'_> {
FrameList(Cow::Owned(
self.0.iter().map(Frame::borrow).collect::<Vec<_>>(),
))
}
}
impl Default for FrameList<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'a> Deref for FrameList<'a> {
type Target = [Frame<'a>];
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl<'a> IntoIterator for FrameList<'a> {
type Item = Frame<'a>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_owned().into_iter()
}
}
impl<'a> From<FrameList<'a>> for Vec<Frame<'a>> {
fn from(list: FrameList<'a>) -> Self {
list.0.into_owned()
}
}