1 | <?php declare(strict_types=1); |
||
2 | |||
3 | /* |
||
4 | * This file is part of Biurad opensource projects. |
||
5 | * |
||
6 | * @copyright 2022 Biurad Group (https://biurad.com/) |
||
7 | * @license https://opensource.org/licenses/BSD-3-Clause License |
||
8 | * |
||
9 | * For the full copyright and license information, please view the LICENSE |
||
10 | * file that was distributed with this source code. |
||
11 | */ |
||
12 | |||
13 | namespace Biurad\Monorepo; |
||
14 | |||
15 | use Symfony\Component\OptionsResolver\Exception\{AccessException, InvalidOptionsException}; |
||
16 | use Symfony\Component\OptionsResolver\{Options, OptionsResolver}; |
||
17 | use Symfony\Component\Yaml\Yaml; |
||
18 | |||
19 | /** |
||
20 | * Monorepo configuration (.monorepo). |
||
21 | * |
||
22 | * @author Divine Niiquaye Ibok <[email protected]> |
||
23 | */ |
||
24 | final class Config implements \ArrayAccess, \Countable |
||
25 | { |
||
26 | /** @var array<string,mixed> */ |
||
27 | private array $config; |
||
28 | |||
29 | public function __construct(string $rootPath, string $cachePath = null, bool $reclone) |
||
30 | { |
||
31 | if (!\file_exists($configFile = ($this->config['path'] = $rootPath).'/.monorepo')) { |
||
32 | throw new \RuntimeException(\sprintf('Config file "%s" does not exist.', $configFile)); |
||
33 | } |
||
34 | |||
35 | $workers = []; |
||
36 | $this->config['reclone'] = $reclone; |
||
37 | $options = new OptionsResolver(); |
||
38 | $options |
||
39 | ->define('base_url')->default(null)->allowedTypes('string', 'null') |
||
40 | ->define('branch_filter')->default(null)->allowedTypes('string', 'null') |
||
41 | ->define('extra')->default([])->allowedTypes('array') |
||
42 | ->define('workers')->default([])->allowedTypes('array') |
||
43 | ->normalize(function (Options $options, array $value): array { |
||
44 | if (\array_is_list($value)) { |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
45 | return ['main' => $value]; |
||
46 | } |
||
47 | |||
48 | foreach ($value as $k => $v) { |
||
49 | $v = \is_string($v) ? [$v] : $v; |
||
50 | |||
51 | if (!\is_string($k)) { |
||
52 | throw new InvalidOptionsException('Expected workers config to begin with a string key.'); |
||
53 | } |
||
54 | |||
55 | if (!\is_array($v) || !\array_is_list($v)) { |
||
56 | throw new InvalidOptionsException('Expected workers config\'s key value be a list of workers classes.'); |
||
57 | } |
||
58 | } |
||
59 | |||
60 | return $value; |
||
61 | }) |
||
62 | ->define('repositories') |
||
63 | ->default(function (OptionsResolver $options, Options $parent) use (&$workers): void { |
||
64 | $workers = \array_keys($parent['workers']); |
||
65 | $options->setPrototype(true) |
||
66 | ->define('url')->default(null)->allowedTypes('string', 'null')->required() |
||
67 | ->define('merge')->default(false)->allowedTypes('bool') |
||
68 | ->define('path') |
||
69 | ->allowedTypes('string') |
||
70 | ->default(\Closure::bind(fn (Options $options): string => $options->prototypeIndex, null, $options)) |
||
0 ignored issues
–
show
|
|||
71 | ->normalize(fn (Options $options, string $value): string => \trim($value, '/')) |
||
72 | ->define('workers')->default($workers)->allowedTypes('array') |
||
73 | ->normalize(function (Options $options, array $value) use ($workers): array { |
||
74 | foreach ($value as $worker) { |
||
75 | if (!\in_array($worker, $workers, true)) { |
||
76 | throw new InvalidOptionsException(\sprintf('The worker "%s" for monorepo\'s path "%s" does not exist.', $worker, $options['path'])); |
||
77 | } |
||
78 | } |
||
79 | |||
80 | return $value; |
||
81 | }) |
||
82 | ; |
||
83 | })->allowedTypes('array') |
||
84 | ; |
||
85 | |||
86 | $options->setRequired(['base_url', 'branch_filter', 'workers', 'repositories']); |
||
87 | $this->config += $options->resolve(\function_exists('yaml_parse_file') ? yaml_parse_file($configFile) : Yaml::parseFile($configFile)); |
||
88 | $this->config['cache'] = $cachePath ?? $this->config['path'].'/.monorepo-cache'; |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * {@inheritdoc} |
||
93 | */ |
||
94 | public function offsetGet(mixed $offset): mixed |
||
95 | { |
||
96 | if (!\is_string($offset)) { |
||
97 | throw new InvalidOptionsException(\sprintf('Expected a string key, got a type of "%s" instead.', \get_debug_type($offset))); |
||
98 | } |
||
99 | |||
100 | return $this->config[$offset] ?? throw new InvalidOptionsException(\sprintf('Value for "%s" does not exist.', $offset)); |
||
101 | } |
||
102 | |||
103 | /** |
||
104 | * {@inheritdoc} |
||
105 | */ |
||
106 | public function offsetExists(mixed $offset): bool |
||
107 | { |
||
108 | if (!\is_string($offset)) { |
||
109 | throw new InvalidOptionsException(\sprintf('Expected a string key, got a type of "%s" instead.', \get_debug_type($offset))); |
||
110 | } |
||
111 | |||
112 | return \array_key_exists($offset, $this->config); |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * {@inheritdoc} |
||
117 | */ |
||
118 | public function offsetSet(mixed $offset, mixed $value): void |
||
119 | { |
||
120 | throw new AccessException('Unexpected call for setting config, kindly define config in the .monorepo file.'); |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * {@inheritdoc} |
||
125 | */ |
||
126 | public function offsetUnset(mixed $offset): void |
||
127 | { |
||
128 | if (!\is_string($offset)) { |
||
129 | throw new InvalidOptionsException(\sprintf('Expected a string key, got a type of "%s" instead.', \get_debug_type($offset))); |
||
130 | } |
||
131 | |||
132 | unset($this->config[$offset]); |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * {@inheritdoc} |
||
137 | */ |
||
138 | public function count(): int |
||
139 | { |
||
140 | return \count($this->config); |
||
141 | } |
||
142 | } |
||
143 |