packages/sql-faker/src/Generation/Value/ValueChoices.php
1<?php
2
3declare(strict_types=1);
4
5namespace SqlFaker\Generation\Value;
6
7use Closure;
8use InvalidArgumentException;
9
10/**
11 * Per-plan decisions, memoized so repeated candidate inspection cannot consume more randomness.
12 * The caller interprets bytes; domains only receive bounded index decisions.
13 */
14final class ValueChoices
15{
16 /**
17 * @var array<string, string|null>
18 */
19 private array $values = [];
20
21 /**
22 * @param Closure(positive-int): ?int $choice Null selects representatives after decision input ends
23 */
24 public function __construct(private readonly Closure $choice)
25 {
26 }
27
28 /**
29 * Samples once per occurrence and domain; null retains the declared representatives.
30 */
31 public function value(int $index, string $definition, ?ValueDomain $domain): ?string
32 {
33 if ($domain === null) {
34 return null;
35 }
36 $key = $index . ':' . $definition;
37 if (!array_key_exists($key, $this->values)) {
38 $this->values[$key] = $this->index(2) === 0 ? null : $domain->choose($this->index(...));
39 }
40 return $this->values[$key];
41 }
42
43 /**
44 * @param positive-int $count
45 * @throws InvalidArgumentException When a caller supplies an out-of-range decision
46 */
47 public function index(int $count): int
48 {
49 $index = ($this->choice)($count) ?? 0;
50 if ($index < 0 || $index >= $count) {
51 throw new InvalidArgumentException('Value selector returned an out-of-range index.');
52 }
53 return $index;
54 }
55}
56