Skip to content

Commit 60a840a

Browse files
author
ityaozm@gmail.com
committed
refactor(ConfigManager): Simplify readFrom method for better clarity
- Replace the old readFrom method with a clearer version that better handles PHP and JSON file configurations. - Ensure that JSON files are validated before decoding to prevent potential errors. - Introduce exceptions for unsupported file types, enhancing error-handling capabilities.
1 parent 7019890 commit 60a840a

7 files changed

Lines changed: 123 additions & 140 deletions

File tree

app/ConfigManager.php

Lines changed: 109 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,22 @@
1313

1414
namespace App;
1515

16-
use App\Exceptions\InvalidJsonFileException;
1716
use App\Exceptions\UnsupportedConfigFileTypeException;
1817
use Illuminate\Config\Repository;
1918
use Illuminate\Contracts\Support\Arrayable;
2019
use Illuminate\Contracts\Support\Jsonable;
2120
use Illuminate\Support\Arr;
2221
use Illuminate\Support\Collection;
22+
use Illuminate\Support\Facades\Config;
2323
use Illuminate\Support\Facades\File;
2424
use Illuminate\Support\Traits\Conditionable;
2525
use Illuminate\Support\Traits\Dumpable;
2626
use Illuminate\Support\Traits\ForwardsCalls;
2727
use Illuminate\Support\Traits\Localizable;
2828
use Illuminate\Support\Traits\Macroable;
2929
use Illuminate\Support\Traits\Tappable;
30+
use const JSON_THROW_ON_ERROR as JSON_THROW_ON_ERROR1;
31+
use function Illuminate\Filesystem\join_paths;
3032

3133
/**
3234
* @template TKey of array-key
@@ -42,9 +44,9 @@ final class ConfigManager extends Repository implements \JsonSerializable, \Stri
4244
use Localizable;
4345
use Macroable;
4446
use Tappable;
45-
46-
/** @var string */
47-
public const NAME = '.ai-commit.json';
47+
public const BASE_NAME = '.ai-commit.json';
48+
public const BASE_DIRNAME = '.ai-commit';
49+
public const JSON_OPTIONS = \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES | \JSON_THROW_ON_ERROR;
4850

4951
/**
5052
* @throws \JsonException
@@ -55,194 +57,214 @@ public function __toString(): string
5557
}
5658

5759
/**
58-
* @throws \JsonException
60+
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
5961
*/
60-
public static function load(): void
62+
public static function load(): self
6163
{
62-
resolve('config')->set('ai-commit', self::create());
64+
return tap(self::make(), static fn (self $self): null => Config::set('ai-commit', $self));
6365
}
6466

6567
/**
66-
* @throws \JsonException
68+
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
6769
*/
68-
public static function create(?array $items = null): self
70+
public static function make(?array $items = null): self
6971
{
7072
if (\is_array($items)) {
7173
return new self($items);
7274
}
7375

74-
return self::createFrom(...array_filter(
76+
return self::makeFrom(...array_filter(
7577
[config_path('ai-commit.php'), self::globalPath(), self::localPath()],
76-
'file_exists'
78+
file_exists(...)
7779
));
7880
}
7981

8082
/**
81-
* @throws \JsonException
83+
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
8284
*/
83-
public static function createFrom(string ...$files): self
85+
public static function makeFrom(string ...$files): self
8486
{
8587
return new self(self::readFrom(...$files));
8688
}
8789

88-
public static function globalPath(string $path = self::NAME): string
90+
public static function globalPath(): string
8991
{
90-
$path = $path ? \DIRECTORY_SEPARATOR.$path : $path;
91-
92-
if (windows_os()) {
93-
return \sprintf('C:\\Users\\%s\\.ai-commit%s', get_current_user(), $path); // @codeCoverageIgnore
94-
}
95-
96-
return \sprintf('%s%s.ai-commit%s', exec('cd ~; pwd'), \DIRECTORY_SEPARATOR, $path);
92+
return windows_os()
93+
? join_paths('C:\\Users', get_current_user(), self::BASE_DIRNAME, self::BASE_NAME)
94+
: join_paths(exec('cd ~; pwd'), self::BASE_DIRNAME, self::BASE_NAME);
9795
}
9896

99-
public static function localPath(string $path = self::NAME): string
97+
public static function localPath(string $path = self::BASE_NAME): string
10098
{
10199
$cwd = getcwd();
102100

103101
if (false === $cwd) {
104102
$cwd = realpath('');
105103
}
106104

107-
return $cwd.($path ? \DIRECTORY_SEPARATOR.$path : $path);
105+
return join_paths($cwd, $path);
108106
}
109107

110108
/**
111109
* @throws \JsonException
112110
*/
113-
public function putGlobal(int $options = \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE): bool|int
111+
public function putGlobal(int $options = self::JSON_OPTIONS): bool|int
114112
{
115113
return $this->putFile(self::globalPath(), $options);
116114
}
117115

118116
/**
119117
* @throws \JsonException
120118
*/
121-
public function putLocal(int $options = \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE): bool|int
119+
public function putLocal(int $options = self::JSON_OPTIONS): bool|int
122120
{
123121
return $this->putFile(self::localPath(), $options);
124122
}
125123

126124
/**
127125
* @throws \JsonException
128126
*/
129-
public function putFile(string $file, int $options = \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE): bool|int
127+
public function putFile(string $file, int $options = self::JSON_OPTIONS): bool|int
130128
{
131129
collect($this->toDotArray())
132-
->filter(static fn (mixed $val): bool => !\is_scalar($val) && null !== $val)
130+
->filter(static function (mixed $value, string $key): bool {
131+
if (str($key)->is([
132+
'generators.*.parameters.messages',
133+
'generators.*.parameters.prompt',
134+
'generators.*.parameters.user',
135+
])) {
136+
return true;
137+
}
138+
139+
if (!\is_object($value)) {
140+
return false;
141+
}
142+
143+
foreach (
144+
[
145+
\JsonSerializable::class,
146+
Arrayable::class,
147+
Jsonable::class,
148+
] as $class
149+
) {
150+
if ($value instanceof $class) {
151+
return false;
152+
}
153+
}
154+
155+
return true;
156+
})
133157
->keys()
134-
->push(
135-
'generators.openai.parameters.prompt',
136-
'generators.openai.parameters.user',
137-
'generators.openai_chat.parameters.prompt',
138-
'generators.openai_chat.parameters.user',
139-
)
140-
->unique()
141-
->tap(function (Collection $collection): void {
142-
$this->forget($collection->all());
143-
});
158+
->tap(fn (Collection $keys): self => $this->forget($keys->all()));
144159

145160
File::ensureDirectoryExists(\dirname($file));
146161

147162
return File::put($file, $this->toJson($options));
148163
}
149164

150165
/**
151-
* @throws \JsonException
166+
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
152167
*/
153-
public function replaceFrom(string $file): void
168+
public function replaceFrom(string $file): self
154169
{
155-
$this->replace(self::readFrom($file));
170+
return $this->replace(self::readFrom($file));
156171
}
157172

158173
/**
159-
* @throws \JsonException
174+
* @see \Illuminate\Support\Collection::replace()
160175
*/
161-
public static function readFrom(string ...$files): array
162-
{
163-
$configurations = array_reduce($files, static function (array $configurations, string $file): array {
164-
$ext = str(pathinfo($file, \PATHINFO_EXTENSION));
165-
166-
if ($ext->is('php')) {
167-
$configurations[] = require $file;
168-
169-
return $configurations;
170-
}
171-
172-
if ($ext->is('json')) {
173-
if (!str($contents = file_get_contents($file))->isJson()) {
174-
throw InvalidJsonFileException::make($file);
175-
}
176-
177-
$configurations[] = json_decode($contents, true, 512, \JSON_THROW_ON_ERROR);
178-
179-
return $configurations;
180-
}
181-
182-
throw UnsupportedConfigFileTypeException::make($file);
183-
}, []);
184-
185-
return array_replace_recursive(...$configurations);
186-
}
187-
188-
public function replace(array $items): void
176+
public function replace(array $items): self
189177
{
190178
$this->items = array_replace_recursive($this->items, $items);
179+
180+
return $this;
191181
}
192182

193183
/**
194-
* @param list<string>|string $keys
184+
* @see \Illuminate\Support\Collection::forget()
185+
*
186+
* @param array-key|list<array-key> $keys
195187
*/
196-
public function forget(array|string $keys): void
188+
public function forget(mixed $keys): self
197189
{
198190
Arr::forget($this->items, $keys);
191+
192+
return $this;
199193
}
200194

201195
/**
202-
* Convert the object into something JSON serializable.
196+
* @see \Illuminate\Support\Collection::jsonSerialize()
203197
*
204198
* @throws \JsonException
205199
*
206200
* @return array<TKey, mixed>
207201
*/
208202
public function jsonSerialize(): array
209203
{
210-
return array_map(static function (mixed $value) {
211-
if ($value instanceof \JsonSerializable) {
212-
return $value->jsonSerialize();
213-
}
204+
return array_map(
205+
static function (mixed $value) {
206+
if ($value instanceof \JsonSerializable) {
207+
return $value->jsonSerialize();
208+
}
214209

215-
if ($value instanceof Jsonable) {
216-
return json_decode($value->toJson(), true, 512, \JSON_THROW_ON_ERROR);
217-
}
210+
if ($value instanceof Jsonable) {
211+
return json_decode($value->toJson(), true, 512, \JSON_THROW_ON_ERROR);
212+
}
218213

219-
if ($value instanceof Arrayable) {
220-
return $value->toArray();
221-
}
214+
if ($value instanceof Arrayable) {
215+
return $value->toArray();
216+
}
222217

223-
return $value;
224-
}, $this->all());
218+
return $value;
219+
},
220+
$this->all()
221+
);
225222
}
226223

227224
public function toDotArray(): array
228225
{
229226
return Arr::dot($this->toArray());
230227
}
231228

229+
/**
230+
* @see \Illuminate\Support\Collection::toArray()
231+
*/
232232
public function toArray(): array
233233
{
234234
return array_map(static fn (mixed $value) => $value instanceof Arrayable ? $value->toArray() : $value, $this->all());
235235
}
236236

237237
/**
238-
* {@inheritDoc}
238+
* @see \Illuminate\Support\Collection::toJson()
239239
*
240-
* @noinspection JsonEncodingApiUsageInspection
240+
* {@inheritDoc}
241241
*
242242
* @throws \JsonException
243243
*/
244-
public function toJson(mixed $options = \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE): string
244+
public function toJson(mixed $options = self::JSON_OPTIONS): string
245245
{
246246
return json_encode($this->jsonSerialize(), $options);
247247
}
248+
249+
/**
250+
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
251+
*/
252+
private static function readFrom(string ...$files): array
253+
{
254+
return array_replace_recursive(
255+
...array_reduce(
256+
$files,
257+
static function (array $configurations, string $file): array {
258+
$configurations[] = match (File::extension($file)) {
259+
'php' => require $file,
260+
'json' => File::json($file, JSON_THROW_ON_ERROR1),
261+
default => throw UnsupportedConfigFileTypeException::make($file)
262+
};
263+
264+
return $configurations;
265+
},
266+
[]
267+
)
268+
);
269+
}
248270
}

app/Exceptions/InvalidJsonFileException.php

Lines changed: 0 additions & 22 deletions
This file was deleted.

app/Exceptions/RuntimeException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515

1616
use App\Contracts\ThrowableContract;
1717

18-
class RuntimeException extends \RuntimeException implements ThrowableContract {}
18+
final class RuntimeException extends \RuntimeException implements ThrowableContract {}

app/Exceptions/TaskException.php

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)