make(BasicPrimitives::class))->toBe([ 'text' => ['required'], 'number' => ['required', 'numeric'], 'flag' => ['required', 'boolean'], 'items' => ['nullable', 'array'], 'floating' => ['sometimes', 'numeric'], ]); }); readonly class AnnotatedArray { /** * @param array $items */ public function __construct(public array $items) {} } test('annotated array', function () { expect(RuleFactory::instance()->make(AnnotatedArray::class))->toBe([ 'items' => ['required', 'array'], 'items.*' => ['required', 'numeric'], ]); }); readonly class AnnotatedArrayNullableValue { /** * @param array $items */ public function __construct(public array $items) {} } test('annotated array with nullable items', function () { expect(RuleFactory::instance()->make(AnnotatedArrayNullableValue::class))->toBe([ 'items' => ['required', 'array'], 'items.*' => ['nullable', 'numeric'], ]); }); readonly class PlainLeaf { public function __construct(public string $name) {} } readonly class PlainRoot { public function __construct(public int $value, public PlainLeaf $leaf) {} } test('plain nesting', function () { expect(RuleFactory::instance()->make(PlainRoot::class))->toBe([ 'value' => ['required', 'numeric'], 'leaf' => ['required'], 'leaf.name' => ['required'], ]); }); readonly class AnnotatedArrayItem { public function __construct(public int $value) {} } readonly class AnnotatedArrayObject { /** * @param ?array $items */ public function __construct(public ?array $items) {} } test('annotated array with object', function () { expect(RuleFactory::instance()->make(AnnotatedArrayObject::class))->toBe([ 'items' => ['nullable', 'array'], 'items.*' => ['required'], 'items.*.value' => ['required', 'numeric'], ]); }); readonly class FlattenedLeaf { public function __construct(public ?bool $flag) {} } readonly class NotFlattenedLeaf { public function __construct(public string $description) {} } readonly class FlattenedNode { public function __construct( public string $id, public NotFlattenedLeaf $leaf, #[Flat] public FlattenedLeaf $squish, public int $level = 1, ) {} } readonly class FlattenedRoot { public function __construct( public int $value, #[Flat] public FlattenedNode $node, ) {} } test('flattened basic', function () { expect(RuleFactory::instance()->make(FlattenedRoot::class))->toBe([ 'value' => ['required', 'numeric'], 'id' => ['required' ], 'leaf' => ['required'], 'leaf.description' => ['required'], 'flag' => ['nullable', 'boolean'], 'level' => ['sometimes', 'numeric'], ]); }); readonly class AnnotatedCollectionItem { public function __construct(public int $value) {} } readonly class AnnotatedCollection { /** * @param Collection $group */ public function __construct(public Collection $group) {} } test('annotated collection', function () { expect(RuleFactory::instance()->make(AnnotatedCollection::class))->toBe([ 'group' => ['required', 'array'], 'group.*' => ['required'], 'group.*.value' => ['required', 'numeric'], ]); });