use std::{slice::Iter, vec::IntoIter};
use mpd_protocol::response::Frame;
use crate::tag::Tag;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct List<const N: usize> {
primary_tag: Tag,
groupings: [Tag; N],
fields: Vec<(Tag, String)>,
}
impl List<0> {
pub fn values(&self) -> ListValuesIter<'_> {
ListValuesIter(self.fields.iter())
}
}
impl<const N: usize> List<N> {
pub(crate) fn from_frame(primary_tag: Tag, groupings: [Tag; N], frame: Frame) -> List<N> {
let fields = frame
.into_iter()
.map(|(tag, value)| (Tag::try_from(tag.as_ref()).unwrap(), value))
.collect();
List {
primary_tag,
groupings,
fields,
}
}
pub fn grouped_values(&self) -> GroupedListValuesIter<'_, N> {
GroupedListValuesIter {
primary_tag: &self.primary_tag,
grouping_tags: &self.groupings,
grouping_values: [""; N],
fields: self.fields.iter(),
}
}
pub fn grouped_by(&self) -> &[Tag; N] {
&self.groupings
}
pub fn into_raw_values(self) -> Vec<(Tag, String)> {
self.fields
}
}
impl<'a> IntoIterator for &'a List<0> {
type Item = &'a str;
type IntoIter = ListValuesIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.values()
}
}
impl IntoIterator for List<0> {
type Item = String;
type IntoIter = ListValuesIntoIter;
fn into_iter(self) -> Self::IntoIter {
ListValuesIntoIter(self.fields.into_iter())
}
}
#[derive(Clone, Debug)]
pub struct GroupedListValuesIter<'a, const N: usize> {
primary_tag: &'a Tag,
grouping_tags: &'a [Tag; N],
grouping_values: [&'a str; N],
fields: Iter<'a, (Tag, String)>,
}
impl<'a, const N: usize> Iterator for GroupedListValuesIter<'a, N> {
type Item = (&'a str, [&'a str; N]);
fn next(&mut self) -> Option<Self::Item> {
loop {
let (tag, value) = self.fields.next()?;
if tag == self.primary_tag {
break Some((value, self.grouping_values));
}
let idx = self.grouping_tags.iter().position(|t| t == tag).unwrap();
self.grouping_values[idx] = value;
}
}
}
#[derive(Clone, Debug)]
pub struct ListValuesIter<'a>(Iter<'a, (Tag, String)>);
impl<'a> Iterator for ListValuesIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(_, v)| &**v)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn count(self) -> usize {
self.0.count()
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth(n).map(|(_, v)| &**v)
}
fn last(self) -> Option<Self::Item>
where
Self: Sized,
{
self.0.last().map(|(_, v)| &**v)
}
}
impl<'a> DoubleEndedIterator for ListValuesIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|(_, v)| &**v)
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth_back(n).map(|(_, v)| &**v)
}
}
impl<'a> ExactSizeIterator for ListValuesIter<'a> {}
#[derive(Debug)]
pub struct ListValuesIntoIter(IntoIter<(Tag, String)>);
impl Iterator for ListValuesIntoIter {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(_, v)| v)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn count(self) -> usize {
self.0.count()
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth(n).map(|(_, v)| v)
}
fn last(self) -> Option<Self::Item>
where
Self: Sized,
{
self.0.last().map(|(_, v)| v)
}
}
impl DoubleEndedIterator for ListValuesIntoIter {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|(_, v)| v)
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth_back(n).map(|(_, v)| v)
}
}
impl ExactSizeIterator for ListValuesIntoIter {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn grouped_iterator() {
let fields = vec![
(Tag::AlbumArtist, String::from("Foo")),
(Tag::Album, String::from("Bar")),
(Tag::Title, String::from("Title 1")),
(Tag::Title, String::from("Title 2")),
(Tag::Album, String::from("Quz")),
(Tag::Title, String::from("Title 3")),
(Tag::AlbumArtist, String::from("Asdf")),
(Tag::Album, String::from("Qwert")),
(Tag::Title, String::from("Title 4")),
];
let mut iter = GroupedListValuesIter {
primary_tag: &Tag::Title,
grouping_tags: &[Tag::Album, Tag::AlbumArtist],
grouping_values: [""; 2],
fields: fields.iter(),
};
assert_eq!(iter.next(), Some(("Title 1", ["Bar", "Foo"])));
assert_eq!(iter.next(), Some(("Title 2", ["Bar", "Foo"])));
assert_eq!(iter.next(), Some(("Title 3", ["Quz", "Foo"])));
assert_eq!(iter.next(), Some(("Title 4", ["Qwert", "Asdf"])));
assert_eq!(iter.next(), None);
}
}