diff --git a/phper/src/values.rs b/phper/src/values.rs index c6256c6..810eb95 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -259,6 +259,120 @@ pub trait FromZValMut<'a>: Sized { fn expect(val: &'a mut ZVal) -> crate::Result; } +/// 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 { + ::expect(val) + } +} + +impl<'a> FromZVal<'a> for ZValRef<'a> { + fn expect(val: &'a ZVal) -> crate::Result { + 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 { + ::expect(val) + } +} + +impl<'a> FromZValMut<'a> for ZValMut<'a> { + fn expect(val: &'a mut ZVal) -> crate::Result { + 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. /// @@ -365,6 +479,18 @@ impl ZVal { T::expect(self) } + /// Converts current [`ZVal`] to borrowed [`ZValRef`]. + #[inline] + pub fn to_value(&self) -> crate::Result> { + ZValRef::from_z_val(self) + } + + /// Converts current mutable [`ZVal`] to borrowed [`ZValMut`]. + #[inline] + pub fn to_value_mut(&mut self) -> crate::Result> { + ZValMut::from_z_val_mut(self) + } + /// Converts to null if `ZVal` is null. pub fn as_null(&self) -> Option<()> { self.expect_null().ok() diff --git a/tests/integration/src/values.rs b/tests/integration/src/values.rs index 978371d..164a79e 100644 --- a/tests/integration/src/values.rs +++ b/tests/integration/src/values.rs @@ -12,7 +12,7 @@ use phper::{ arrays::{InsertKey, ZArray}, modules::Module, objects::ZObject, - values::ZVal, + values::{ZVal, ZValMut, ZValRef}, }; use std::convert::Infallible; @@ -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(()) }