Skip to content

Commit b4f199b

Browse files
committed
refactor(array): split array.rs types into smaller files
1 parent da9db12 commit b4f199b

File tree

5 files changed

+632
-604
lines changed

5 files changed

+632
-604
lines changed

src/types/array/array_key.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use std::{convert::TryFrom, fmt::Display};
2+
3+
use crate::{convert::FromZval, error::Error, flags::DataType, types::Zval};
4+
5+
/// Represents the key of a PHP array, which can be either a long or a string.
6+
#[derive(Debug, Clone, PartialEq)]
7+
pub enum ArrayKey<'a> {
8+
/// A numerical key.
9+
/// In Zend API it's represented by `u64` (`zend_ulong`), so the value needs
10+
/// to be cast to `zend_ulong` before passing into Zend functions.
11+
Long(i64),
12+
/// A string key.
13+
String(String),
14+
/// A string key by reference.
15+
Str(&'a str),
16+
}
17+
18+
impl From<String> for ArrayKey<'_> {
19+
fn from(value: String) -> Self {
20+
Self::String(value)
21+
}
22+
}
23+
24+
impl TryFrom<ArrayKey<'_>> for String {
25+
type Error = Error;
26+
27+
fn try_from(value: ArrayKey<'_>) -> std::result::Result<Self, Self::Error> {
28+
match value {
29+
ArrayKey::String(s) => Ok(s),
30+
ArrayKey::Str(s) => Ok(s.to_string()),
31+
ArrayKey::Long(_) => Err(Error::InvalidProperty),
32+
}
33+
}
34+
}
35+
36+
impl TryFrom<ArrayKey<'_>> for i64 {
37+
type Error = Error;
38+
39+
fn try_from(value: ArrayKey<'_>) -> std::result::Result<Self, Self::Error> {
40+
match value {
41+
ArrayKey::Long(i) => Ok(i),
42+
ArrayKey::String(s) => s.parse::<i64>().map_err(|_| Error::InvalidProperty),
43+
ArrayKey::Str(s) => s.parse::<i64>().map_err(|_| Error::InvalidProperty),
44+
}
45+
}
46+
}
47+
48+
impl ArrayKey<'_> {
49+
/// Check if the key is an integer.
50+
///
51+
/// # Returns
52+
///
53+
/// Returns true if the key is an integer, false otherwise.
54+
#[must_use]
55+
pub fn is_long(&self) -> bool {
56+
match self {
57+
ArrayKey::Long(_) => true,
58+
ArrayKey::String(_) | ArrayKey::Str(_) => false,
59+
}
60+
}
61+
}
62+
63+
impl Display for ArrayKey<'_> {
64+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65+
match self {
66+
ArrayKey::Long(key) => write!(f, "{key}"),
67+
ArrayKey::String(key) => write!(f, "{key}"),
68+
ArrayKey::Str(key) => write!(f, "{key}"),
69+
}
70+
}
71+
}
72+
73+
impl<'a> From<&'a str> for ArrayKey<'a> {
74+
fn from(key: &'a str) -> ArrayKey<'a> {
75+
ArrayKey::Str(key)
76+
}
77+
}
78+
79+
impl<'a> From<i64> for ArrayKey<'a> {
80+
fn from(index: i64) -> ArrayKey<'a> {
81+
ArrayKey::Long(index)
82+
}
83+
}
84+
85+
impl<'a> FromZval<'a> for ArrayKey<'_> {
86+
const TYPE: DataType = DataType::String;
87+
88+
fn from_zval(zval: &'a Zval) -> Option<Self> {
89+
if let Some(key) = zval.long() {
90+
return Some(ArrayKey::Long(key));
91+
}
92+
if let Some(key) = zval.string() {
93+
return Some(ArrayKey::String(key));
94+
}
95+
None
96+
}
97+
}

src/types/array/conversions.rs

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
use std::{collections::HashMap, convert::TryFrom, iter::FromIterator};
2+
3+
use crate::{
4+
boxed::ZBox,
5+
convert::{FromZval, IntoZval},
6+
error::{Error, Result},
7+
flags::DataType,
8+
types::Zval,
9+
};
10+
11+
use super::{ArrayKey, ZendHashTable};
12+
13+
///////////////////////////////////////////
14+
// HashMap
15+
///////////////////////////////////////////
16+
17+
// TODO: Generalize hasher
18+
#[allow(clippy::implicit_hasher)]
19+
impl<'a, V> TryFrom<&'a ZendHashTable> for HashMap<String, V>
20+
where
21+
V: FromZval<'a>,
22+
{
23+
type Error = Error;
24+
25+
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
26+
let mut hm = HashMap::with_capacity(value.len());
27+
28+
for (key, val) in value {
29+
hm.insert(
30+
key.to_string(),
31+
V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?,
32+
);
33+
}
34+
35+
Ok(hm)
36+
}
37+
}
38+
39+
impl<K, V> TryFrom<HashMap<K, V>> for ZBox<ZendHashTable>
40+
where
41+
K: AsRef<str>,
42+
V: IntoZval,
43+
{
44+
type Error = Error;
45+
46+
fn try_from(value: HashMap<K, V>) -> Result<Self> {
47+
let mut ht = ZendHashTable::with_capacity(
48+
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
49+
);
50+
51+
for (k, v) in value {
52+
ht.insert(k.as_ref(), v)?;
53+
}
54+
55+
Ok(ht)
56+
}
57+
}
58+
59+
impl<'a, K, V> TryFrom<&'a ZendHashTable> for Vec<(K, V)>
60+
where
61+
K: TryFrom<ArrayKey<'a>, Error = Error>,
62+
V: FromZval<'a>,
63+
{
64+
type Error = Error;
65+
66+
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
67+
let mut vec = Vec::with_capacity(value.len());
68+
69+
for (key, val) in value {
70+
vec.push((
71+
key.try_into()?,
72+
V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?,
73+
));
74+
}
75+
76+
Ok(vec)
77+
}
78+
}
79+
80+
impl<'a, V> TryFrom<&'a ZendHashTable> for Vec<(ArrayKey<'a>, V)>
81+
where
82+
V: FromZval<'a>,
83+
{
84+
type Error = Error;
85+
86+
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
87+
let mut vec = Vec::with_capacity(value.len());
88+
89+
for (key, val) in value {
90+
vec.push((
91+
key,
92+
V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?,
93+
));
94+
}
95+
96+
Ok(vec)
97+
}
98+
}
99+
100+
impl<'a, K, V> TryFrom<Vec<(K, V)>> for ZBox<ZendHashTable>
101+
where
102+
K: Into<ArrayKey<'a>>,
103+
V: IntoZval,
104+
{
105+
type Error = Error;
106+
107+
fn try_from(value: Vec<(K, V)>) -> Result<Self> {
108+
let mut ht = ZendHashTable::with_capacity(
109+
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
110+
);
111+
112+
for (k, v) in value {
113+
ht.insert(k, v)?;
114+
}
115+
116+
Ok(ht)
117+
}
118+
}
119+
120+
// TODO: Generalize hasher
121+
#[allow(clippy::implicit_hasher)]
122+
impl<K, V> IntoZval for HashMap<K, V>
123+
where
124+
K: AsRef<str>,
125+
V: IntoZval,
126+
{
127+
const TYPE: DataType = DataType::Array;
128+
const NULLABLE: bool = false;
129+
130+
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
131+
let arr = self.try_into()?;
132+
zv.set_hashtable(arr);
133+
Ok(())
134+
}
135+
}
136+
137+
// TODO: Generalize hasher
138+
#[allow(clippy::implicit_hasher)]
139+
impl<'a, T> FromZval<'a> for HashMap<String, T>
140+
where
141+
T: FromZval<'a>,
142+
{
143+
const TYPE: DataType = DataType::Array;
144+
145+
fn from_zval(zval: &'a Zval) -> Option<Self> {
146+
zval.array().and_then(|arr| arr.try_into().ok())
147+
}
148+
}
149+
150+
impl<'a, K, V> IntoZval for Vec<(K, V)>
151+
where
152+
K: Into<ArrayKey<'a>>,
153+
V: IntoZval,
154+
{
155+
const TYPE: DataType = DataType::Array;
156+
const NULLABLE: bool = false;
157+
158+
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
159+
let arr = self.try_into()?;
160+
zv.set_hashtable(arr);
161+
Ok(())
162+
}
163+
}
164+
165+
impl<'a, K, V> FromZval<'a> for Vec<(K, V)>
166+
where
167+
K: TryFrom<ArrayKey<'a>, Error = Error>,
168+
V: FromZval<'a>,
169+
{
170+
const TYPE: DataType = DataType::Array;
171+
172+
fn from_zval(zval: &'a Zval) -> Option<Self> {
173+
zval.array().and_then(|arr| arr.try_into().ok())
174+
}
175+
}
176+
177+
impl<'a, V> FromZval<'a> for Vec<(ArrayKey<'a>, V)>
178+
where
179+
V: FromZval<'a>,
180+
{
181+
const TYPE: DataType = DataType::Array;
182+
183+
fn from_zval(zval: &'a Zval) -> Option<Self> {
184+
zval.array().and_then(|arr| arr.try_into().ok())
185+
}
186+
}
187+
188+
///////////////////////////////////////////
189+
// Vec
190+
///////////////////////////////////////////
191+
192+
impl<'a, T> TryFrom<&'a ZendHashTable> for Vec<T>
193+
where
194+
T: FromZval<'a>,
195+
{
196+
type Error = Error;
197+
198+
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
199+
let mut vec = Vec::with_capacity(value.len());
200+
201+
for (_, val) in value {
202+
vec.push(T::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?);
203+
}
204+
205+
Ok(vec)
206+
}
207+
}
208+
209+
impl<T> TryFrom<Vec<T>> for ZBox<ZendHashTable>
210+
where
211+
T: IntoZval,
212+
{
213+
type Error = Error;
214+
215+
fn try_from(value: Vec<T>) -> Result<Self> {
216+
let mut ht = ZendHashTable::with_capacity(
217+
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
218+
);
219+
220+
for val in value {
221+
ht.push(val)?;
222+
}
223+
224+
Ok(ht)
225+
}
226+
}
227+
228+
impl<T> IntoZval for Vec<T>
229+
where
230+
T: IntoZval,
231+
{
232+
const TYPE: DataType = DataType::Array;
233+
const NULLABLE: bool = false;
234+
235+
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
236+
let arr = self.try_into()?;
237+
zv.set_hashtable(arr);
238+
Ok(())
239+
}
240+
}
241+
242+
impl<'a, T> FromZval<'a> for Vec<T>
243+
where
244+
T: FromZval<'a>,
245+
{
246+
const TYPE: DataType = DataType::Array;
247+
248+
fn from_zval(zval: &'a Zval) -> Option<Self> {
249+
zval.array().and_then(|arr| arr.try_into().ok())
250+
}
251+
}
252+
253+
impl FromIterator<Zval> for ZBox<ZendHashTable> {
254+
fn from_iter<T: IntoIterator<Item = Zval>>(iter: T) -> Self {
255+
let mut ht = ZendHashTable::new();
256+
for item in iter {
257+
// Inserting a zval cannot fail, as `push` only returns `Err` if converting
258+
// `val` to a zval fails.
259+
let _ = ht.push(item);
260+
}
261+
ht
262+
}
263+
}
264+
265+
impl FromIterator<(i64, Zval)> for ZBox<ZendHashTable> {
266+
fn from_iter<T: IntoIterator<Item = (i64, Zval)>>(iter: T) -> Self {
267+
let mut ht = ZendHashTable::new();
268+
for (key, val) in iter {
269+
// Inserting a zval cannot fail, as `push` only returns `Err` if converting
270+
// `val` to a zval fails.
271+
let _ = ht.insert_at_index(key, val);
272+
}
273+
ht
274+
}
275+
}
276+
277+
impl<'a> FromIterator<(&'a str, Zval)> for ZBox<ZendHashTable> {
278+
fn from_iter<T: IntoIterator<Item = (&'a str, Zval)>>(iter: T) -> Self {
279+
let mut ht = ZendHashTable::new();
280+
for (key, val) in iter {
281+
// Inserting a zval cannot fail, as `push` only returns `Err` if converting
282+
// `val` to a zval fails.
283+
let _ = ht.insert(key, val);
284+
}
285+
ht
286+
}
287+
}

0 commit comments

Comments
 (0)