ext_php_rs/types/
iterable.rs1use super::array::Iter as ZendHashTableIter;
2use super::iterator::Iter as ZendIteratorIter;
3use crate::convert::FromZvalMut;
4use crate::flags::DataType;
5use crate::types::{ZendHashTable, ZendIterator, Zval};
6
7#[derive(Debug)]
23pub enum Iterable<'a> {
24 Array(&'a ZendHashTable),
26 Traversable(&'a mut ZendIterator),
28}
29
30impl Iterable<'_> {
31 #[allow(clippy::iter_not_returning_iterator)]
35 pub fn iter(&mut self) -> Option<Iter<'_>> {
36 match self {
37 Iterable::Array(array) => Some(Iter::Array(array.iter())),
38 Iterable::Traversable(traversable) => Some(Iter::Traversable(traversable.iter()?)),
39 }
40 }
41}
42
43#[allow(clippy::into_iter_without_iter)]
45impl<'a> IntoIterator for &'a mut Iterable<'a> {
46 type Item = (Zval, Zval);
47 type IntoIter = Iter<'a>;
48
49 #[expect(
50 clippy::expect_used,
51 reason = "IntoIterator has no error channel, and a fresh iterator can always be rewound"
52 )]
53 fn into_iter(self) -> Self::IntoIter {
54 self.iter().expect("Could not rewind iterator!")
55 }
56}
57
58impl<'a> FromZvalMut<'a> for Iterable<'a> {
59 const TYPE: DataType = DataType::Iterable;
60
61 fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
62 if zval.is_traversable() {
63 return zval.traversable().map(Iterable::Traversable);
64 }
65
66 zval.array().map(Iterable::Array)
67 }
68}
69
70pub enum Iter<'a> {
75 Array(ZendHashTableIter<'a>),
76 Traversable(ZendIteratorIter<'a>),
77}
78
79impl Iterator for Iter<'_> {
80 type Item = (Zval, Zval);
81
82 fn next(&mut self) -> Option<Self::Item> {
83 match self {
84 Iter::Array(array) => array
85 .next_zval()
86 .map(|(key, value)| (key, value.shallow_clone())),
87 Iter::Traversable(traversable) => traversable.next(),
88 }
89 }
90}