Lines 100.00% 18 / 18
Methods 100.00% 6 / 6
Classes 100.00% 1 / 1
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 defaultMessage 100.00% 1 / 1 100.00% 1 / 1 1
 stringify 100.00% 7 / 7 100.00% 1 / 1 10
 validate 100.00% 1 / 1 100.00% 1 / 1 1
 [Lucent\Validation\Constraint] withMessage 100.00% 2 / 2 100.00% 1 / 1 1
 [Lucent\Validation\Constraint] message 100.00% 6 / 6 100.00% 1 / 1 3
22final class NotIn extends Constraint
23{
24    /**
25     * Create a "not in" constraint.
26     *
27     * @param array<int, mixed> $values The forbidden values.
28     */
29    public function __construct(private readonly array $values) {}
30
31    /**
32     * @return string|Closure(FieldContext): string The default error message.
33     */
34    #[Override]
35    protected function defaultMessage(): string|Closure|null
36    {
37        return fn(FieldContext $ctx) => "The {$ctx->field} must not be one of: " . implode(', ', array_map($this->stringify(...), $this->values)) . '.';
38    }
39
40    /**
41     * Safely render a forbidden value for the error message.
42     *
43     * Handles any value type without relying on `strval`, which throws on
44     * objects without `__toString` and warns on arrays.
45     *
46     * @param mixed $value The value to render.
47     * @return string A human-readable representation.
48     */
49    private function stringify(mixed $value): string
50    {
51        return match (true) {
52            is_string($value)  => $value,
53            is_int($value), is_float($value) => (string) $value,
54            is_bool($value)    => $value ? 'true' : 'false',
55            $value === null    => 'null',
56            is_array($value)   => 'array',
57            is_object($value)  => method_exists($value, '__toString') ? (string) $value : $value::class,
58            default            => 'unknown',
59        };
60    }
61
62    /**
63     * Validate that the value is not in the forbidden set.
64     *
65     * Comparison is strict (`===`), so `1` and `'1'` are treated as distinct.
66     *
67     * @param FieldContext $ctx The context of the field being validated.
68     * @return bool True if the value is not forbidden, false otherwise.
69     */
70    #[Override]
71    public function validate(FieldContext $ctx): bool
72    {
73        return !in_array($ctx->value, $this->values, true);
74    }
75}

Inherited from Lucent\Validation\Constraint

38    final public function withMessage(string|\Closure $message): static
39    {
40        $this->customMessage = $message;
41        return $this;
42    }
56    final public function message(FieldContext $ctx): ?string
57    {
58        $message = $this->customMessage ?? $this->defaultMessage();
59
60        if ($message === null) {
61            return null;
62        }
63
64        if ($message instanceof \Closure) {
65            return call_user_func($message, $ctx);
66        }
67
68        return $message;
69    }