diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff36389..f16e7f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,12 +123,6 @@ jobs: command: clippy args: --release - - name: Cargo build - uses: actions-rs/cargo@v1 - with: - command: build - args: --release - - name: Cargo test uses: actions-rs/cargo@v1 with: diff --git a/phper/src/values.rs b/phper/src/values.rs index 690aed0..c6256c6 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -236,6 +236,29 @@ pub struct ZVal { _p: PhantomData<*mut ()>, } +/// Conversion from immutable [`ZVal`]. +pub trait FromZVal<'a>: Sized { + /// Converts from `ZVal`, return `None` when type mismatch. + fn as_(val: &'a ZVal) -> Option { + Self::expect(val).ok() + } + + /// Converts from `ZVal`, return [`ExpectTypeError`] when type mismatch. + fn expect(val: &'a ZVal) -> crate::Result; +} + +/// Conversion from mutable [`ZVal`]. +pub trait FromZValMut<'a>: Sized { + /// Converts from mutable `ZVal`, return `None` when type mismatch. + fn as_(val: &'a mut ZVal) -> Option { + Self::expect(val).ok() + } + + /// Converts from mutable `ZVal`, return [`ExpectTypeError`] when type + /// mismatch. + fn expect(val: &'a mut ZVal) -> crate::Result; +} + impl ZVal { /// Wraps a raw pointer. /// @@ -308,6 +331,40 @@ impl ZVal { t.into() } + /// Converts to target type by [`FromZVal`]. + pub fn as_type<'a, T>(&'a self) -> Option + where + T: FromZVal<'a>, + { + T::as_(self) + } + + /// Converts to target type by [`FromZVal`], otherwise returns + /// [`ExpectTypeError`]. + pub fn expect_type<'a, T>(&'a self) -> crate::Result + where + T: FromZVal<'a>, + { + T::expect(self) + } + + /// Converts to target mutable type by [`FromZValMut`]. + pub fn as_mut_type<'a, T>(&'a mut self) -> Option + where + T: FromZValMut<'a>, + { + T::as_(self) + } + + /// Converts to target mutable type by [`FromZValMut`], otherwise returns + /// [`ExpectTypeError`]. + pub fn expect_mut_type<'a, T>(&'a mut self) -> crate::Result + where + T: FromZValMut<'a>, + { + T::expect(self) + } + /// Converts to null if `ZVal` is null. pub fn as_null(&self) -> Option<()> { self.expect_null().ok() @@ -659,6 +716,102 @@ impl Debug for ZVal { } } +impl<'a> FromZVal<'a> for () { + fn expect(val: &'a ZVal) -> crate::Result { + val.expect_null() + } +} + +impl<'a> FromZVal<'a> for bool { + fn expect(val: &'a ZVal) -> crate::Result { + val.expect_bool() + } +} + +impl<'a> FromZVal<'a> for i64 { + fn expect(val: &'a ZVal) -> crate::Result { + val.expect_long() + } +} + +impl<'a> FromZValMut<'a> for &'a mut i64 { + fn expect(val: &'a mut ZVal) -> crate::Result { + val.expect_mut_long() + } +} + +impl<'a> FromZVal<'a> for f64 { + fn expect(val: &'a ZVal) -> crate::Result { + val.expect_double() + } +} + +impl<'a> FromZValMut<'a> for &'a mut f64 { + fn expect(val: &'a mut ZVal) -> crate::Result { + val.expect_mut_double() + } +} + +impl<'a> FromZVal<'a> for &'a ZStr { + fn expect(val: &'a ZVal) -> crate::Result { + val.expect_z_str() + } +} + +impl<'a> FromZValMut<'a> for &'a mut ZStr { + fn expect(val: &'a mut ZVal) -> crate::Result { + val.expect_mut_z_str() + } +} + +impl<'a> FromZVal<'a> for &'a ZArr { + fn expect(val: &'a ZVal) -> crate::Result { + val.expect_z_arr() + } +} + +impl<'a> FromZValMut<'a> for &'a mut ZArr { + fn expect(val: &'a mut ZVal) -> crate::Result { + val.expect_mut_z_arr() + } +} + +impl<'a> FromZVal<'a> for &'a ZObj { + fn expect(val: &'a ZVal) -> crate::Result { + val.expect_z_obj() + } +} + +impl<'a> FromZValMut<'a> for &'a mut ZObj { + fn expect(val: &'a mut ZVal) -> crate::Result { + val.expect_mut_z_obj() + } +} + +impl<'a> FromZVal<'a> for &'a ZRes { + fn expect(val: &'a ZVal) -> crate::Result { + val.expect_z_res() + } +} + +impl<'a> FromZValMut<'a> for &'a mut ZRes { + fn expect(val: &'a mut ZVal) -> crate::Result { + val.expect_mut_z_res() + } +} + +impl<'a> FromZVal<'a> for &'a ZRef { + fn expect(val: &'a ZVal) -> crate::Result { + val.expect_z_ref() + } +} + +impl<'a> FromZValMut<'a> for &'a mut ZRef { + fn expect(val: &'a mut ZVal) -> crate::Result { + val.expect_mut_z_ref() + } +} + impl Default for ZVal { #[inline] fn default() -> Self { diff --git a/tests/integration/src/values.rs b/tests/integration/src/values.rs index 2f35f38..978371d 100644 --- a/tests/integration/src/values.rs +++ b/tests/integration/src/values.rs @@ -18,7 +18,6 @@ use std::convert::Infallible; pub fn integrate(module: &mut Module) { integrate_returns(module); - integrate_as(module); } fn integrate_returns(module: &mut Module) { @@ -82,6 +81,7 @@ fn integrate_returns(module: &mut Module) { "integration_values_return_val", integration_values_return_val, ); + module.add_function("integration_values_as", integration_values_as); } fn integration_values_return_null(_: &mut [ZVal]) -> Result<(), Infallible> { @@ -155,7 +155,7 @@ fn integration_values_return_val(_: &mut [ZVal]) -> Result { Ok(ZVal::from("foo")) } -fn integrate_as(_module: &mut Module) { +fn integration_values_as(_: &mut [ZVal]) -> Result<(), Infallible> { { let val = ZVal::default(); assert_eq!(val.as_null(), Some(())); @@ -187,4 +187,98 @@ fn integrate_as(_module: &mut Module) { } assert_eq!(val.as_double(), Some(200.)); } + + { + let val = ZVal::default(); + assert_eq!(val.as_type::<()>(), Some(())); + assert!(val.expect_type::<()>().is_ok()); + assert!(val.expect_type::().is_err()); + } + + { + let val = ZVal::from(true); + assert_eq!(val.as_type::(), Some(true)); + assert_eq!(val.as_type::(), None); + assert!(val.expect_type::().is_err()); + } + + { + let mut val = ZVal::from(100i64); + assert_eq!(val.as_type::(), Some(100)); + if let Some(l) = val.as_mut_type::<&mut i64>() { + *l += 100; + } + assert_eq!(val.expect_type::().unwrap(), 200); + assert!(val.expect_mut_type::<&mut f64>().is_err()); + } + + { + let mut val = ZVal::from(100f64); + assert_eq!(val.as_type::(), Some(100.)); + if let Some(d) = val.as_mut_type::<&mut f64>() { + *d += 100.; + } + assert_eq!(val.expect_type::().unwrap(), 200.); + assert!(val.expect_mut_type::<&mut i64>().is_err()); + } + + { + let val = ZVal::from("foo"); + assert_eq!( + val.as_type::<&phper::strings::ZStr>().unwrap().to_bytes(), + b"foo" + ); + assert!(val.expect_type::<&phper::arrays::ZArr>().is_err()); + } + + { + let mut arr = ZArray::new(); + arr.insert(InsertKey::NextIndex, ZVal::from("a")); + arr.insert(InsertKey::NextIndex, ZVal::from("b")); + let mut val = ZVal::from(arr); + + { + let zarr = val.expect_type::<&phper::arrays::ZArr>().unwrap(); + let got = zarr.get(1).unwrap().expect_z_str().unwrap().to_bytes(); + assert_eq!(got, b"b"); + } + + { + let zarr = val.expect_mut_type::<&mut phper::arrays::ZArr>().unwrap(); + zarr.insert(InsertKey::NextIndex, ZVal::from("c")); + assert_eq!(zarr.len(), 3); + } + + assert!(val.expect_type::<&phper::objects::ZObj>().is_err()); + } + + { + let mut obj = ZObject::new_by_std_class(); + obj.set_property("foo", ZVal::from("bar")); + let mut val = ZVal::from(obj); + + { + let zobj = val.expect_type::<&phper::objects::ZObj>().unwrap(); + let got = zobj.get_property("foo").expect_z_str().unwrap().to_bytes(); + assert_eq!(got, b"bar"); + } + + { + let zobj = val.expect_mut_type::<&mut phper::objects::ZObj>().unwrap(); + zobj.set_property("foo", ZVal::from("baz")); + } + + let got = val + .expect_type::<&phper::objects::ZObj>() + .unwrap() + .get_property("foo") + .expect_z_str() + .unwrap() + .to_bytes(); + assert_eq!(got, b"baz"); + + assert!(val.expect_type::<&phper::arrays::ZArr>().is_err()); + } + + Ok(()) } diff --git a/tests/integration/tests/php/values.php b/tests/integration/tests/php/values.php index 2588b76..31566c6 100644 --- a/tests/integration/tests/php/values.php +++ b/tests/integration/tests/php/values.php @@ -28,3 +28,4 @@ assert_eq(integration_values_return_result_string_ok(), "foo"); assert_throw("integration_values_return_result_string_err", "ErrorException", 0, "a zhe"); assert_eq(integration_values_return_val(), "foo"); +assert_eq(integration_values_as(), null);