Skip to content

feat(commands): take a backed enum as a command option - #20

Merged
mikield merged 1 commit into
masterfrom
feat/enum-command-options
Sep 2, 2026
Merged

mikield merged 1 commit into
masterfrom
feat/enum-command-options

Conversation

@mikield

@mikield mikield commented Sep 2, 2026

Copy link
Copy Markdown
Member

A fixed set of values had to be written as a string option with a choice list beside it:

#[Option(description: 'Where you play.', choices: ['PC' => 'PC', 'PlayStation' => 'PS4', 'Xbox' => 'X1'])]
string $platform,

Two places to change and one to forget, and the handler still receives a bare string it has to validate itself.

#[Option(description: 'Where you play.')]
Platform $platform,

The framework already resolved backed enums for a component's arguments (ComponentArgumentResolver); a command's options were the one place it did not. Now:

  • a string-backed enum becomes a STRING option, an int-backed one an INTEGER option;
  • every case is offered as a choice, unless choices: says otherwise;
  • the handler is given the case, not the value.

Naming the cases

A case name is a PHP identifier, in English, and rarely what a bot wants shown — PlayStation happens to read well, NorthAmerica does not. An enum may implement Choosable to say how each one reads:

enum Platform: string implements Choosable
{
    case PlayStation = 'PS4';

    public function label(): string { /* ... */ }
}

Without it the case name is used, which is at least a name somebody chose.

A value that is not a case

Discord checks a choice against the list it was given, so this can only come from a client that made one up. It names the option and the value rather than surfacing a ValueError from deep inside from().

Also sets scanDirectories for tests/Fixtures — they are excluded from analysis, but a test naming one still has to resolve to something.

🤖 Generated with Claude Code

A fixed set of values had to be written as a string option with a choice
list beside it — two places to change and one to forget, and the handler
still received a bare string it had to validate itself.

The framework already resolved backed enums for a component's arguments;
a command's options were the one place it did not. Now an enum typed
option becomes a string or integer option depending on its backing, every
case is offered as a choice, and the handler is given the case.

Cases are labelled by their own name, which is a PHP identifier and
rarely what a bot wants shown, so an enum may implement Choosable to say
how each one reads.

A value that is not a case can only come from a client that made one up,
since Discord checks a choice against the list it was given. That names
the option and the value rather than surfacing a ValueError from deep
inside from().
@mikield
mikield merged commit 326704b into master Sep 2, 2026
3 of 4 checks passed
@mikield
mikield deleted the feat/enum-command-options branch September 2, 2026 11:32
github-actions Bot pushed a commit that referenced this pull request Sep 2, 2026
## [0.12.0](v0.11.0...v0.12.0) (2026-09-02)

### Features

* **commands:** take a backed enum as a command option ([#20](#20)) ([326704b](326704b))
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 0.12.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant