• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

extphprs / ext-php-rs / 21706110311

05 Feb 2026 09:29AM UTC coverage: 34.453% (-0.2%) from 34.699%
21706110311

push

github

web-flow
feat(types): indexmap feature #522 (#670)

0 of 43 new or added lines in 2 files covered. (0.0%)

2083 of 6046 relevant lines covered (34.45%)

23.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/types/array/conversions/index_set.rs
1
//! `IndexSet` conversions for `ZendHashTable`.
2
//!
3
//! Unlike `HashSet`, `IndexSet` preserves insertion order, making it suitable
4
//! for working with PHP arrays where element order matters.
5

6
use super::super::ZendHashTable;
7
use crate::{
8
    boxed::ZBox,
9
    convert::{FromZval, IntoZval},
10
    error::{Error, Result},
11
    flags::DataType,
12
    types::Zval,
13
};
14
use indexmap::IndexSet;
15
use std::convert::TryFrom;
16
use std::hash::{BuildHasher, Hash};
17

18
impl<'a, V, H> TryFrom<&'a ZendHashTable> for IndexSet<V, H>
19
where
20
    V: FromZval<'a> + Eq + Hash,
21
    H: BuildHasher + Default,
22
{
23
    type Error = Error;
24

NEW
25
    fn try_from(value: &'a ZendHashTable) -> Result<Self> {
×
NEW
26
        let mut set = Self::with_capacity_and_hasher(value.len(), H::default());
×
27

NEW
28
        for (_key, val) in value {
×
NEW
29
            set.insert(V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?);
×
30
        }
31

NEW
32
        Ok(set)
×
33
    }
34
}
35

36
impl<V, H> TryFrom<IndexSet<V, H>> for ZBox<ZendHashTable>
37
where
38
    V: IntoZval,
39
    H: BuildHasher,
40
{
41
    type Error = Error;
42

NEW
43
    fn try_from(value: IndexSet<V, H>) -> Result<Self> {
×
44
        let mut ht = ZendHashTable::with_capacity(
NEW
45
            value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
×
46
        );
47

NEW
48
        for (k, v) in value.into_iter().enumerate() {
×
NEW
49
            ht.insert(k, v)?;
×
50
        }
51

NEW
52
        Ok(ht)
×
53
    }
54
}
55

56
impl<'a, V, H> FromZval<'a> for IndexSet<V, H>
57
where
58
    V: FromZval<'a> + Eq + Hash,
59
    H: BuildHasher + Default,
60
{
61
    const TYPE: DataType = DataType::Array;
62

NEW
63
    fn from_zval(zval: &'a Zval) -> Option<Self> {
×
NEW
64
        zval.array().and_then(|arr| arr.try_into().ok())
×
65
    }
66
}
67

68
impl<V, H> IntoZval for IndexSet<V, H>
69
where
70
    V: IntoZval,
71
    H: BuildHasher,
72
{
73
    const TYPE: DataType = DataType::Array;
74
    const NULLABLE: bool = false;
75

NEW
76
    fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
×
NEW
77
        let arr = self.try_into()?;
×
NEW
78
        zv.set_hashtable(arr);
×
NEW
79
        Ok(())
×
80
    }
81
}
82

83
#[cfg(test)]
84
#[cfg(feature = "embed")]
85
#[allow(clippy::unwrap_used)]
86
mod tests {
87
    use indexmap::IndexSet;
88

89
    use crate::boxed::ZBox;
90
    use crate::convert::{FromZval, IntoZval};
91
    use crate::embed::Embed;
92
    use crate::types::{ZendHashTable, Zval};
93

94
    #[test]
95
    fn test_hash_table_try_from_index_set() {
96
        Embed::run(|| {
97
            let mut set = IndexSet::new();
98
            set.insert("one");
99
            set.insert("two");
100
            set.insert("three");
101

102
            let ht: ZBox<ZendHashTable> = set.try_into().unwrap();
103
            assert_eq!(ht.len(), 3);
104
            assert_eq!(ht.get(0).unwrap().string().unwrap(), "one");
105
            assert_eq!(ht.get(1).unwrap().string().unwrap(), "two");
106
            assert_eq!(ht.get(2).unwrap().string().unwrap(), "three");
107
        });
108
    }
109

110
    #[test]
111
    fn test_index_set_into_zval() {
112
        Embed::run(|| {
113
            let mut set = IndexSet::new();
114
            set.insert("one");
115
            set.insert("two");
116
            set.insert("three");
117

118
            let zval = set.into_zval(false).unwrap();
119
            assert!(zval.is_array());
120
            let ht: &ZendHashTable = zval.array().unwrap();
121
            assert_eq!(ht.len(), 3);
122
            assert_eq!(ht.get(0).unwrap().string().unwrap(), "one");
123
            assert_eq!(ht.get(1).unwrap().string().unwrap(), "two");
124
            assert_eq!(ht.get(2).unwrap().string().unwrap(), "three");
125
        });
126
    }
127

128
    #[test]
129
    fn test_index_set_try_from_hash_table() {
130
        Embed::run(|| {
131
            let mut ht = ZendHashTable::new();
132
            ht.insert(0, "value1").unwrap();
133
            ht.insert(1, "value2").unwrap();
134
            ht.insert(2, "value3").unwrap();
135
            let mut zval = Zval::new();
136
            zval.set_hashtable(ht);
137

138
            let set = IndexSet::<String>::from_zval(&zval).unwrap();
139
            assert_eq!(set.len(), 3);
140
            assert!(set.contains("value1"));
141
            assert!(set.contains("value2"));
142
            assert!(set.contains("value3"));
143
        });
144
    }
145

146
    #[test]
147
    fn test_index_set_preserves_order() {
148
        Embed::run(|| {
149
            let mut set = IndexSet::new();
150
            set.insert("z");
151
            set.insert("a");
152
            set.insert("m");
153

154
            let ht: ZBox<ZendHashTable> = set.try_into().unwrap();
155

156
            // Verify order is preserved by iterating
157
            let values: Vec<_> = ht.iter().map(|(_, v)| v.string().unwrap()).collect();
158
            assert_eq!(values, vec!["z", "a", "m"]);
159

160
            // Convert back and verify order
161
            let set_back: IndexSet<String> = ht.as_ref().try_into().unwrap();
162
            let values_back: Vec<_> = set_back.iter().cloned().collect();
163
            assert_eq!(values_back, vec!["z", "a", "m"]);
164
        });
165
    }
166
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc