twitch_types/
collection.rs

1use std::borrow::Cow;
2
3/// Generic collection of an abstracted item.
4///
5/// This is used to abstract over the different types of collections that can be used,
6/// such as `Vec<T>`, `&[T]`, `&[&T]`, `&[&str]`, etc.
7///
8/// In most cases, you can use the [`Collection::from`] method to create a collection.
9///
10/// # Examples
11///
12/// ```rust
13/// use twitch_types::{Collection, UserId, UserIdRef};
14///
15/// // A vector of `UserId`s
16/// let c0: Collection<UserId> = Collection::from(vec![UserId::from("1234"), UserId::from("5678")]);
17/// // A vector of `&str`s
18/// let c1: Collection<UserId> = Collection::from(vec!["1234", "5678"]);
19/// // An array of `&str`s
20/// let c2: Collection<UserId> = Collection::from(&["1234", "5678"]);
21/// // A vector of `UserIdRef`s
22/// let c3: Collection<UserId> = Collection::from(vec![
23///     UserIdRef::from_static("1234"),
24///     UserIdRef::from_static("5678"),
25/// ]);
26///
27/// assert!([c1, c2, c3].iter().all(|c| *c == c0));
28/// ```
29///
30/// ```rust
31/// use twitch_types::{Collection, UserId, UserIdRef};
32/// // It's also possible to create a collection from a single item, passing it by reference.
33/// let c0: Collection<UserId> = Collection::from(&"1234");
34/// let id = UserId::from("1234");
35/// let c1: Collection<UserId> = Collection::from(&id);
36///
37/// assert_eq!(c0, c1);
38/// ```
39///
40/// ```rust
41/// use twitch_types::{Collection, UserId, UserIdRef};
42/// // you can also collect from an iterator
43/// let mut iter = std::iter::from_fn(|| Some(UserId::from("1234"))).take(10);
44/// let c0: Collection<UserId> = iter.collect();
45/// let mut iter = std::iter::from_fn(|| Some(UserIdRef::from_static("1234"))).take(10);
46/// let c1: Collection<UserId> = iter.collect();
47/// let mut iter = std::iter::from_fn(|| Some("1234")).take(10);
48/// let c2: Collection<UserId> = iter.collect();
49/// let mut iter = std::iter::from_fn(|| Some(String::from("1234"))).take(10);
50/// let c3: Collection<UserId> = iter.collect();
51///
52/// assert!([c1, c2, c3].iter().all(|c| *c == c0));
53/// ````
54pub enum Collection<'c, T: std::ops::Deref + 'static>
55where [T]: ToOwned {
56    /// A collection over owned items
57    Owned(Cow<'c, [T]>),
58    /// A collection over borrowed items
59    Borrowed(Cow<'c, [&'c T]>),
60    /// A collection over deref items
61    Ref(Cow<'c, [&'c T::Target]>),
62    /// A collection over owned string items
63    OwnedString(Cow<'c, [String]>),
64    /// A collection over borrowed string items
65    BorrowedString(Cow<'c, [&'c String]>),
66    /// A collection over &str items
67    RefStr(Cow<'c, [&'c str]>),
68}
69
70impl<'c, T: std::ops::Deref> Collection<'c, T>
71where
72    [T]: ToOwned,
73    for<'t> &'t T::Target: From<&'t str>,
74{
75    /// An empty collection.
76    pub const EMPTY: Self = Self::Ref(Cow::Borrowed(&[]));
77
78    /// Returns an iterator over the collection.
79    ///
80    /// # Examples
81    ///
82    /// ```rust
83    /// use twitch_types::{Collection, UserId, UserIdRef};
84    ///
85    /// let collection: Collection<UserId> = Collection::from(&["1234", "5678"]);
86    /// let mut iter = collection.iter();
87    /// assert_eq!(iter.next(), Some(UserIdRef::from_static("1234")));
88    /// assert_eq!(iter.next(), Some(UserIdRef::from_static("5678")));
89    /// assert_eq!(iter.next(), None);
90    /// ```
91    pub fn iter(&self) -> CollectionIter<'_, T> {
92        match self {
93            Collection::Owned(v) => CollectionIter {
94                inner: CollectionIterInner::Owned(v.as_ref()),
95            },
96            Collection::Borrowed(v) => CollectionIter {
97                inner: CollectionIterInner::Borrowed(v.as_ref()),
98            },
99            Collection::Ref(v) => CollectionIter {
100                inner: CollectionIterInner::Ref(v.as_ref()),
101            },
102            Collection::OwnedString(v) => {
103                let iter = Box::new(v.iter().map(|v| <_>::from(v.as_str())));
104                CollectionIter {
105                    inner: CollectionIterInner::String(iter),
106                }
107            }
108            Collection::BorrowedString(v) => {
109                let iter = Box::new(v.iter().map(|&v| <_>::from(v.as_str())));
110                CollectionIter {
111                    inner: CollectionIterInner::String(iter),
112                }
113            }
114            Collection::RefStr(v) => {
115                let iter = Box::new(v.iter().map(move |&v| <_>::from(v)));
116                CollectionIter {
117                    inner: CollectionIterInner::String(iter),
118                }
119            }
120        }
121    }
122
123    /// Converts the collection into a vector.
124    ///
125    /// # Examples
126    ///
127    /// ```
128    /// use twitch_types::{Collection, UserId};
129    ///
130    /// let collection = Collection::from(vec!["1", "2", "3"]);
131    /// let vector: Vec<UserId> = collection.into_vec();
132    /// ```
133    pub fn into_vec(self) -> Vec<T>
134    where
135        [T]: ToOwned<Owned = Vec<T>>,
136        T: 'static + Clone,
137        for<'d> T: From<&'d <T as std::ops::Deref>::Target>,
138        for<'d> T: From<&'d str>, {
139        match self {
140            Collection::Owned(v) => v.into_owned(),
141            Collection::Borrowed(v) => v.iter().map(|v| (*v).clone()).collect(),
142            Collection::Ref(v) => v.iter().map(|&v| <T>::from(v)).collect(),
143            Collection::OwnedString(v) => v.iter().map(|v| <T>::from(v.as_str())).collect(),
144            Collection::BorrowedString(v) => v.iter().map(|v| <T>::from(v.as_str())).collect(),
145            Collection::RefStr(v) => v.iter().map(|&v| <T>::from(v)).collect(),
146        }
147    }
148
149    fn index(&'c self, range: impl std::ops::RangeBounds<usize>) -> Option<Collection<'c, T>> {
150        let range = (range.start_bound().cloned(), range.end_bound().cloned());
151        let new: Collection<'_, T> = match self {
152            Collection::Owned(v) => Collection::Owned(Cow::Borrowed(v.get(range)?)),
153            Collection::Borrowed(v) => Collection::Borrowed(Cow::Borrowed(v.get(range)?)),
154            Collection::Ref(v) => Collection::Ref(Cow::Borrowed(v.get(range)?)),
155            Collection::OwnedString(v) => Collection::OwnedString(Cow::Borrowed(v.get(range)?)),
156            Collection::BorrowedString(v) => {
157                Collection::BorrowedString(Cow::Borrowed(v.get(range)?))
158            }
159            Collection::RefStr(v) => Collection::RefStr(Cow::Borrowed(v.get(range)?)),
160        };
161        Some(new)
162    }
163
164    /// Returns chunks of items, similar to [`slice::chunks`]
165    pub fn chunks(&'c self, chunk_size: usize) -> impl Iterator<Item = Collection<'c, T>> + 'c {
166        let len = self.iter().len();
167        let mut start = 0;
168        std::iter::from_fn(move || {
169            if start >= len {
170                return None;
171            }
172            let end = start + chunk_size;
173            let end = if end > len { len } else { end };
174            let range = start..end;
175            start = end;
176            self.index(range)
177        })
178    }
179
180    /// Returns the length of the collection.
181    pub fn len(&self) -> usize {
182        match self {
183            Collection::Owned(v) => v.len(),
184            Collection::Borrowed(v) => v.len(),
185            Collection::Ref(v) => v.len(),
186            Collection::OwnedString(v) => v.len(),
187            Collection::BorrowedString(v) => v.len(),
188            Collection::RefStr(v) => v.len(),
189        }
190    }
191
192    /// Returns `true` if the collection is empty.
193    pub fn is_empty(&self) -> bool { self.len() == 0 }
194}
195
196impl<T: std::ops::Deref + std::fmt::Debug> std::fmt::Debug for Collection<'_, T>
197where
198    [T]: ToOwned,
199    <[T] as ToOwned>::Owned: std::fmt::Debug,
200    T: std::fmt::Debug,
201    T::Target: std::fmt::Debug,
202{
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        match self {
205            Self::Owned(owned) => f.debug_tuple("Owned").field(owned).finish(),
206            Self::Borrowed(borrowed) => f.debug_tuple("Borrowed").field(borrowed).finish(),
207            Self::Ref(ref_) => f.debug_tuple("Ref").field(ref_).finish(),
208            Self::OwnedString(owned) => f.debug_tuple("OwnedString").field(owned).finish(),
209            Self::BorrowedString(borrowed) => {
210                f.debug_tuple("BorrowedString").field(borrowed).finish()
211            }
212            Self::RefStr(ref_) => f.debug_tuple("RefStr").field(ref_).finish(),
213        }
214    }
215}
216
217impl<'a, T: std::ops::Deref, A: std::ops::Deref> FromIterator<A> for Collection<'a, T>
218where
219    [T]: ToOwned,
220    [A]: ToOwned,
221    Collection<'a, T>: From<Vec<A>>,
222    T: 'static,
223{
224    fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self {
225        let v: Vec<_> = iter.into_iter().collect();
226        Collection::from(v)
227    }
228}
229
230impl<T: std::ops::Deref + Clone> From<Vec<T>> for Collection<'_, T>
231where
232    [T]: ToOwned,
233    T: 'static,
234{
235    fn from(v: Vec<T>) -> Self { Self::Owned(Cow::from(v)) }
236}
237
238impl<'c, T: std::ops::Deref + Clone> From<Vec<&'c T>> for Collection<'c, T>
239where
240    [T]: ToOwned,
241    T: 'static,
242{
243    fn from(v: Vec<&'c T>) -> Self { Self::Borrowed(Cow::from(v)) }
244}
245
246//
247impl<'c, T: std::ops::Deref + Clone> From<&'c [T]> for Collection<'c, T>
248where
249    [T]: ToOwned,
250    T: 'static,
251{
252    fn from(v: &'c [T]) -> Self { Self::Owned(Cow::Borrowed(v)) }
253}
254
255impl<'c, T: std::ops::Deref + Clone> From<&'c [&'c T]> for Collection<'c, T>
256where
257    [T]: ToOwned,
258    T: 'static,
259{
260    fn from(v: &'c [&'c T]) -> Self { Self::Borrowed(Cow::from(v)) }
261}
262
263impl<'s, 'c, T: std::ops::Deref> IntoIterator for &'s Collection<'c, T>
264where
265    [T]: ToOwned,
266    for<'t> &'t T::Target: From<&'t str>,
267    's: 'c,
268{
269    type IntoIter = CollectionIter<'c, T>;
270    type Item = &'c T::Target;
271
272    fn into_iter(self) -> Self::IntoIter { self.iter() }
273}
274
275/// Iterator over the elements of a [`Collection`].
276pub struct CollectionIter<'c, T: std::ops::Deref>
277where [T]: ToOwned {
278    inner: CollectionIterInner<'c, T>,
279}
280
281enum CollectionIterInner<'c, T: std::ops::Deref>
282where [T]: ToOwned {
283    Ref(&'c [&'c T::Target]),
284    Borrowed(&'c [&'c T]),
285    Owned(&'c [T]),
286    String(Box<dyn std::iter::ExactSizeIterator<Item = &'c T::Target> + 'c>),
287}
288
289impl<'c, T: std::ops::Deref> Iterator for CollectionIter<'c, T>
290where
291    [T]: ToOwned,
292    'c: 'c,
293{
294    type Item = &'c T::Target;
295
296    fn next(&mut self) -> Option<Self::Item> {
297        match &mut self.inner {
298            CollectionIterInner::Ref(ref_) => {
299                let v = ref_.first()?;
300                *ref_ = &ref_[1..];
301                Some(v)
302            }
303            CollectionIterInner::Borrowed(borrowed) => {
304                let v = borrowed.first()?;
305                *borrowed = &borrowed[1..];
306                Some(v)
307            }
308            CollectionIterInner::Owned(owned) => {
309                let v = owned.first()?;
310                *owned = &owned[1..];
311                Some(v)
312            }
313            CollectionIterInner::String(b) => b.next(),
314        }
315    }
316
317    fn size_hint(&self) -> (usize, Option<usize>) { (self.len(), Some(self.len())) }
318
319    fn count(self) -> usize
320    where Self: Sized {
321        self.len()
322    }
323
324    fn last(self) -> Option<Self::Item>
325    where Self: Sized {
326        match self.inner {
327            CollectionIterInner::Ref(v) => v.last().copied(),
328            CollectionIterInner::Borrowed(v) => v.last().map(|v| v.deref()),
329            CollectionIterInner::Owned(v) => v.last().map(|v| v.deref()),
330            CollectionIterInner::String(b) => b.last(),
331        }
332    }
333}
334
335impl<T: std::ops::Deref> CollectionIter<'_, T>
336where [T]: ToOwned
337{
338    fn len(&self) -> usize {
339        match self.inner {
340            CollectionIterInner::Ref(v) => v.len(),
341            CollectionIterInner::Borrowed(v) => v.len(),
342            CollectionIterInner::Owned(v) => v.len(),
343            CollectionIterInner::String(ref b) => b.len(),
344        }
345    }
346}
347
348#[cfg(feature = "serde")]
349impl<'a, 'de: 'a, T: std::ops::Deref + serde::Deserialize<'de> + Clone> serde::Deserialize<'de>
350    for Collection<'a, T>
351where
352    [T]: ToOwned,
353    &'a T::Target: serde::Deserialize<'de>,
354{
355    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
356    where D: serde::Deserializer<'de> {
357        let collection: Vec<T> = serde::Deserialize::deserialize(deserializer)?;
358        Ok(Collection::Owned(Cow::from(collection)))
359    }
360}
361
362#[cfg(feature = "serde")]
363impl<T: std::ops::Deref> serde::Serialize for Collection<'_, T>
364where
365    [T]: ToOwned,
366    for<'s> &'s T::Target: serde::Serialize,
367    for<'s> &'s T::Target: From<&'s str>,
368{
369    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
370    where S: serde::Serializer {
371        serializer.collect_seq(self.iter())
372    }
373}
374
375//#[derive(PartialEq, Deserialize, Serialize, Clone, Debug)]
376impl<T: Clone + std::ops::Deref> Clone for Collection<'_, T>
377where [T]: ToOwned
378{
379    fn clone(&self) -> Self {
380        match self {
381            Collection::Owned(v) => Collection::Owned(v.clone()),
382            Collection::Borrowed(v) => Collection::Borrowed(v.clone()),
383            Collection::Ref(v) => Collection::Ref(v.clone()),
384            Collection::OwnedString(v) => Collection::OwnedString(v.clone()),
385            Collection::BorrowedString(v) => Collection::BorrowedString(v.clone()),
386            Collection::RefStr(v) => Collection::RefStr(v.clone()),
387        }
388    }
389}
390
391impl<T: std::ops::Deref> PartialEq for Collection<'_, T>
392where
393    [T]: ToOwned,
394    T: PartialEq,
395    T::Target: PartialEq,
396    T: std::cmp::PartialEq<T::Target>,
397    for<'s> &'s T::Target: From<&'s str>,
398    T::Target: std::fmt::Debug,
399    T: std::fmt::Debug,
400{
401    #[rustfmt::skip]
402    fn eq(&self, other: &Self) -> bool {
403        use self::Collection::*;
404        match (self, other) {
405            (Owned(v), Owned(v2)) => v == v2,
406            (Borrowed(v), Borrowed(v2)) => v == v2,
407            (Ref(v), Ref(v2)) => v == v2,
408            (OwnedString(v), OwnedString(v2)) => v == v2,
409            (BorrowedString(v), BorrowedString(v2)) => v == v2,
410            (RefStr(v), RefStr(v2)) => v == v2,
411
412            // (Owned(owned), Borrowed(borrowed)) | (Borrowed(borrowed), Owned(owned)) => borrowed == owned,
413            // (Ref(ref_), Owned(owned)) | (Owned(owned), Ref(ref_)) => owned == ref_,
414            (Borrowed(borrowed), Ref(ref_)) | (Ref(ref_), Borrowed(borrowed)) => borrowed == ref_,
415
416            // etc for strings
417            _ => {
418                let _: Vec<_> = dbg!(self.iter().collect());
419                let _: Vec<_> = dbg!(other.iter().collect());
420                dbg!(self.iter().eq(other.iter()))},
421        }
422    }
423}
424
425impl<T: std::ops::Deref> Eq for Collection<'_, T>
426where
427    [T]: ToOwned,
428    T: PartialEq,
429    T::Target: PartialEq,
430    T: std::cmp::PartialEq<T::Target>,
431    for<'s> &'s T::Target: From<&'s str>,
432    T::Target: std::fmt::Debug,
433    T: std::fmt::Debug,
434{
435}
436
437impl<T: std::ops::Deref> Default for Collection<'_, T>
438where
439    [T]: ToOwned,
440    <[T] as ToOwned>::Owned: Default,
441{
442    fn default() -> Self { Self::Ref(Cow::default()) }
443}
444
445#[cfg(test)]
446mod test {
447    use super::*;
448
449    #[test]
450    fn test_collection() {
451        let owned: Vec<_> = vec![crate::UserId::from("1234"), crate::UserId::from("5678")];
452        let borrow = (&crate::UserId::from("1234"), &crate::UserId::from("5678"));
453        let borrowed: Vec<_> = vec![borrow.0, borrow.1];
454        let ref_: Vec<_> = vec![
455            crate::UserIdRef::from_str("1234"),
456            crate::UserIdRef::from_str("5678"),
457        ];
458        let collection_owned = Collection::from(owned.clone());
459        let collection_borrowed = Collection::from(borrowed.clone());
460        let collection_ref = Collection::from(ref_);
461        assert_eq!(collection_owned, collection_borrowed);
462        assert_eq!(collection_owned, collection_ref);
463        assert_eq!(collection_borrowed, collection_ref);
464        assert_eq!(collection_owned.iter().collect::<Vec<_>>(), owned);
465        assert_eq!(collection_borrowed.iter().collect::<Vec<_>>(), owned);
466        assert_eq!(collection_ref.iter().collect::<Vec<_>>(), owned);
467        assert_eq!(collection_ref.iter().last(), Some("5678".into()));
468    }
469
470    #[test]
471    fn test_collection_stringed() {
472        let owned_s: Vec<_> = vec![String::from("1234"), String::from("5678")];
473        let borrow_s = (&String::from("1234"), &String::from("5678"));
474        let borrowed_s: Vec<_> = vec![borrow_s.0, borrow_s.1];
475        let ref_s: Vec<_> = vec!["1234", "5678"];
476        let ref_: Vec<_> = vec![
477            crate::UserIdRef::from_str("1234"),
478            crate::UserIdRef::from_str("5678"),
479        ];
480        let collection_owned_s: Collection<crate::UserId> = Collection::from(owned_s.clone());
481        let collection_borrowed_s: Collection<crate::UserId> = Collection::from(borrowed_s.clone());
482        let collection_ref_s: Collection<crate::UserId> = Collection::from(ref_s);
483        let collection_ref: Collection<crate::UserId> = Collection::from(ref_);
484        assert_eq!(collection_owned_s, collection_borrowed_s);
485        assert_eq!(collection_owned_s, collection_ref_s);
486        assert_eq!(collection_borrowed_s, collection_ref_s);
487        assert_eq!(collection_ref, collection_ref_s);
488        assert_eq!(collection_ref_s.iter().last(), Some("5678".into()));
489    }
490}