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    Array(&'a ZendHashTable),
12    Traversable(&'a mut ZendIterator),
13}
14
15impl Iterable<'_> {
16    /// Creates a new rust iterator from a PHP iterable.
17    /// May return None if a Traversable cannot be rewound.
18    pub fn iter(&mut self) -> Option<Iter> {
19        match self {
20            Iterable::Array(array) => Some(Iter::Array(array.iter())),
21            Iterable::Traversable(traversable) => Some(Iter::Traversable(traversable.iter()?)),
22        }
23    }
24}
25
26impl<'a> IntoIterator for &'a mut Iterable<'a> {
27    type Item = (Zval, &'a Zval);
28    type IntoIter = Iter<'a>;
29
30    fn into_iter(self) -> Self::IntoIter {
31        self.iter().expect("Could not rewind iterator!")
32    }
33}
34
35impl<'a> FromZval<'a> for Iterable<'a> {
36    const TYPE: DataType = DataType::Iterable;
37
38    fn from_zval(zval: &'a Zval) -> Option<Self> {
39        if let Some(array) = zval.array() {
40            return Some(Iterable::Array(array));
41        }
42
43        if let Some(traversable) = zval.traversable() {
44            return Some(Iterable::Traversable(traversable));
45        }
46
47        None
48    }
49}
50
51/// Rust iterator over a PHP iterable.
52pub enum Iter<'a> {
53    Array(ZendHashTableIter<'a>),
54    Traversable(ZendIteratorIter<'a>),
55}
56
57impl<'a> Iterator for Iter<'a> {
58    type Item = (Zval, &'a Zval);
59
60    fn next(&mut self) -> Option<Self::Item> {
61        match self {
62            Iter::Array(array) => array.next_zval(),
63            Iter::Traversable(traversable) => traversable.next(),
64        }
65    }
66}