Skip to content

Commit c251f5a

Browse files
committed
Make configuration parsing more flexible
This moves responsibilities around and introduces abstractions to support different file formats with less effort. Signed-off-by: Luís Cobucci <[email protected]>
1 parent 3cc1fbe commit c251f5a

File tree

7 files changed

+124
-132
lines changed

7 files changed

+124
-132
lines changed

src/Command/AssertBackwardsCompatible.php

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use Psl;
88
use Psl\Env;
9-
use Psl\File;
109
use Psl\Iter;
1110
use Psl\Str;
1211
use Psl\Type;
@@ -35,8 +34,6 @@
3534

3635
final class AssertBackwardsCompatible extends Command
3736
{
38-
private const CONFIGURATION_FILENAME = '.roave-backward-compatibility-check.json';
39-
4037
/** @throws LogicException */
4138
public function __construct(
4239
private PerformCheckoutOfRevision $git,
@@ -130,7 +127,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
130127

131128
$toRevision = $this->parseRevision->fromStringForRepository($to, $sourceRepo);
132129

133-
$configuration = $this->determineConfiguration($currentDirectory, $stdErr);
130+
$configuration = (new DetermineConfigurationFromFilesystem())($currentDirectory, $stdErr);
134131

135132
$stdErr->writeln(Str\format(
136133
'Comparing from %s to %s...',
@@ -220,24 +217,4 @@ private function determineFromRevisionFromRepository(
220217
$repository,
221218
);
222219
}
223-
224-
private function determineConfiguration(
225-
string $currentDirectory,
226-
OutputInterface $stdErr,
227-
): Configuration {
228-
$fileName = $currentDirectory . '/' . self::CONFIGURATION_FILENAME;
229-
230-
try {
231-
$configContents = File\read($fileName);
232-
} catch (File\Exception\InvalidArgumentException) {
233-
return Configuration::default();
234-
}
235-
236-
$stdErr->writeln(Str\format(
237-
'Using "%s" as configuration file',
238-
Type\string()->coerce($fileName),
239-
));
240-
241-
return Configuration::fromJson($configContents);
242-
}
243220
}

src/Command/Configuration.php

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Roave\BackwardCompatibility\Command;
6+
7+
use Psl\Str;
8+
use Psl\Type;
9+
use Roave\BackwardCompatibility\Configuration\Configuration;
10+
use Roave\BackwardCompatibility\Configuration\ParseConfigurationFile;
11+
use Roave\BackwardCompatibility\Configuration\ParseJsonConfigurationFile;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
14+
final class DetermineConfigurationFromFilesystem
15+
{
16+
public function __construct(
17+
private readonly ParseConfigurationFile $parser = new ParseJsonConfigurationFile(),
18+
) {
19+
}
20+
21+
public function __invoke(
22+
string $currentDirectory,
23+
OutputInterface $stdErr,
24+
): Configuration {
25+
$configuration = $this->parser->parse($currentDirectory);
26+
27+
if ($configuration->filename !== null) {
28+
$stdErr->writeln(Str\format(
29+
'Using "%s" as configuration file',
30+
Type\string()->coerce($configuration->filename),
31+
));
32+
}
33+
34+
return $configuration;
35+
}
36+
}

src/Configuration/Configuration.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Roave\BackwardCompatibility\Configuration;
6+
7+
use Roave\BackwardCompatibility\Baseline;
8+
9+
/** @psalm-immutable */
10+
final class Configuration
11+
{
12+
private function __construct(
13+
public readonly Baseline $baseline,
14+
public readonly string|null $filename,
15+
) {
16+
}
17+
18+
public static function default(): self
19+
{
20+
return new self(Baseline::empty(), null);
21+
}
22+
23+
public static function fromFile(Baseline $baseline, string $filename): self
24+
{
25+
return new self($baseline, $filename);
26+
}
27+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Roave\BackwardCompatibility\Configuration;
5+
6+
interface ParseConfigurationFile
7+
{
8+
public function parse(string $currentDirectory): Configuration;
9+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Roave\BackwardCompatibility\Configuration;
6+
7+
use Psl\File;
8+
use Psl\Json;
9+
use Psl\Type;
10+
use Roave\BackwardCompatibility\Baseline;
11+
use RuntimeException;
12+
13+
final class ParseJsonConfigurationFile implements ParseConfigurationFile
14+
{
15+
private const CONFIGURATION_FILENAME = '.roave-backward-compatibility-check.json';
16+
17+
public function parse(string $currentDirectory): Configuration
18+
{
19+
$filename = $currentDirectory . '/' . self::CONFIGURATION_FILENAME;
20+
21+
try {
22+
$jsonContents = File\read($filename);
23+
} catch (File\Exception\InvalidArgumentException) {
24+
return Configuration::default();
25+
}
26+
27+
try {
28+
$configuration = Json\typed(
29+
$jsonContents,
30+
Type\shape(
31+
['baseline' => Type\optional(Type\vec(Type\string()))],
32+
),
33+
);
34+
} catch (Json\Exception\DecodeException $exception) {
35+
throw new RuntimeException(
36+
'It was not possible to parse the configuration',
37+
previous: $exception,
38+
);
39+
}
40+
41+
$baseline = $configuration['baseline'] ?? [];
42+
43+
return Configuration::fromFile(
44+
Baseline::fromList(...$baseline),
45+
$filename,
46+
);
47+
}
48+
}

test/unit/Command/ConfigurationTest.php

Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,77 +6,16 @@
66

77
use PHPUnit\Framework\TestCase;
88
use Roave\BackwardCompatibility\Baseline;
9-
use Roave\BackwardCompatibility\Command\Configuration;
10-
use RuntimeException;
9+
use Roave\BackwardCompatibility\Configuration\Configuration;
1110

12-
/** @covers \Roave\BackwardCompatibility\Command\Configuration */
11+
/** @covers \Roave\BackwardCompatibility\Configuration\Configuration */
1312
final class ConfigurationTest extends TestCase
1413
{
1514
public function testBaselineShouldBeEmptyForDefaultConfiguration(): void
1615
{
1716
$config = Configuration::default();
1817

1918
self::assertEquals(Baseline::empty(), $config->baseline);
20-
}
21-
22-
/** @dataProvider validConfigurations */
23-
public function testBaselineShouldBeReadFromJsonContents(
24-
string $jsonContents,
25-
Baseline $expectedBaseline,
26-
): void {
27-
$config = Configuration::fromJson($jsonContents);
28-
29-
self::assertEquals($expectedBaseline, $config->baseline);
30-
}
31-
32-
/** @psalm-return iterable<string, array{string, Baseline}> */
33-
public function validConfigurations(): iterable
34-
{
35-
yield 'empty object' => ['{}', Baseline::empty()];
36-
yield 'empty array' => ['[]', Baseline::empty()];
37-
yield 'empty baseline property' => ['{"baseline":[]}', Baseline::empty()];
38-
39-
yield 'baseline with strings' => [
40-
<<<'JSON'
41-
{"baseline": ["#\\[BC\\] CHANGED: The parameter \\$a#"]}
42-
JSON,
43-
Baseline::fromList('#\[BC\] CHANGED: The parameter \$a#'),
44-
];
45-
46-
yield 'random properties are ignored' => [
47-
<<<'JSON'
48-
{
49-
"baseline": ["#\\[BC\\] CHANGED: The parameter \\$a#"],
50-
"random": false
51-
}
52-
JSON,
53-
Baseline::fromList('#\[BC\] CHANGED: The parameter \$a#'),
54-
];
55-
}
56-
57-
/** @dataProvider invalidConfigurations */
58-
public function testExceptionShouldBeTriggeredOnInvalidConfiguration(
59-
string $jsonContents,
60-
): void {
61-
$this->expectException(RuntimeException::class);
62-
63-
Configuration::fromJson($jsonContents);
64-
}
65-
66-
/** @psalm-return iterable<string, array{string}> */
67-
public function invalidConfigurations(): iterable
68-
{
69-
yield 'empty content' => [''];
70-
yield 'empty string' => ['""'];
71-
yield 'int' => ['0'];
72-
yield 'float' => ['0.1'];
73-
yield 'boolean' => ['false'];
74-
yield 'baseline with string' => ['{"baseline": "this should be a list"}'];
75-
yield 'baseline with int' => ['{"baseline": 0}'];
76-
yield 'baseline with float' => ['{"baseline": 0.0}'];
77-
yield 'baseline with bool' => ['{"baseline": true}'];
78-
yield 'baseline with array of float' => ['{"baseline": [0.0]}'];
79-
yield 'baseline with array of bool' => ['{"baseline": [false]}'];
80-
yield 'baseline with array of object' => ['{"baseline": [{}]}'];
19+
self::assertNull($config->filename);
8120
}
8221
}

0 commit comments

Comments
 (0)