• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

davidcole1340 / ext-php-rs / 16401140698

20 Jul 2025 02:13PM UTC coverage: 26.945% (+1.2%) from 25.766%
16401140698

Pull #489

github

Xenira
refactor(enum): use raii box pattern for enum values

Refs: #178
Pull Request #489: feat(enum): add basic enum support

118 of 288 new or added lines in 7 files covered. (40.97%)

1 existing line in 1 file now uncovered.

1115 of 4138 relevant lines covered (26.95%)

5.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

31.03
/src/enum_.rs
1
//! This module defines the `PhpEnum` trait and related types for Rust enums that are exported to PHP.
2
use std::ptr;
3

4
use crate::{
5
    boxed::ZBox,
6
    class::RegisteredClass,
7
    convert::{FromZendObject, FromZval, IntoZendObject, IntoZval},
8
    describe::DocComments,
9
    error::{Error, Result},
10
    ffi::zend_enum_get_case,
11
    flags::{ClassFlags, DataType},
12
    types::{ZendObject, ZendStr, Zval},
13
};
14

15
/// Implemented on Rust enums which are exported to PHP.
16
pub trait RegisteredEnum {
17
    /// The cases of the enum.
18
    const CASES: &'static [EnumCase];
19

20
    /// # Errors
21
    ///
22
    /// - [`Error::InvalidProperty`] if the enum does not have a case with the given name, an error is returned.
23
    fn from_name(name: &str) -> Result<Self>
24
    where
25
        Self: Sized;
26

27
    /// Returns the variant name of the enum as it is registered in PHP.
28
    fn to_name(&self) -> &'static str;
29
}
30

31
impl<T> FromZendObject<'_> for T
32
where
33
    T: RegisteredEnum,
34
{
NEW
35
    fn from_zend_object(obj: &ZendObject) -> Result<Self> {
×
NEW
36
        if !ClassFlags::from_bits_truncate(unsafe { (*obj.ce).ce_flags }).contains(ClassFlags::Enum)
×
37
        {
NEW
38
            return Err(Error::InvalidProperty);
×
39
        }
40

NEW
41
        let name = obj
×
42
            .get_properties()?
43
            .get("name")
NEW
44
            .and_then(Zval::indirect)
×
NEW
45
            .and_then(Zval::str)
×
NEW
46
            .ok_or(Error::InvalidProperty)?;
×
47

NEW
48
        T::from_name(name)
×
49
    }
50
}
51

52
impl<T> FromZval<'_> for T
53
where
54
    T: RegisteredEnum + RegisteredClass,
55
{
56
    const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME));
57

NEW
58
    fn from_zval(zval: &Zval) -> Option<Self> {
×
NEW
59
        zval.object()
×
NEW
60
            .and_then(|obj| Self::from_zend_object(obj).ok())
×
61
    }
62
}
63

64
impl<T> IntoZendObject for T
65
where
66
    T: RegisteredEnum + RegisteredClass,
67
{
NEW
68
    fn into_zend_object(self) -> Result<ZBox<ZendObject>> {
×
NEW
69
        let mut name = ZendStr::new(T::to_name(&self), false);
×
70
        let variant = unsafe {
71
            zend_enum_get_case(
NEW
72
                ptr::from_ref(T::get_metadata().ce()).cast_mut(),
×
NEW
73
                &raw mut *name,
×
74
            )
75
        };
76

NEW
77
        Ok(unsafe { ZBox::from_raw(variant) })
×
78
    }
79
}
80

81
impl<T> IntoZval for T
82
where
83
    T: RegisteredEnum + RegisteredClass,
84
{
85
    const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME));
86
    const NULLABLE: bool = false;
87

NEW
88
    fn set_zval(self, zv: &mut Zval, _persistent: bool) -> Result<()> {
×
NEW
89
        let obj = self.into_zend_object()?;
×
NEW
90
        zv.set_object(obj.into_raw());
×
NEW
91
        Ok(())
×
92
    }
93
}
94
// impl<'a, T> IntoZval for T
95
// where
96
//     T: RegisteredEnum + RegisteredClass + IntoZendObject
97
// {
98
//     const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME));
99
//     const NULLABLE: bool = false;
100
//
101
//     fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> {
102
//         let obj = self.into_zend_object()?;
103
//     }
104
// }
105

106
/// Represents a case in a PHP enum.
107
pub struct EnumCase {
108
    /// The identifier of the enum case, e.g. `Bar` in `enum Foo { Bar }`.
109
    pub name: &'static str,
110
    /// The value of the enum case, which can be an integer or a string.
111
    pub discriminant: Option<Discriminant>,
112
    /// The documentation comments for the enum case.
113
    pub docs: DocComments,
114
}
115

116
impl EnumCase {
117
    /// Gets the PHP data type of the enum case's discriminant.
118
    #[must_use]
119
    pub fn data_type(&self) -> DataType {
5✔
120
        match self.discriminant {
3✔
121
            Some(Discriminant::Int(_)) => DataType::Long,
2✔
122
            Some(Discriminant::String(_)) => DataType::String,
1✔
123
            None => DataType::Undef,
2✔
124
        }
125
    }
126
}
127

128
/// Represents the discriminant of an enum case in PHP, which can be either an integer or a string.
129
#[derive(Debug, PartialEq, Eq)]
130
pub enum Discriminant {
131
    /// An integer discriminant.
132
    Int(i64),
133
    /// A string discriminant.
134
    String(&'static str),
135
}
136

137
impl TryFrom<&Discriminant> for Zval {
138
    type Error = Error;
139

140
    fn try_from(value: &Discriminant) -> Result<Self> {
2✔
141
        match value {
2✔
142
            Discriminant::Int(i) => i.into_zval(false),
1✔
143
            Discriminant::String(s) => s.into_zval(false),
3✔
144
        }
145
    }
146
}
147

148
#[cfg(test)]
149
#[cfg(feature = "embed")]
150
mod tests {
151
    #![allow(clippy::unwrap_used)]
152
    use super::*;
153
    use crate::embed::Embed;
154

155
    #[test]
156
    fn test_zval_try_from_discriminant() {
157
        Embed::run(|| {
158
            let zval_int: Zval = Zval::try_from(&Discriminant::Int(42)).unwrap();
159
            assert!(zval_int.is_long());
160
            assert_eq!(zval_int.long().unwrap(), 42);
161

162
            let zval_str: Zval = Zval::try_from(&Discriminant::String("foo")).unwrap();
163
            assert!(zval_str.is_string());
164
            assert_eq!(zval_str.string().unwrap().to_string(), "foo");
165
        });
166
    }
167
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc