Skip to content

Commit d313598

Browse files
committed
feat: Implement EnumCase for direct access to enum cases and add tests
1 parent c36ba6b commit d313598

File tree

5 files changed

+331
-147
lines changed

5 files changed

+331
-147
lines changed

phper-doc/doc/_06_module/_08_register_enum/index.md

Lines changed: 148 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -225,19 +225,20 @@ $colorFromValue = Color::from("FF0000"); // Returns RED
225225
$colorOrNull = Color::tryFrom("INVALID"); // Returns null (when the value doesn't exist)
226226
```
227227

228-
## Accessing Enums from Rust Code
228+
## Using EnumCase for Direct Access
229229

230-
After registering an enum, you might need to access it from Rust code. `PHPER` provides two ways to do this:
230+
When you call `add_case()` on an enum entity, it returns an `EnumCase` instance that you can use for direct access to that case later. This is more efficient than looking up the enum case by name each time you need it.
231231

232-
1. Using the returned `Enum` instance when registering the enum
233-
2. Using `Enum::from_name` to look up an enum by name
234-
235-
### Using the Returned `Enum` Instance
236-
237-
When you register an enum using `module.add_enum()`, it returns an `Enum` instance that you can save and use later.
232+
Here's an example showing how to use EnumCase within static methods of the enum:
238233

239234
```rust,no_run
240-
use phper::{modules::Module, php_get_module, enums::{EnumEntity, Enum}};
235+
use phper::{
236+
modules::Module,
237+
php_get_module,
238+
enums::EnumEntity,
239+
classes::Visibility,
240+
alloc::ToRefOwned,
241+
};
241242
242243
#[php_get_module]
243244
pub fn get_module() -> Module {
@@ -247,96 +248,148 @@ pub fn get_module() -> Module {
247248
env!("CARGO_PKG_AUTHORS"),
248249
);
249250
250-
// Create and register the enum
251-
let mut status_entity = EnumEntity::new("Status");
252-
status_entity.add_case("ACTIVE", ());
253-
status_entity.add_case("INACTIVE", ());
251+
// Create a pure enum
252+
let mut pure_enum_entity = EnumEntity::new("PureEnum");
253+
254+
// Store references to enum cases when adding them
255+
let one_case = pure_enum_entity.add_case("ONE", ());
256+
let _two_case = pure_enum_entity.add_case("TWO", ());
257+
258+
// Add static method that returns the ONE case
259+
pure_enum_entity.add_static_method("getOneCase", Visibility::Public, {
260+
// Clone the EnumCase because it will be moved into the closure
261+
move |_| {
262+
// Get the case object directly from EnumCase
263+
let one_obj = one_case.clone().get_mut_case();
264+
let result = one_obj.to_ref_owned();
265+
phper::ok(result)
266+
}
267+
});
268+
269+
// Register the enum to the module
270+
module.add_enum(pure_enum_entity);
271+
272+
// Create an int-backed enum
273+
let mut int_enum_entity = EnumEntity::<i64>::new("IntEnum");
254274
255-
// Save the returned Enum instance
256-
let status_enum: Enum = module.add_enum(status_entity);
275+
// Store reference to LOW case
276+
let low_case = int_enum_entity.add_case("LOW", 10);
277+
let _high_case = int_enum_entity.add_case("HIGH", 100);
257278
258-
// Use the saved enum instance in a function
259-
module.add_function("get_active_status", move |_| {
260-
// Get the ACTIVE case from the enum
261-
let active_case = status_enum.get_case("ACTIVE")?;
262-
Ok::<_, phper::Error>(active_case)
279+
// Add static method that returns the LOW case
280+
int_enum_entity.add_static_method("getLowCase", Visibility::Public, {
281+
move |_| {
282+
let low_obj = low_case.clone().get_mut_case();
283+
let result = low_obj.to_ref_owned();
284+
phper::ok(result)
285+
}
263286
});
287+
288+
// Register the enum to the module
289+
module.add_enum(int_enum_entity);
264290
265291
module
266292
}
267293
```
268294

269-
### Using `Enum::from_name`
295+
This creates PHP enums with static methods that can access specific cases:
270296

271-
If you don't have the original `Enum` instance, you can use `Enum::from_name` to look up an enum by its name.
297+
```php
298+
enum PureEnum {
299+
case ONE;
300+
case TWO;
301+
302+
public static function getOneCase(): self {
303+
return self::ONE;
304+
}
305+
}
306+
307+
enum IntEnum: int {
308+
case LOW = 10;
309+
case HIGH = 100;
310+
311+
public static function getLowCase(): self {
312+
return self::LOW;
313+
}
314+
}
315+
```
316+
317+
## Using Enum::from_name
318+
319+
If you don't have direct access to the EnumCase, you can use `Enum::from_name()` to get an enum by its name, and then use `get_case()` or `get_mut_case()` to access specific cases:
272320

273321
```rust,no_run
274-
use phper::{enums::Enum, modules::Module, php_get_module};
322+
use phper::{
323+
enums::Enum,
324+
enums::EnumEntity,
325+
classes::Visibility,
326+
alloc::ToRefOwned,
327+
};
275328
276-
#[php_get_module]
277-
pub fn get_module() -> Module {
278-
let mut module = Module::new(
279-
env!("CARGO_CRATE_NAME"),
280-
env!("CARGO_PKG_VERSION"),
281-
env!("CARGO_PKG_AUTHORS"),
282-
);
329+
fn create_enum_with_dynamic_lookup() -> EnumEntity {
330+
let mut enum_entity = EnumEntity::new("DynamicEnum");
331+
enum_entity.add_case("ONE", ());
332+
enum_entity.add_case("TWO", ());
283333
284-
// Register functions that use enums
285-
module.add_function("get_status_case", |args| {
286-
// Look up the Status enum by name
287-
let status_enum = Enum::from_name("Status");
334+
// Add a static method that looks up cases dynamically
335+
enum_entity.add_static_method("getCaseByName", Visibility::Public, |args| {
336+
// Get case name from parameters
337+
let case_name = args[0].expect_z_str()?.to_string_lossy();
288338
289-
// Get the case name from the function arguments
290-
let case_name = args[0].as_z_str()?.to_str()?;
339+
// Use Enum::from_name to get the enum
340+
let mut enum_obj = Enum::from_name("DynamicEnum");
291341
292-
// Get the requested enum case
293-
let case = status_enum.get_case(case_name)?;
342+
// Try to get the requested case
343+
let case = unsafe { enum_obj.get_mut_case(&case_name)? };
344+
let result = case.to_ref_owned();
294345
295-
Ok::<_, phper::Error>(case)
346+
phper::ok(result)
296347
});
297-
298-
module
348+
349+
enum_entity
299350
}
300351
```
301352

302-
## Getting Enum Cases
353+
> **Important**: The `get_case()` and `get_mut_case()` methods on `Enum` are marked as unsafe because they can cause segmentation faults if the case doesn't exist.
303354
304-
Once you have an `Enum` instance, you can use the `get_case` method to access specific enum cases:
355+
## Bound Enum
305356

306-
```rust,no_run
307-
// Get a case from a pure enum
308-
let status_enum = Enum::from_name("Status");
309-
let active_case = status_enum.get_case("ACTIVE")?;
310-
311-
// Get a case from a backed enum
312-
let level_enum = Enum::from_name("Level");
313-
let high_level = level_enum.get_case("HIGH")?;
314-
```
315-
316-
If you need to modify an enum case's properties, you can use `get_case_mut` to get a mutable reference:
357+
You can use the `bound_enum()` method to get a reference to the enum that can be used in methods or functions:
317358

318359
```rust,no_run
319-
// Get a mutable reference to an enum case
320-
let mut status_enum = Enum::from_name("Status");
321-
let mut active_case = status_enum.get_case_mut("ACTIVE")?;
360+
use phper::{enums::EnumEntity, classes::Visibility, alloc::ToRefOwned};
322361
323-
// Now you can modify the case object if needed
362+
pub fn make_status_enum() -> EnumEntity {
363+
let mut enum_entity = EnumEntity::new("Status");
364+
enum_entity.add_case("Active", ());
365+
enum_entity.add_case("Inactive", ());
366+
367+
// Get a reference to the enum that will be created
368+
let status_enum = enum_entity.bound_enum();
369+
370+
// Add a static method that uses the bound enum
371+
enum_entity.add_static_method("getActiveCase", Visibility::Public, move |_| {
372+
// Use the bound enum to get the Active case
373+
let active_case = unsafe { status_enum.clone().get_mut_case("Active")? };
374+
phper::ok(active_case.to_ref_owned())
375+
});
376+
377+
enum_entity
378+
}
324379
```
325380

326-
If the specified case doesn't exist in the enum, both `get_case` and `get_case_mut` will return an `EnumCaseNotFoundError`.
327-
328381
## Complete Example
329382

330-
Here's a comprehensive example using both pure and backed enums:
383+
Here's a comprehensive example using both pure and backed enums with static methods:
331384

332385
```rust,no_run
333386
use phper::{
334387
modules::Module,
335388
php_get_module,
336389
enums::EnumEntity,
337-
classes::Visibility
390+
classes::Visibility,
391+
alloc::ToRefOwned,
338392
};
339-
use std::convert::Infallible;
340393
341394
#[php_get_module]
342395
pub fn get_module() -> Module {
@@ -348,19 +401,44 @@ pub fn get_module() -> Module {
348401
349402
// Pure enum
350403
let mut status = EnumEntity::new("Status");
351-
status.add_case("PENDING", ());
352-
status.add_case("ACTIVE", ());
353-
status.add_case("INACTIVE", ());
404+
let pending_case = status.add_case("PENDING", ());
405+
let active_case = status.add_case("ACTIVE", ());
406+
let inactive_case = status.add_case("INACTIVE", ());
407+
408+
// Add constants
354409
status.add_constant("VERSION", "1.0.0");
410+
411+
// Add static method that returns the active state
412+
status.add_static_method("getActiveStatus", Visibility::Public, {
413+
move |_| {
414+
let obj = active_case.clone().get_mut_case();
415+
phper::ok(obj.to_ref_owned())
416+
}
417+
});
418+
419+
// Add static method that returns status description
355420
status.add_static_method("getDescription", Visibility::Public, |_| {
356-
Ok::<_, Infallible>("Status enumeration")
421+
phper::ok("Status enumeration")
357422
});
358423
359424
// Integer-backed enum
360425
let mut level = EnumEntity::<i64>::new("Level");
361-
level.add_case("LOW", 1);
362-
level.add_case("MEDIUM", 5);
363-
level.add_case("HIGH", 10);
426+
let low_case = level.add_case("LOW", 1);
427+
let medium_case = level.add_case("MEDIUM", 5);
428+
let high_case = level.add_case("HIGH", 10);
429+
430+
// Add static method that returns level by value
431+
level.add_static_method("getLevelByValue", Visibility::Public, {
432+
move |args| {
433+
let value: i64 = args[0].expect_long()?;
434+
let case_obj = match value {
435+
v if v < 3 => low_case.clone().get_mut_case(),
436+
v if v < 8 => medium_case.clone().get_mut_case(),
437+
_ => high_case.clone().get_mut_case(),
438+
};
439+
phper::ok(case_obj.to_ref_owned())
440+
}
441+
});
364442
365443
// Register enums to the module
366444
module.add_enum(status);

0 commit comments

Comments
 (0)