1use std::ptr::NonNull;
2
3use crate::{Error, Result, Value, ValueType, sys};
4
5impl Value<'_> {
6 #[must_use]
24 pub fn is_list(&self) -> bool {
25 self.value_type() == ValueType::List
26 }
27
28 pub fn list_len(&self) -> Result<usize> {
49 if !self.is_list() {
50 return Err(Error::InvalidType {
51 expected: "list",
52 actual: self.value_type().to_string(),
53 });
54 }
55
56 let len = unsafe {
59 sys::nix_get_list_size(self.state.context.as_ptr(), self.inner.as_ptr())
60 };
61
62 Ok(len as usize)
63 }
64
65 pub fn list_get(&self, idx: usize) -> Result<Value<'_>> {
92 if !self.is_list() {
93 return Err(Error::InvalidType {
94 expected: "list",
95 actual: self.value_type().to_string(),
96 });
97 }
98
99 let len = self.list_len()?;
100 if idx >= len {
101 return Err(Error::IndexOutOfBounds {
102 index: idx,
103 length: len,
104 });
105 }
106
107 let elem_ptr = unsafe {
111 sys::nix_get_list_byidx(
112 self.state.context.as_ptr(),
113 self.inner.as_ptr(),
114 self.state.as_ptr(),
115 idx as std::os::raw::c_uint,
116 )
117 };
118
119 let inner = NonNull::new(elem_ptr).ok_or(Error::NullPointer)?;
120 Ok(Value {
121 inner,
122 state: self.state,
123 })
124 }
125
126 pub fn list_iter(&self) -> Result<ListIterator<'_>> {
132 if !self.is_list() {
133 return Err(Error::InvalidType {
134 expected: "list",
135 actual: self.value_type().to_string(),
136 });
137 }
138
139 let len = self.list_len()?;
140 Ok(ListIterator {
141 value: self,
142 index: 0,
143 length: len,
144 })
145 }
146}
147
148#[derive(Debug)]
153pub struct ListIterator<'a> {
154 value: &'a Value<'a>,
155 index: usize,
156 length: usize,
157}
158
159impl<'a> Iterator for ListIterator<'a> {
160 type Item = Result<Value<'a>>;
161
162 fn next(&mut self) -> Option<Self::Item> {
163 if self.index >= self.length {
164 return None;
165 }
166
167 let idx = self.index;
168 self.index += 1;
169
170 let elem_ptr = unsafe {
174 sys::nix_get_list_byidx(
175 self.value.state.context.as_ptr(),
176 self.value.inner.as_ptr(),
177 self.value.state.as_ptr(),
178 idx as std::os::raw::c_uint,
179 )
180 };
181
182 match NonNull::new(elem_ptr) {
183 Some(inner) => {
184 Some(Ok(Value {
185 inner,
186 state: self.value.state,
187 }))
188 },
189 None => Some(Err(Error::NullPointer)),
190 }
191 }
192
193 fn size_hint(&self) -> (usize, Option<usize>) {
194 let remaining = self.length - self.index;
195 (remaining, Some(remaining))
196 }
197}
198
199impl ExactSizeIterator for ListIterator<'_> {}
200
201#[cfg(test)]
202mod tests {
203 use std::sync::Arc;
204
205 use serial_test::serial;
206
207 use super::*;
208 use crate::{Context, EvalStateBuilder, Store};
209
210 #[test]
211 #[serial]
212 fn test_is_list() {
213 let ctx = Arc::new(Context::new().expect("Failed to create context"));
214 let store =
215 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
216 let state = EvalStateBuilder::new(&store)
217 .expect("Failed to create builder")
218 .build()
219 .expect("Failed to build state");
220
221 let list = state
222 .eval_from_string("[1 2 3]", "<eval>")
223 .expect("Failed to evaluate list");
224 assert!(list.is_list());
225
226 let int = state
227 .eval_from_string("1", "<eval>")
228 .expect("Failed to evaluate int");
229 assert!(!int.is_list());
230 }
231
232 #[test]
233 #[serial]
234 fn test_list_len() {
235 let ctx = Arc::new(Context::new().expect("Failed to create context"));
236 let store =
237 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
238 let state = EvalStateBuilder::new(&store)
239 .expect("Failed to create builder")
240 .build()
241 .expect("Failed to build state");
242
243 let empty = state
244 .eval_from_string("[]", "<eval>")
245 .expect("Failed to evaluate empty list");
246 assert_eq!(empty.list_len().expect("Failed to get list length"), 0);
247
248 let list = state
249 .eval_from_string("[1 2 3]", "<eval>")
250 .expect("Failed to evaluate list");
251 assert_eq!(list.list_len().expect("Failed to get list length"), 3);
252 }
253
254 #[test]
255 #[serial]
256 fn test_list_get() {
257 let ctx = Arc::new(Context::new().expect("Failed to create context"));
258 let store =
259 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
260 let state = EvalStateBuilder::new(&store)
261 .expect("Failed to create builder")
262 .build()
263 .expect("Failed to build state");
264
265 let list = state
266 .eval_from_string("[10 20 30]", "<eval>")
267 .expect("Failed to evaluate list");
268
269 let first = list.list_get(0).expect("Failed to get first element");
270 assert_eq!(first.as_int().expect("Failed to get int"), 10);
271
272 let second = list.list_get(1).expect("Failed to get second element");
273 assert_eq!(second.as_int().expect("Failed to get int"), 20);
274
275 let third = list.list_get(2).expect("Failed to get third element");
276 assert_eq!(third.as_int().expect("Failed to get int"), 30);
277
278 let result = list.list_get(5);
280 assert!(matches!(
281 result,
282 Err(Error::IndexOutOfBounds {
283 index: 5,
284 length: 3,
285 })
286 ));
287 }
288
289 #[test]
290 #[serial]
291 fn test_list_iter() {
292 let ctx = Arc::new(Context::new().expect("Failed to create context"));
293 let store =
294 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
295 let state = EvalStateBuilder::new(&store)
296 .expect("Failed to create builder")
297 .build()
298 .expect("Failed to build state");
299
300 let list = state
301 .eval_from_string("[1 2 3]", "<eval>")
302 .expect("Failed to evaluate list");
303
304 let mut iter = list.list_iter().expect("Failed to create iterator");
305 assert_eq!(iter.len(), 3);
306
307 let first = iter
308 .next()
309 .expect("Failed to get first")
310 .expect("Failed to get first value");
311 assert_eq!(first.as_int().expect("Failed to get int"), 1);
312
313 let second = iter
314 .next()
315 .expect("Failed to get second")
316 .expect("Failed to get second value");
317 assert_eq!(second.as_int().expect("Failed to get int"), 2);
318
319 let third = iter
320 .next()
321 .expect("Failed to get third")
322 .expect("Failed to get third value");
323 assert_eq!(third.as_int().expect("Failed to get int"), 3);
324
325 assert!(iter.next().is_none());
326 }
327}