Skip to content

Commit 146e92e

Browse files
authored
feat(types): Argument coercion into HashSet/BTreeSet #493 (#598)
* feat(types): Argument coercion into HashSet/BTreeSet #493 * feat(types): array: impl FromZval for HashSet/BTreeSet * feat(types): array: impl FromZval for HashSet/BTreeSet: tests
1 parent 2092227 commit 146e92e

File tree

6 files changed

+505
-17
lines changed

6 files changed

+505
-17
lines changed

src/types/array/array_key.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::str::FromStr;
33
use std::{convert::TryFrom, fmt::Display};
44

55
/// Represents the key of a PHP array, which can be either a long or a string.
6-
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
6+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
77
pub enum ArrayKey<'a> {
88
/// A numerical key.
99
/// In Zend API it's represented by `u64` (`zend_ulong`), so the value needs
@@ -92,12 +92,38 @@ impl<'a> From<&'a str> for ArrayKey<'a> {
9292
}
9393
}
9494

95+
impl<'a> From<i32> for ArrayKey<'a> {
96+
fn from(index: i32) -> ArrayKey<'a> {
97+
ArrayKey::Long(i64::from(index))
98+
}
99+
}
100+
95101
impl<'a> From<i64> for ArrayKey<'a> {
96102
fn from(index: i64) -> ArrayKey<'a> {
97103
ArrayKey::Long(index)
98104
}
99105
}
100106

107+
impl<'a> From<u64> for ArrayKey<'a> {
108+
fn from(index: u64) -> ArrayKey<'a> {
109+
if let Ok(index) = i64::try_from(index) {
110+
ArrayKey::Long(index)
111+
} else {
112+
ArrayKey::String(index.to_string())
113+
}
114+
}
115+
}
116+
117+
impl<'a> From<usize> for ArrayKey<'a> {
118+
fn from(index: usize) -> ArrayKey<'a> {
119+
if let Ok(index) = i64::try_from(index) {
120+
ArrayKey::Long(index)
121+
} else {
122+
ArrayKey::String(index.to_string())
123+
}
124+
}
125+
}
126+
101127
impl<'a> FromZval<'a> for ArrayKey<'_> {
102128
const TYPE: DataType = DataType::String;
103129

src/types/array/conversions/btree_map.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ mod tests {
122122
use crate::types::{ArrayKey, ZendHashTable, Zval};
123123

124124
#[test]
125-
fn test_hash_table_try_from_btree_mab() {
125+
fn test_hash_table_try_from_btree_map() {
126126
Embed::run(|| {
127127
let mut map = BTreeMap::new();
128128
map.insert("key1", "value1");
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
use super::super::ZendHashTable;
2+
use crate::{
3+
boxed::ZBox,
4+
convert::{FromZval, IntoZval},
5+
error::{Error, Result},
6+
flags::DataType,
7+
types::Zval,
8+
};
9+
use std::collections::BTreeSet;
10+
use std::convert::TryFrom;
11+
12+
impl<'a, V> TryFrom<&'a ZendHashTable> for BTreeSet<V>
13+
where
14+
V: FromZval<'a> + Ord,
15+
{
16+
type Error = Error;
17+
18+
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
19+
let mut set = Self::new();
20+
21+
for (_key, val) in value {
22+
set.insert(V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?);
23+
}
24+
25+
Ok(set)
26+
}
27+
}
28+
29+
impl<V> TryFrom<BTreeSet<V>> for ZBox<ZendHashTable>
30+
where
31+
V: IntoZval,
32+
{
33+
type Error = Error;
34+
35+
fn try_from(value: BTreeSet<V>) -> Result<Self> {
36+
let mut set = ZendHashTable::with_capacity(
37+
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
38+
);
39+
40+
for (k, v) in value.into_iter().enumerate() {
41+
set.insert(k, v)?;
42+
}
43+
44+
Ok(set)
45+
}
46+
}
47+
48+
impl<'a, V> FromZval<'a> for BTreeSet<V>
49+
where
50+
V: FromZval<'a> + Ord,
51+
{
52+
const TYPE: DataType = DataType::Array;
53+
54+
fn from_zval(zval: &'a Zval) -> Option<Self> {
55+
zval.array().and_then(|arr| arr.try_into().ok())
56+
}
57+
}
58+
59+
impl<V> IntoZval for BTreeSet<V>
60+
where
61+
V: IntoZval,
62+
{
63+
const TYPE: DataType = DataType::Array;
64+
const NULLABLE: bool = false;
65+
66+
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
67+
let arr = self.try_into()?;
68+
zv.set_hashtable(arr);
69+
Ok(())
70+
}
71+
}
72+
73+
#[cfg(test)]
74+
#[cfg(feature = "embed")]
75+
#[allow(clippy::unwrap_used)]
76+
mod tests {
77+
use std::collections::BTreeSet;
78+
79+
use crate::boxed::ZBox;
80+
use crate::convert::FromZval;
81+
use crate::embed::Embed;
82+
use crate::types::{ZendHashTable, Zval};
83+
84+
#[test]
85+
fn test_hash_table_try_from_btree_set() {
86+
Embed::run(|| {
87+
let mut set = BTreeSet::new();
88+
set.insert("one");
89+
let ht: ZBox<ZendHashTable> = set.try_into().unwrap();
90+
assert_eq!(ht.len(), 1);
91+
assert!(ht.get(0).is_some());
92+
});
93+
}
94+
95+
#[test]
96+
fn test_btree_set_try_from_hash_table() {
97+
Embed::run(|| {
98+
let mut ht = ZendHashTable::new();
99+
ht.insert(0, "value1").unwrap();
100+
ht.insert(1, "value2").unwrap();
101+
ht.insert(2, "value3").unwrap();
102+
let mut zval = Zval::new();
103+
zval.set_hashtable(ht);
104+
105+
let map = BTreeSet::<String>::from_zval(&zval).unwrap();
106+
assert_eq!(map.len(), 3);
107+
let mut it = map.iter();
108+
assert_eq!(it.next().unwrap(), "value1");
109+
assert_eq!(it.next().unwrap(), "value2");
110+
assert_eq!(it.next().unwrap(), "value3");
111+
assert_eq!(it.next(), None);
112+
});
113+
}
114+
}

0 commit comments

Comments
 (0)