mpd_client/responses/
list.rs

1use std::{slice::Iter, vec::IntoIter};
2
3use mpd_protocol::response::Frame;
4
5use crate::tag::Tag;
6
7/// Response to the [`list`] command.
8///
9/// [`list`]: crate::commands::definitions::List
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct List<const N: usize> {
12    primary_tag: Tag,
13    groupings: [Tag; N],
14    fields: Vec<(Tag, String)>,
15}
16
17impl List<0> {
18    /// Returns an iterator over all distinct values returned.
19    pub fn values(&self) -> ListValuesIter<'_> {
20        ListValuesIter(self.fields.iter())
21    }
22}
23
24impl<const N: usize> List<N> {
25    pub(crate) fn from_frame(primary_tag: Tag, groupings: [Tag; N], frame: Frame) -> List<N> {
26        let fields = frame
27            .into_iter()
28            // Unwrapping here is fine because the parser already validated the fields
29            .map(|(tag, value)| (Tag::try_from(tag.as_ref()).unwrap(), value))
30            .collect();
31
32        List {
33            primary_tag,
34            groupings,
35            fields,
36        }
37    }
38
39    /// Returns an iterator over the grouped combinations returned by the command.
40    ///
41    /// The grouped values are returned in the same order they were passed to [`group_by`].
42    ///
43    /// [`group_by`]: crate::commands::definitions::List::group_by
44    pub fn grouped_values(&self) -> GroupedListValuesIter<'_, N> {
45        GroupedListValuesIter {
46            primary_tag: &self.primary_tag,
47            grouping_tags: &self.groupings,
48            grouping_values: [""; N],
49            fields: self.fields.iter(),
50        }
51    }
52
53    /// Returns the tags the response was grouped by.
54    pub fn grouped_by(&self) -> &[Tag; N] {
55        &self.groupings
56    }
57
58    /// Get the raw fields as they were returned by the server.
59    pub fn into_raw_values(self) -> Vec<(Tag, String)> {
60        self.fields
61    }
62}
63
64impl<'a> IntoIterator for &'a List<0> {
65    type Item = &'a str;
66
67    type IntoIter = ListValuesIter<'a>;
68
69    fn into_iter(self) -> Self::IntoIter {
70        self.values()
71    }
72}
73
74impl IntoIterator for List<0> {
75    type Item = String;
76
77    type IntoIter = ListValuesIntoIter;
78
79    fn into_iter(self) -> Self::IntoIter {
80        ListValuesIntoIter(self.fields.into_iter())
81    }
82}
83
84/// Iterator over references to grouped values.
85///
86/// Returned by [`List::grouped_values`].
87#[derive(Clone, Debug)]
88pub struct GroupedListValuesIter<'a, const N: usize> {
89    primary_tag: &'a Tag,
90    grouping_tags: &'a [Tag; N],
91    grouping_values: [&'a str; N],
92    fields: Iter<'a, (Tag, String)>,
93}
94
95impl<'a, const N: usize> Iterator for GroupedListValuesIter<'a, N> {
96    type Item = (&'a str, [&'a str; N]);
97
98    fn next(&mut self) -> Option<Self::Item> {
99        loop {
100            let (tag, value) = self.fields.next()?;
101
102            if tag == self.primary_tag {
103                break Some((value, self.grouping_values));
104            }
105
106            let idx = self.grouping_tags.iter().position(|t| t == tag).unwrap();
107            self.grouping_values[idx] = value;
108        }
109    }
110}
111
112/// Iterator over references to ungrouped [`List`] values.
113#[derive(Clone, Debug)]
114pub struct ListValuesIter<'a>(Iter<'a, (Tag, String)>);
115
116impl<'a> Iterator for ListValuesIter<'a> {
117    type Item = &'a str;
118
119    fn next(&mut self) -> Option<Self::Item> {
120        self.0.next().map(|(_, v)| &**v)
121    }
122
123    fn size_hint(&self) -> (usize, Option<usize>) {
124        self.0.size_hint()
125    }
126
127    fn count(self) -> usize {
128        self.0.count()
129    }
130
131    fn nth(&mut self, n: usize) -> Option<Self::Item> {
132        self.0.nth(n).map(|(_, v)| &**v)
133    }
134
135    fn last(self) -> Option<Self::Item>
136    where
137        Self: Sized,
138    {
139        self.0.last().map(|(_, v)| &**v)
140    }
141}
142
143impl<'a> DoubleEndedIterator for ListValuesIter<'a> {
144    fn next_back(&mut self) -> Option<Self::Item> {
145        self.0.next_back().map(|(_, v)| &**v)
146    }
147
148    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
149        self.0.nth_back(n).map(|(_, v)| &**v)
150    }
151}
152
153impl<'a> ExactSizeIterator for ListValuesIter<'a> {}
154
155/// Iterator over ungrouped [`List`] values.
156#[derive(Debug)]
157pub struct ListValuesIntoIter(IntoIter<(Tag, String)>);
158
159impl Iterator for ListValuesIntoIter {
160    type Item = String;
161
162    fn next(&mut self) -> Option<Self::Item> {
163        self.0.next().map(|(_, v)| v)
164    }
165
166    fn size_hint(&self) -> (usize, Option<usize>) {
167        self.0.size_hint()
168    }
169
170    fn count(self) -> usize {
171        self.0.count()
172    }
173
174    fn nth(&mut self, n: usize) -> Option<Self::Item> {
175        self.0.nth(n).map(|(_, v)| v)
176    }
177
178    fn last(self) -> Option<Self::Item>
179    where
180        Self: Sized,
181    {
182        self.0.last().map(|(_, v)| v)
183    }
184}
185
186impl DoubleEndedIterator for ListValuesIntoIter {
187    fn next_back(&mut self) -> Option<Self::Item> {
188        self.0.next_back().map(|(_, v)| v)
189    }
190
191    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
192        self.0.nth_back(n).map(|(_, v)| v)
193    }
194}
195
196impl ExactSizeIterator for ListValuesIntoIter {}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn grouped_iterator() {
204        let fields = vec![
205            (Tag::AlbumArtist, String::from("Foo")),
206            (Tag::Album, String::from("Bar")),
207            (Tag::Title, String::from("Title 1")),
208            (Tag::Title, String::from("Title 2")),
209            (Tag::Album, String::from("Quz")),
210            (Tag::Title, String::from("Title 3")),
211            (Tag::AlbumArtist, String::from("Asdf")),
212            (Tag::Album, String::from("Qwert")),
213            (Tag::Title, String::from("Title 4")),
214        ];
215
216        let mut iter = GroupedListValuesIter {
217            primary_tag: &Tag::Title,
218            grouping_tags: &[Tag::Album, Tag::AlbumArtist],
219            grouping_values: [""; 2],
220            fields: fields.iter(),
221        };
222
223        assert_eq!(iter.next(), Some(("Title 1", ["Bar", "Foo"])));
224        assert_eq!(iter.next(), Some(("Title 2", ["Bar", "Foo"])));
225        assert_eq!(iter.next(), Some(("Title 3", ["Quz", "Foo"])));
226        assert_eq!(iter.next(), Some(("Title 4", ["Qwert", "Asdf"])));
227        assert_eq!(iter.next(), None);
228    }
229}