77use Attribute ;
88use Tempest \Validation \Rule ;
99use UnexpectedValueException ;
10+ use UnitEnum ;
1011
1112#[Attribute]
1213final readonly class Enum implements Rule
1314{
14- public function __construct (private string $ enum )
15+ public function __construct (private string $ enum, private array $ only = [], private array $ except = [] )
1516 {
1617 if (! enum_exists ($ this ->enum )) {
1718 throw new UnexpectedValueException (sprintf (
@@ -23,15 +24,65 @@ public function __construct(private string $enum)
2324
2425 public function isValid (mixed $ value ): bool
2526 {
26- if (method_exists ( $ this ->enum , ' tryFrom ' ) ) {
27- return $ this ->enum :: tryFrom ($ value ) !== null ;
27+ if ($ value instanceof $ this ->enum ) {
28+ return $ this ->isDesirable ($ value );
2829 }
2930
30- return defined ("$ this ->enum :: {$ value }" );
31+ return ($ enumValue = $ this ->retrieveEnumValue ($ value )) !== null && $ this ->isDesirable ($ enumValue );
32+ }
33+
34+ /**
35+ * Specify the cases that should be considered valid.
36+ *
37+ * @param UnitEnum|array<UnitEnum> $values
38+ */
39+ public function only (UnitEnum |array $ values ): self
40+ {
41+ return new self (
42+ enum: $ this ->enum ,
43+ only: [
44+ ...$ this ->only ,
45+ ...(is_array ($ values ) ? $ values : func_get_args ()),
46+ ],
47+ );
48+ }
49+
50+ /**
51+ * Specify the cases that should be considered invalid.
52+ *
53+ * @param UnitEnum|array<UnitEnum> $values
54+ */
55+ public function except (UnitEnum |array $ values ): self
56+ {
57+ return new self (
58+ enum: $ this ->enum ,
59+ except: [
60+ ...$ this ->except ,
61+ ...(is_array ($ values ) ? $ values : func_get_args ()),
62+ ],
63+ );
3164 }
3265
3366 public function message (): string
3467 {
3568 return "The value must be a valid enumeration [ $ this ->enum ] case " ;
3669 }
70+
71+ private function isDesirable ($ value ): bool
72+ {
73+ return match (true ) {
74+ ! empty ($ this ->only ) => in_array (needle: $ value , haystack: $ this ->only , strict: true ),
75+ ! empty ($ this ->except ) => ! in_array (needle: $ value , haystack: $ this ->except , strict: true ),
76+ default => true ,
77+ };
78+ }
79+
80+ private function retrieveEnumValue (mixed $ value )
81+ {
82+ if (method_exists ($ this ->enum , 'tryFrom ' )) {
83+ return $ this ->enum ::tryFrom ($ value );
84+ }
85+
86+ return defined ("$ this ->enum :: {$ value }" ) ? $ this ->enum ::{$ value } : null ;
87+ }
3788}
0 commit comments