Skip to content

feat: Enhance enum functionality with access methods and error handling #203

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 7, 2025
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
198 changes: 188 additions & 10 deletions phper-doc/doc/_06_module/_08_register_enum/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,18 +225,171 @@ $colorFromValue = Color::from("FF0000"); // Returns RED
$colorOrNull = Color::tryFrom("INVALID"); // Returns null (when the value doesn't exist)
```

## Using EnumCase for Direct Access

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.

Here's an example showing how to use EnumCase within static methods of the enum:

```rust,no_run
use phper::{
modules::Module,
php_get_module,
enums::EnumEntity,
classes::Visibility,
alloc::ToRefOwned,
};

#[php_get_module]
pub fn get_module() -> Module {
let mut module = Module::new(
env!("CARGO_CRATE_NAME"),
env!("CARGO_PKG_VERSION"),
env!("CARGO_PKG_AUTHORS"),
);

// Create a pure enum
let mut pure_enum_entity = EnumEntity::new("PureEnum");

// Store references to enum cases when adding them
let one_case = pure_enum_entity.add_case("ONE", ());
let _two_case = pure_enum_entity.add_case("TWO", ());

// Add static method that returns the ONE case
pure_enum_entity.add_static_method("getOneCase", Visibility::Public, {
// Clone the EnumCase because it will be moved into the closure
move |_| {
// Get the case object directly from EnumCase
let one_obj = one_case.clone().get_mut_case();
let result = one_obj.to_ref_owned();
phper::ok(result)
}
});

// Register the enum to the module
module.add_enum(pure_enum_entity);

// Create an int-backed enum
let mut int_enum_entity = EnumEntity::<i64>::new("IntEnum");

// Store reference to LOW case
let low_case = int_enum_entity.add_case("LOW", 10);
let _high_case = int_enum_entity.add_case("HIGH", 100);

// Add static method that returns the LOW case
int_enum_entity.add_static_method("getLowCase", Visibility::Public, {
move |_| {
let low_obj = low_case.clone().get_mut_case();
let result = low_obj.to_ref_owned();
phper::ok(result)
}
});

// Register the enum to the module
module.add_enum(int_enum_entity);

module
}
```

This creates PHP enums with static methods that can access specific cases:

```php
enum PureEnum {
case ONE;
case TWO;

public static function getOneCase(): self {
return self::ONE;
}
}

enum IntEnum: int {
case LOW = 10;
case HIGH = 100;

public static function getLowCase(): self {
return self::LOW;
}
}
```

## Using Enum::from_name

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:

```rust,no_run
use phper::{
enums::Enum,
enums::EnumEntity,
classes::Visibility,
alloc::ToRefOwned,
};

fn create_enum_with_dynamic_lookup() -> EnumEntity {
let mut enum_entity = EnumEntity::new("DynamicEnum");
enum_entity.add_case("ONE", ());
enum_entity.add_case("TWO", ());

// Add a static method that looks up cases dynamically
enum_entity.add_static_method("getCaseByName", Visibility::Public, |args| {
// Get case name from parameters
let case_name = args[0].expect_z_str()?.to_string_lossy();

// Use Enum::from_name to get the enum
let mut enum_obj = Enum::from_name("DynamicEnum");

// Try to get the requested case
let case = unsafe { enum_obj.get_mut_case(&case_name)? };
let result = case.to_ref_owned();

phper::ok(result)
});

enum_entity
}
```

> **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.

## Bound Enum

You can use the `bound_enum()` method to get a reference to the enum that can be used in methods or functions:

```rust,no_run
use phper::{enums::EnumEntity, classes::Visibility, alloc::ToRefOwned};

pub fn make_status_enum() -> EnumEntity {
let mut enum_entity = EnumEntity::new("Status");
enum_entity.add_case("Active", ());
enum_entity.add_case("Inactive", ());

// Get a reference to the enum that will be created
let status_enum = enum_entity.bound_enum();

// Add a static method that uses the bound enum
enum_entity.add_static_method("getActiveCase", Visibility::Public, move |_| {
// Use the bound enum to get the Active case
let active_case = unsafe { status_enum.clone().get_mut_case("Active")? };
phper::ok(active_case.to_ref_owned())
});

enum_entity
}
```

## Complete Example

Here's a comprehensive example using both pure and backed enums:
Here's a comprehensive example using both pure and backed enums with static methods:

```rust,no_run
use phper::{
modules::Module,
php_get_module,
enums::EnumEntity,
classes::Visibility
classes::Visibility,
alloc::ToRefOwned,
};
use std::convert::Infallible;

#[php_get_module]
pub fn get_module() -> Module {
Expand All @@ -248,19 +401,44 @@ pub fn get_module() -> Module {

// Pure enum
let mut status = EnumEntity::new("Status");
status.add_case("PENDING", ());
status.add_case("ACTIVE", ());
status.add_case("INACTIVE", ());
let pending_case = status.add_case("PENDING", ());
let active_case = status.add_case("ACTIVE", ());
let inactive_case = status.add_case("INACTIVE", ());

// Add constants
status.add_constant("VERSION", "1.0.0");

// Add static method that returns the active state
status.add_static_method("getActiveStatus", Visibility::Public, {
move |_| {
let obj = active_case.clone().get_mut_case();
phper::ok(obj.to_ref_owned())
}
});

// Add static method that returns status description
status.add_static_method("getDescription", Visibility::Public, |_| {
Ok::<_, Infallible>("Status enumeration")
phper::ok("Status enumeration")
});

// Integer-backed enum
let mut level = EnumEntity::<i64>::new("Level");
level.add_case("LOW", 1);
level.add_case("MEDIUM", 5);
level.add_case("HIGH", 10);
let low_case = level.add_case("LOW", 1);
let medium_case = level.add_case("MEDIUM", 5);
let high_case = level.add_case("HIGH", 10);

// Add static method that returns level by value
level.add_static_method("getLevelByValue", Visibility::Public, {
move |args| {
let value: i64 = args[0].expect_long()?;
let case_obj = match value {
v if v < 3 => low_case.clone().get_mut_case(),
v if v < 8 => medium_case.clone().get_mut_case(),
_ => high_case.clone().get_mut_case(),
};
phper::ok(case_obj.to_ref_owned())
}
});

// Register enums to the module
module.add_enum(status);
Expand Down
Loading
Loading