1313
1414namespace App ;
1515
16- use App \Exceptions \InvalidJsonFileException ;
1716use App \Exceptions \UnsupportedConfigFileTypeException ;
1817use Illuminate \Config \Repository ;
1918use Illuminate \Contracts \Support \Arrayable ;
2019use Illuminate \Contracts \Support \Jsonable ;
2120use Illuminate \Support \Arr ;
2221use Illuminate \Support \Collection ;
22+ use Illuminate \Support \Facades \Config ;
2323use Illuminate \Support \Facades \File ;
2424use Illuminate \Support \Traits \Conditionable ;
2525use Illuminate \Support \Traits \Dumpable ;
2626use Illuminate \Support \Traits \ForwardsCalls ;
2727use Illuminate \Support \Traits \Localizable ;
2828use Illuminate \Support \Traits \Macroable ;
2929use 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}
0 commit comments