ext_php_rs/types/
iterable.rs

1use super::array::Iter as ZendHashTableIter;
2use super::iterator::Iter as ZendIteratorIter;
3use crate::convert::FromZval;
4use crate::flags::DataType;
5use crate::types::{ZendHashTable, ZendIterator, Zval};
6
7/// This type represents a PHP iterable, which can be either an array or an
8/// object implementing the Traversable interface.
9#[derive(Debug)]
10pub enum Iterable<'a> {
11    /// Iterable is an Array
12    Array(&'a ZendHashTable),
13    /// Iterable is a Traversable
14    Traversable(&'a mut ZendIterator),
15}
16
17impl Iterable<'_> {
18    /// Creates a new rust iterator from a PHP iterable.
19    /// May return None if a Traversable cannot be rewound.
20    // TODO: Check iter not returning iterator
21    #[allow(clippy::iter_not_returning_iterator)]
22    pub fn iter(&mut self) -> Option<Iter<'_>> {
23        match self {
24            Iterable::Array(array) => Some(Iter::Array(array.iter())),
25            Iterable::Traversable(traversable) => Some(Iter::Traversable(traversable.iter()?)),
26        }
27    }
28}
29
30// TODO: Implement `iter_mut`
31#[allow(clippy::into_iter_without_iter)]
32impl<'a> IntoIterator for &'a mut Iterable<'a> {
33    type Item = (Zval, &'a Zval);
34    type IntoIter = Iter<'a>;
35
36    fn into_iter(self) -> Self::IntoIter {
37        self.iter().expect("Could not rewind iterator!")
38    }
39}
40
41impl<'a> FromZval<'a> for Iterable<'a> {
42    const TYPE: DataType = DataType::Iterable;
43
44    fn from_zval(zval: &'a Zval) -> Option<Self> {
45        if let Some(array) = zval.array() {
46            return Some(Iterable::Array(array));
47        }
48
49        if let Some(traversable) = zval.traversable() {
50            return Some(Iterable::Traversable(traversable));
51        }
52
53        None
54    }
55}
56
57/// Rust iterator over a PHP iterable.
58pub enum Iter<'a> {
59    Array(ZendHashTableIter<'a>),
60    Traversable(ZendIteratorIter<'a>),
61}
62
63impl<'a> Iterator for Iter<'a> {
64    type Item = (Zval, &'a Zval);
65
66    fn next(&mut self) -> Option<Self::Item> {
67        match self {
68            Iter::Array(array) => array.next_zval(),
69            Iter::Traversable(traversable) => traversable.next(),
70        }
71    }
72}