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
6 changes: 0 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
153 changes: 153 additions & 0 deletions phper/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Self::expect(val).ok()
}

/// Converts from `ZVal`, return [`ExpectTypeError`] when type mismatch.
fn expect(val: &'a ZVal) -> crate::Result<Self>;
}

/// 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> {
Self::expect(val).ok()
}

/// Converts from mutable `ZVal`, return [`ExpectTypeError`] when type
/// mismatch.
fn expect(val: &'a mut ZVal) -> crate::Result<Self>;
}

impl ZVal {
/// Wraps a raw pointer.
///
Expand Down Expand Up @@ -308,6 +331,40 @@ impl ZVal {
t.into()
}

/// Converts to target type by [`FromZVal`].
pub fn as_type<'a, T>(&'a self) -> Option<T>
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<T>
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<T>
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<T>
where
T: FromZValMut<'a>,
{
T::expect(self)
}

/// Converts to null if `ZVal` is null.
pub fn as_null(&self) -> Option<()> {
self.expect_null().ok()
Expand Down Expand Up @@ -659,6 +716,102 @@ impl Debug for ZVal {
}
}

impl<'a> FromZVal<'a> for () {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
val.expect_null()
}
}

impl<'a> FromZVal<'a> for bool {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
val.expect_bool()
}
}

impl<'a> FromZVal<'a> for i64 {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
val.expect_long()
}
}

impl<'a> FromZValMut<'a> for &'a mut i64 {
fn expect(val: &'a mut ZVal) -> crate::Result<Self> {
val.expect_mut_long()
}
}

impl<'a> FromZVal<'a> for f64 {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
val.expect_double()
}
}

impl<'a> FromZValMut<'a> for &'a mut f64 {
fn expect(val: &'a mut ZVal) -> crate::Result<Self> {
val.expect_mut_double()
}
}

impl<'a> FromZVal<'a> for &'a ZStr {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
val.expect_z_str()
}
}

impl<'a> FromZValMut<'a> for &'a mut ZStr {
fn expect(val: &'a mut ZVal) -> crate::Result<Self> {
val.expect_mut_z_str()
}
}

impl<'a> FromZVal<'a> for &'a ZArr {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
val.expect_z_arr()
}
}

impl<'a> FromZValMut<'a> for &'a mut ZArr {
fn expect(val: &'a mut ZVal) -> crate::Result<Self> {
val.expect_mut_z_arr()
}
}

impl<'a> FromZVal<'a> for &'a ZObj {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
val.expect_z_obj()
}
}

impl<'a> FromZValMut<'a> for &'a mut ZObj {
fn expect(val: &'a mut ZVal) -> crate::Result<Self> {
val.expect_mut_z_obj()
}
}

impl<'a> FromZVal<'a> for &'a ZRes {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
val.expect_z_res()
}
}

impl<'a> FromZValMut<'a> for &'a mut ZRes {
fn expect(val: &'a mut ZVal) -> crate::Result<Self> {
val.expect_mut_z_res()
}
}

impl<'a> FromZVal<'a> for &'a ZRef {
fn expect(val: &'a ZVal) -> crate::Result<Self> {
val.expect_z_ref()
}
}

impl<'a> FromZValMut<'a> for &'a mut ZRef {
fn expect(val: &'a mut ZVal) -> crate::Result<Self> {
val.expect_mut_z_ref()
}
}

impl Default for ZVal {
#[inline]
fn default() -> Self {
Expand Down
98 changes: 96 additions & 2 deletions tests/integration/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -155,7 +155,7 @@ fn integration_values_return_val(_: &mut [ZVal]) -> Result<ZVal, Infallible> {
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(()));
Expand Down Expand Up @@ -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::<bool>().is_err());
}

{
let val = ZVal::from(true);
assert_eq!(val.as_type::<bool>(), Some(true));
assert_eq!(val.as_type::<i64>(), None);
assert!(val.expect_type::<i64>().is_err());
}

{
let mut val = ZVal::from(100i64);
assert_eq!(val.as_type::<i64>(), Some(100));
if let Some(l) = val.as_mut_type::<&mut i64>() {
*l += 100;
}
assert_eq!(val.expect_type::<i64>().unwrap(), 200);
assert!(val.expect_mut_type::<&mut f64>().is_err());
}

{
let mut val = ZVal::from(100f64);
assert_eq!(val.as_type::<f64>(), Some(100.));
if let Some(d) = val.as_mut_type::<&mut f64>() {
*d += 100.;
}
assert_eq!(val.expect_type::<f64>().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(())
}
1 change: 1 addition & 0 deletions tests/integration/tests/php/values.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Loading