Skip to content

Commit 59012d2

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

File tree

7 files changed

+656
-604
lines changed

7 files changed

+656
-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+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::{collections::HashMap, convert::TryFrom};
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::super::ZendHashTable;
12+
13+
// TODO: Generalize hasher
14+
#[allow(clippy::implicit_hasher)]
15+
impl<'a, V> TryFrom<&'a ZendHashTable> for HashMap<String, V>
16+
where
17+
V: FromZval<'a>,
18+
{
19+
type Error = Error;
20+
21+
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
22+
let mut hm = HashMap::with_capacity(value.len());
23+
24+
for (key, val) in value {
25+
hm.insert(
26+
key.to_string(),
27+
V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?,
28+
);
29+
}
30+
31+
Ok(hm)
32+
}
33+
}
34+
35+
impl<K, V> TryFrom<HashMap<K, V>> for ZBox<ZendHashTable>
36+
where
37+
K: AsRef<str>,
38+
V: IntoZval,
39+
{
40+
type Error = Error;
41+
42+
fn try_from(value: HashMap<K, V>) -> Result<Self> {
43+
let mut ht = ZendHashTable::with_capacity(
44+
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
45+
);
46+
47+
for (k, v) in value {
48+
ht.insert(k.as_ref(), v)?;
49+
}
50+
51+
Ok(ht)
52+
}
53+
}
54+
55+
// TODO: Generalize hasher
56+
#[allow(clippy::implicit_hasher)]
57+
impl<K, V> IntoZval for HashMap<K, V>
58+
where
59+
K: AsRef<str>,
60+
V: IntoZval,
61+
{
62+
const TYPE: DataType = DataType::Array;
63+
const NULLABLE: bool = false;
64+
65+
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
66+
let arr = self.try_into()?;
67+
zv.set_hashtable(arr);
68+
Ok(())
69+
}
70+
}
71+
72+
// TODO: Generalize hasher
73+
#[allow(clippy::implicit_hasher)]
74+
impl<'a, T> FromZval<'a> for HashMap<String, T>
75+
where
76+
T: FromZval<'a>,
77+
{
78+
const TYPE: DataType = DataType::Array;
79+
80+
fn from_zval(zval: &'a Zval) -> Option<Self> {
81+
zval.array().and_then(|arr| arr.try_into().ok())
82+
}
83+
}

src/types/array/conversions/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! Collection type conversions for `ZendHashTable`.
2+
//!
3+
//! This module provides conversions between Rust collection types and PHP arrays
4+
//! (represented as `ZendHashTable`). Each collection type has its own module for
5+
//! better organization and maintainability.
6+
//!
7+
//! ## Supported Collections
8+
//!
9+
//! - `HashMap<K, V>` ↔ `ZendHashTable` (via `hash_map` module)
10+
//! - `Vec<T>` and `Vec<(K, V)>` ↔ `ZendHashTable` (via `vec` module)
11+
12+
mod hash_map;
13+
mod vec;

src/types/array/conversions/vec.rs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
use std::convert::TryFrom;
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::super::{ArrayKey, ZendHashTable};
12+
13+
///////////////////////////////////////////
14+
// Vec<(K, V)> conversions
15+
///////////////////////////////////////////
16+
17+
impl<'a, K, V> TryFrom<&'a ZendHashTable> for Vec<(K, V)>
18+
where
19+
K: TryFrom<ArrayKey<'a>, Error = Error>,
20+
V: FromZval<'a>,
21+
{
22+
type Error = Error;
23+
24+
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
25+
let mut vec = Vec::with_capacity(value.len());
26+
27+
for (key, val) in value {
28+
vec.push((
29+
key.try_into()?,
30+
V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?,
31+
));
32+
}
33+
34+
Ok(vec)
35+
}
36+
}
37+
38+
impl<'a, V> TryFrom<&'a ZendHashTable> for Vec<(ArrayKey<'a>, V)>
39+
where
40+
V: FromZval<'a>,
41+
{
42+
type Error = Error;
43+
44+
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
45+
let mut vec = Vec::with_capacity(value.len());
46+
47+
for (key, val) in value {
48+
vec.push((
49+
key,
50+
V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?,
51+
));
52+
}
53+
54+
Ok(vec)
55+
}
56+
}
57+
58+
impl<'a, K, V> TryFrom<Vec<(K, V)>> for ZBox<ZendHashTable>
59+
where
60+
K: Into<ArrayKey<'a>>,
61+
V: IntoZval,
62+
{
63+
type Error = Error;
64+
65+
fn try_from(value: Vec<(K, V)>) -> Result<Self> {
66+
let mut ht = ZendHashTable::with_capacity(
67+
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
68+
);
69+
70+
for (k, v) in value {
71+
ht.insert(k, v)?;
72+
}
73+
74+
Ok(ht)
75+
}
76+
}
77+
78+
impl<'a, K, V> IntoZval for Vec<(K, V)>
79+
where
80+
K: Into<ArrayKey<'a>>,
81+
V: IntoZval,
82+
{
83+
const TYPE: DataType = DataType::Array;
84+
const NULLABLE: bool = false;
85+
86+
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
87+
let arr = self.try_into()?;
88+
zv.set_hashtable(arr);
89+
Ok(())
90+
}
91+
}
92+
93+
impl<'a, K, V> FromZval<'a> for Vec<(K, V)>
94+
where
95+
K: TryFrom<ArrayKey<'a>, Error = Error>,
96+
V: FromZval<'a>,
97+
{
98+
const TYPE: DataType = DataType::Array;
99+
100+
fn from_zval(zval: &'a Zval) -> Option<Self> {
101+
zval.array().and_then(|arr| arr.try_into().ok())
102+
}
103+
}
104+
105+
impl<'a, V> FromZval<'a> for Vec<(ArrayKey<'a>, V)>
106+
where
107+
V: FromZval<'a>,
108+
{
109+
const TYPE: DataType = DataType::Array;
110+
111+
fn from_zval(zval: &'a Zval) -> Option<Self> {
112+
zval.array().and_then(|arr| arr.try_into().ok())
113+
}
114+
}
115+
116+
///////////////////////////////////////////
117+
// Vec<T> conversions
118+
///////////////////////////////////////////
119+
120+
impl<'a, T> TryFrom<&'a ZendHashTable> for Vec<T>
121+
where
122+
T: FromZval<'a>,
123+
{
124+
type Error = Error;
125+
126+
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
127+
let mut vec = Vec::with_capacity(value.len());
128+
129+
for (_, val) in value {
130+
vec.push(T::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?);
131+
}
132+
133+
Ok(vec)
134+
}
135+
}
136+
137+
impl<T> TryFrom<Vec<T>> for ZBox<ZendHashTable>
138+
where
139+
T: IntoZval,
140+
{
141+
type Error = Error;
142+
143+
fn try_from(value: Vec<T>) -> Result<Self> {
144+
let mut ht = ZendHashTable::with_capacity(
145+
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
146+
);
147+
148+
for val in value {
149+
ht.push(val)?;
150+
}
151+
152+
Ok(ht)
153+
}
154+
}
155+
156+
impl<T> IntoZval for Vec<T>
157+
where
158+
T: IntoZval,
159+
{
160+
const TYPE: DataType = DataType::Array;
161+
const NULLABLE: bool = false;
162+
163+
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
164+
let arr = self.try_into()?;
165+
zv.set_hashtable(arr);
166+
Ok(())
167+
}
168+
}
169+
170+
impl<'a, T> FromZval<'a> for Vec<T>
171+
where
172+
T: FromZval<'a>,
173+
{
174+
const TYPE: DataType = DataType::Array;
175+
176+
fn from_zval(zval: &'a Zval) -> Option<Self> {
177+
zval.array().and_then(|arr| arr.try_into().ok())
178+
}
179+
}

0 commit comments

Comments
 (0)