Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions phper/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,120 @@ pub trait FromZValMut<'a>: Sized {
fn expect(val: &'a mut ZVal) -> crate::Result<Self>;
}

/// Borrowed value set converted from immutable [`ZVal`].
#[derive(Debug)]
pub enum ZValRef<'a> {
/// `null`
Null,
/// `bool`
Bool(bool),
/// `int`
Long(i64),
/// `float`
Double(f64),
/// `string`
Str(&'a ZStr),
/// `array`
Arr(&'a ZArr),
/// `object`
Obj(&'a ZObj),
/// `resource`
Res(&'a ZRes),
/// `reference`
Ref(&'a ZRef),
}

impl<'a> ZValRef<'a> {
/// Converts from immutable [`ZVal`].
#[inline]
pub fn from_z_val(val: &'a ZVal) -> crate::Result<Self> {
<Self as FromZVal>::expect(val)
}
}

impl<'a> FromZVal<'a> for ZValRef<'a> {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
let t = val.get_type_info();

if t.is_null() {
Ok(Self::Null)
} else if t.is_bool() {
Ok(Self::Bool(val.expect_bool()?))
} else if t.is_long() {
Ok(Self::Long(val.expect_long()?))
} else if t.is_double() {
Ok(Self::Double(val.expect_double()?))
} else if t.is_string() {
Ok(Self::Str(val.expect_z_str()?))
} else if t.is_array() {
Ok(Self::Arr(val.expect_z_arr()?))
} else if t.is_object() {
Ok(Self::Obj(val.expect_z_obj()?))
} else if t.is_resource() {
Ok(Self::Res(val.expect_z_res()?))
} else if t.is_reference() {
Ok(Self::Ref(val.expect_z_ref()?))
} else {
Err(ExpectTypeError::new(TypeInfo::NULL, t).into())
}
}
}

/// Borrowed value set converted from mutable [`ZVal`].
#[derive(Debug)]
pub enum ZValMut<'a> {
/// `null`
Null,
/// `int`
Long(&'a mut i64),
/// `float`
Double(&'a mut f64),
/// `string`
Str(&'a mut ZStr),
/// `array`
Arr(&'a mut ZArr),
/// `object`
Obj(&'a mut ZObj),
/// `resource`
Res(&'a mut ZRes),
/// `reference`
Ref(&'a mut ZRef),
}

impl<'a> ZValMut<'a> {
/// Converts from mutable [`ZVal`].
#[inline]
pub fn from_z_val_mut(val: &'a mut ZVal) -> crate::Result<Self> {
<Self as FromZValMut>::expect(val)
}
}

impl<'a> FromZValMut<'a> for ZValMut<'a> {
fn expect(val: &'a mut ZVal) -> crate::Result<Self> {
let t = val.get_type_info();

if t.is_null() {
Ok(Self::Null)
} else if t.is_long() {
Ok(Self::Long(val.expect_mut_long()?))
} else if t.is_double() {
Ok(Self::Double(val.expect_mut_double()?))
} else if t.is_string() {
Ok(Self::Str(val.expect_mut_z_str()?))
} else if t.is_array() {
Ok(Self::Arr(val.expect_mut_z_arr()?))
} else if t.is_object() {
Ok(Self::Obj(val.expect_mut_z_obj()?))
} else if t.is_resource() {
Ok(Self::Res(val.expect_mut_z_res()?))
} else if t.is_reference() {
Ok(Self::Ref(val.expect_mut_z_ref()?))
} else {
Err(ExpectTypeError::new(TypeInfo::LONG, t).into())
}
}
}

impl ZVal {
/// Wraps a raw pointer.
///
Expand Down Expand Up @@ -365,6 +479,18 @@ impl ZVal {
T::expect(self)
}

/// Converts current [`ZVal`] to borrowed [`ZValRef`].
#[inline]
pub fn to_value(&self) -> crate::Result<ZValRef<'_>> {
ZValRef::from_z_val(self)
}

/// Converts current mutable [`ZVal`] to borrowed [`ZValMut`].
#[inline]
pub fn to_value_mut(&mut self) -> crate::Result<ZValMut<'_>> {
ZValMut::from_z_val_mut(self)
}

/// Converts to null if `ZVal` is null.
pub fn as_null(&self) -> Option<()> {
self.expect_null().ok()
Expand Down
109 changes: 108 additions & 1 deletion tests/integration/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use phper::{
arrays::{InsertKey, ZArray},
modules::Module,
objects::ZObject,
values::ZVal,
values::{ZVal, ZValMut, ZValRef},
};
use std::convert::Infallible;

Expand Down Expand Up @@ -280,5 +280,112 @@ fn integration_values_as(_: &mut [ZVal]) -> Result<(), Infallible> {
assert!(val.expect_type::<&phper::arrays::ZArr>().is_err());
}

{
let val = ZVal::default();
match val.to_value().unwrap() {
ZValRef::Null => {}
other => panic!("expect Null, got {other:?}"),
}
}

{
let val = ZVal::from("foo");
match ZValRef::from_z_val(&val).unwrap() {
ZValRef::Str(s) => assert_eq!(s.to_bytes(), b"foo"),
other => panic!("expect Str, got {other:?}"),
}
}

{
let mut val = ZVal::default();
match val.to_value_mut().unwrap() {
ZValMut::Null => {}
other => panic!("expect Null, got {other:?}"),
}
}

{
let mut val = ZVal::from(100i64);
match ZValMut::from_z_val_mut(&mut val).unwrap() {
ZValMut::Long(i) => *i += 23,
other => panic!("expect Long, got {other:?}"),
}
assert_eq!(val.expect_long().unwrap(), 123);
}

{
let mut val = ZVal::from("bar");
match val.to_value_mut().unwrap() {
ZValMut::Str(s) => {
assert_eq!(s.to_bytes(), b"bar");
}
other => panic!("expect Str, got {other:?}"),
}
}

{
let mut arr = ZArray::new();
arr.insert(InsertKey::NextIndex, ZVal::from("x"));
let val = ZVal::from(arr);

match val.to_value().unwrap() {
ZValRef::Arr(a) => {
let got = a.get(0).unwrap().expect_z_str().unwrap().to_bytes();
assert_eq!(got, b"x");
}
other => panic!("expect Arr, got {other:?}"),
}
}

{
let mut obj = ZObject::new_by_std_class();
obj.set_property("name", ZVal::from("copilot"));
let val = ZVal::from(obj);

match val.to_value().unwrap() {
ZValRef::Obj(o) => {
let got = o.get_property("name").expect_z_str().unwrap().to_bytes();
assert_eq!(got, b"copilot");
}
other => panic!("expect Obj, got {other:?}"),
}
}

{
let mut arr = ZArray::new();
arr.insert(InsertKey::NextIndex, ZVal::from("a"));
let mut val = ZVal::from(arr);

match val.to_value_mut().unwrap() {
ZValMut::Arr(a) => {
a.insert(InsertKey::NextIndex, ZVal::from("b"));
assert_eq!(a.len(), 2);
}
other => panic!("expect Arr, got {other:?}"),
}
}

{
let mut obj = ZObject::new_by_std_class();
obj.set_property("foo", ZVal::from("bar"));
let mut val = ZVal::from(obj);

match val.to_value_mut().unwrap() {
ZValMut::Obj(o) => {
o.set_property("foo", ZVal::from("baz"));
}
other => panic!("expect Obj, got {other:?}"),
}

let got = val
.expect_type::<&phper::objects::ZObj>()
.unwrap()
.get_property("foo")
.expect_z_str()
.unwrap()
.to_bytes();
assert_eq!(got, b"baz");
}

Ok(())
}
Loading