Skip to main content

zlink_core/idl/
list.rs

1//! List type for holding either borrowed or owned collections.
2
3use alloc::vec::Vec;
4
5/// A list that can be either borrowed or owned.
6///
7/// This type is useful for const contexts where we need borrowed data,
8/// as well as for deserialization where we need owned data.
9#[derive(Debug, Clone, Eq)]
10pub enum List<'a, T> {
11    /// Borrowed slice of references, useful for const contexts.
12    Borrowed(&'a [&'a T]),
13    /// Owned vector, used for deserialization.
14    Owned(Vec<T>),
15}
16
17impl<'a, T> List<'a, T> {
18    /// Returns an iterator over references to the items.
19    pub fn iter(&self) -> impl Iterator<Item = &T> {
20        match self {
21            List::Borrowed(slice) => ListIter::Borrowed(slice.iter()),
22            List::Owned(vec) => ListIter::Owned(vec.iter()),
23        }
24    }
25
26    /// Returns the number of items in the list.
27    pub fn len(&self) -> usize {
28        match self {
29            List::Borrowed(slice) => slice.len(),
30            List::Owned(vec) => vec.len(),
31        }
32    }
33
34    /// Returns true if the list is empty.
35    pub fn is_empty(&self) -> bool {
36        self.len() == 0
37    }
38
39    /// The borrowed slice of references if this list is borrowed.
40    pub const fn as_borrowed(&self) -> Option<&[&T]> {
41        match self {
42            List::Borrowed(slice) => Some(slice),
43            List::Owned(_) => None,
44        }
45    }
46
47    /// The owned vector of values if this list is owned.
48    pub fn as_owned(&self) -> Option<&Vec<T>> {
49        match self {
50            List::Borrowed(_) => None,
51            List::Owned(vec) => Some(vec),
52        }
53    }
54}
55
56/// Iterator over list items.
57enum ListIter<'a, T> {
58    Borrowed(core::slice::Iter<'a, &'a T>),
59    Owned(core::slice::Iter<'a, T>),
60}
61
62impl<'a, T> Iterator for ListIter<'a, T> {
63    type Item = &'a T;
64
65    fn next(&mut self) -> Option<Self::Item> {
66        match self {
67            ListIter::Borrowed(iter) => iter.next().copied(),
68            ListIter::Owned(iter) => iter.next(),
69        }
70    }
71}
72
73impl<'a, T> Default for List<'a, T> {
74    fn default() -> Self {
75        List::Borrowed(&[])
76    }
77}
78
79impl<'a, T> From<Vec<T>> for List<'a, T> {
80    fn from(vec: Vec<T>) -> Self {
81        List::Owned(vec)
82    }
83}
84
85impl<'a, T> From<&'a [&'a T]> for List<'a, T> {
86    fn from(slice: &'a [&'a T]) -> Self {
87        List::Borrowed(slice)
88    }
89}
90
91impl<'a, T> PartialEq for List<'a, T>
92where
93    T: PartialEq,
94{
95    fn eq(&self, other: &Self) -> bool {
96        if self.len() != other.len() {
97            return false;
98        }
99
100        self.iter().zip(other.iter()).all(|(a, b)| a == b)
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use alloc::{
107        string::{String, ToString},
108        vec,
109    };
110
111    use super::*;
112
113    #[test]
114    fn list_borrowed() {
115        static ITEM_ONE: &str = "one";
116        static ITEM_TWO: &str = "two";
117        static ITEM_THREE: &str = "three";
118        static ITEMS: [&'static &str; 3] = [&ITEM_ONE, &ITEM_TWO, &ITEM_THREE];
119        let list: List<'_, &str> = List::Borrowed(&ITEMS);
120
121        assert_eq!(list.len(), 3);
122        assert!(!list.is_empty());
123
124        let expected = ["one", "two", "three"];
125        let mut actual = Vec::<&str>::new();
126        for item in list.iter() {
127            actual.push(*item);
128        }
129        assert_eq!(actual.as_slice(), &expected);
130    }
131
132    #[test]
133    fn list_owned() {
134        let vec = vec!["one".to_string(), "two".to_string(), "three".to_string()];
135        let list: List<'_, String> = List::from(vec);
136
137        assert_eq!(list.len(), 3);
138        assert!(!list.is_empty());
139
140        let collected: Vec<_> = list.iter().map(|s| s.as_str()).collect();
141        assert_eq!(collected, vec!["one", "two", "three"]);
142    }
143
144    #[test]
145    fn list_partial_eq_cross_variant() {
146        // Test that borrowed and owned variants compare equal when they have the same content
147        static ITEM_ONE: &str = "one";
148        static ITEM_TWO: &str = "two";
149        static ITEM_THREE: &str = "three";
150        static REFS: [&'static &str; 3] = [&ITEM_ONE, &ITEM_TWO, &ITEM_THREE];
151        let borrowed_list: List<'_, &str> = List::Borrowed(&REFS);
152
153        let owned_list: List<'_, &str> = List::Owned(vec!["one", "two", "three"]);
154        assert_eq!(borrowed_list, owned_list);
155        assert_eq!(owned_list, borrowed_list);
156
157        // Test that lists with different content are not equal
158        static OTHER_REFS: [&'static &str; 2] = [&ITEM_ONE, &ITEM_TWO];
159        let different_borrowed: List<'_, &str> = List::Borrowed(&OTHER_REFS);
160        assert_ne!(borrowed_list, different_borrowed);
161
162        let different_owned: List<'_, &str> = List::Owned(vec!["one", "two"]);
163        assert_ne!(borrowed_list, different_owned);
164    }
165}