Skip to content

Commit 18fcf64

Browse files
authored
Add support for generating a standalone package (#1484)
* Extract the resolution of the directory for a given namespace This avoids duplicating the logic in the 2 writers (with slightly different implementation) and will allow swapping the resolver for standalone packages. * Refactor the entrypoint of the runner The generate.php script in the code generator does not make sense as an executable as it defaults to reading the manifest and generating the code in the code generator folder itself, which is not the actual use case. Configuring it through environment variables also makes it harder to configure objects like the directory resolver. * Implement a directory resolver for standalone packages This resolver supports the use case of generating a standalone package containing only one service, where the package is at the root of the repository.
1 parent 6ca369f commit 18fcf64

File tree

9 files changed

+144
-93
lines changed

9 files changed

+144
-93
lines changed

generate

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,23 @@
11
#!/usr/bin/env php
22
<?php
33

4+
use AsyncAws\CodeGenerator\File\Location\AsyncAwsMonoRepoResolver;
5+
use AsyncAws\CodeGenerator\Runner;
46
use AsyncAws\Core\EnvVar;
57

6-
$generatorDir = __DIR__ . '/src/CodeGenerator';
7-
if (!file_exists($generatorDir . '/generate.php')) {
8-
echo "Unable to find the `generate.php` script in `/src/CodeGenerator/`.\n";
9-
exit(1);
10-
}
11-
128
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
139
echo "Run `composer install` before you run the `generate` script.\n";
1410
exit(1);
1511
}
1612

1713
require __DIR__ . '/vendor/autoload.php';
1814

19-
if (null === EnvVar::get('ASYNC_AWS_GENERATE_MANIFEST')) {
20-
$_SERVER['ASYNC_AWS_GENERATE_MANIFEST'] = __DIR__ . '/manifest.json';
21-
}
22-
if (null === EnvVar::get('ASYNC_AWS_GENERATE_CACHE')) {
23-
$_SERVER['ASYNC_AWS_GENERATE_CACHE'] = __DIR__ . '/.cache/generate';
24-
}
25-
if (null === EnvVar::get('ASYNC_AWS_GENERATE_SRC')) {
26-
$_SERVER['ASYNC_AWS_GENERATE_SRC'] = __DIR__ . '/src';
27-
}
2815
if (null === EnvVar::get('SYMFONY_PATCH_TYPE_DECLARATIONS')) {
2916
$_SERVER['SYMFONY_PATCH_TYPE_DECLARATIONS'] = 'deprecations=1';
3017
}
3118

32-
require $generatorDir . '/generate.php';
19+
Runner::create(
20+
__DIR__ . '/manifest.json',
21+
new AsyncAwsMonoRepoResolver(__DIR__ . '/src'),
22+
__DIR__ . '/.cache/generate'
23+
)->run();

src/CodeGenerator/generate

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

src/CodeGenerator/generate.php

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

src/CodeGenerator/src/File/ClassWriter.php

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace AsyncAws\CodeGenerator\File;
66

7+
use AsyncAws\CodeGenerator\File\Location\DirectoryResolver;
78
use AsyncAws\CodeGenerator\Generator\PhpGenerator\ClassBuilder;
89

910
/**
@@ -16,9 +17,9 @@
1617
class ClassWriter
1718
{
1819
/**
19-
* @var string
20+
* @var DirectoryResolver
2021
*/
21-
private $srcDirectory;
22+
private $directoryResolver;
2223

2324
/**
2425
* @var FileDumper
@@ -30,44 +31,31 @@ class ClassWriter
3031
*/
3132
private $printer;
3233

33-
public function __construct(string $srcDirectory, FileDumper $fileDumper)
34+
public function __construct(DirectoryResolver $directoryResolver, FileDumper $fileDumper)
3435
{
35-
$this->srcDirectory = $srcDirectory;
3636
$this->fileDumper = $fileDumper;
3737
$this->printer = new Printer();
38+
$this->directoryResolver = $directoryResolver;
3839
}
3940

4041
public function write(ClassBuilder $classBuilder): void
4142
{
4243
$content = $this->printer->printNamespace($classBuilder->build());
4344

4445
$className = $classBuilder->getClassName();
45-
// Remove AsyncAws\
46-
$fqcn = substr($className->getNamespace(), 9);
4746

47+
$namespace = $className->getNamespace();
4848
$className = $className->getName();
49-
$directory = $this->resolveDirectory($fqcn);
49+
$directory = $this->resolveDirectory($namespace);
5050

5151
$filename = sprintf('%s/%s.php', $directory, $className);
5252

5353
$this->fileDumper->dump($filename, $content);
5454
}
5555

56-
private function resolveDirectory(string $fqcn): string
56+
private function resolveDirectory(string $namespace): string
5757
{
58-
$parts = explode('\\', $fqcn);
59-
$service = array_shift($parts); // Lambda, S3, Sqs etc
60-
if (isset($parts[0]) && 'Tests' === $parts[0]) {
61-
array_shift($parts); // Tests
62-
array_unshift($parts, $service, 'tests');
63-
} else {
64-
array_unshift($parts, $service, 'src');
65-
}
66-
if ('Core' !== $service) {
67-
array_unshift($parts, 'Service');
68-
}
69-
70-
$directory = sprintf('%s/%s', $this->srcDirectory, implode('/', $parts));
58+
$directory = $this->directoryResolver->resolveDirectory($namespace);
7159
if (!is_dir($directory)) {
7260
if (false === mkdir($directory, 0777, true)) {
7361
throw new \RuntimeException(sprintf('Could not create directory "%s"', $directory));

src/CodeGenerator/src/File/ComposerWriter.php

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace AsyncAws\CodeGenerator\File;
66

7+
use AsyncAws\CodeGenerator\File\Location\DirectoryResolver;
8+
79
/**
810
* Update composer.json requirements.
911
*
@@ -14,13 +16,13 @@
1416
class ComposerWriter
1517
{
1618
/**
17-
* @var string
19+
* @var DirectoryResolver
1820
*/
19-
private $srcDirectory;
21+
private $directoryResolver;
2022

21-
public function __construct(string $srcDirectory)
23+
public function __construct(DirectoryResolver $directoryResolver)
2224
{
23-
$this->srcDirectory = $srcDirectory;
25+
$this->directoryResolver = $directoryResolver;
2426
}
2527

2628
/**
@@ -55,31 +57,11 @@ public function setRequirements(string $namespace, array $requirements, bool $cl
5557

5658
private function findFile(string $namespace): string
5759
{
58-
$dir = $this->resolveDirectory($namespace);
60+
$dir = $this->directoryResolver->resolveDirectory($namespace);
5961
while (!file_exists($dir . '/composer.json')) {
6062
$dir = \dirname($dir);
6163
}
6264

6365
return $dir . '/composer.json';
6466
}
65-
66-
private function resolveDirectory(string $fqcn): string
67-
{
68-
$parts = explode('\\', $fqcn);
69-
array_shift($parts); // AsyncAws
70-
$service = array_shift($parts); // Lambda, S3, Sqs etc
71-
array_unshift($parts, $service, 'src');
72-
if ('Core' !== $service) {
73-
array_unshift($parts, 'Service');
74-
}
75-
76-
$directory = sprintf('%s/%s', $this->srcDirectory, implode('/', $parts));
77-
if (!is_dir($directory)) {
78-
if (false === mkdir($directory, 0777, true)) {
79-
throw new \RuntimeException(sprintf('Could not create directory "%s"', $directory));
80-
}
81-
}
82-
83-
return $directory;
84-
}
8567
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace AsyncAws\CodeGenerator\File\Location;
4+
5+
/**
6+
* @internal
7+
*/
8+
class AsyncAwsMonoRepoResolver implements DirectoryResolver
9+
{
10+
/**
11+
* @var string
12+
*/
13+
private $srcDirectory;
14+
15+
public function __construct(string $srcDirectory)
16+
{
17+
$this->srcDirectory = $srcDirectory;
18+
}
19+
20+
public function resolveDirectory(string $namespace): string
21+
{
22+
$relativeNamespace = substr($namespace, \strlen('AsyncAws\\'));
23+
24+
$parts = explode('\\', $relativeNamespace);
25+
$service = array_shift($parts); // Lambda, S3, Sqs etc
26+
if (isset($parts[0]) && 'Tests' === $parts[0]) {
27+
array_shift($parts); // Tests
28+
array_unshift($parts, $service, 'tests');
29+
} else {
30+
array_unshift($parts, $service, 'src');
31+
}
32+
if ('Core' !== $service) {
33+
array_unshift($parts, 'Service');
34+
}
35+
36+
return sprintf('%s/%s', $this->srcDirectory, implode('/', $parts));
37+
}
38+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace AsyncAws\CodeGenerator\File\Location;
4+
5+
interface DirectoryResolver
6+
{
7+
/**
8+
* Resolves the directory in which files should be written for this namespace.
9+
*/
10+
public function resolveDirectory(string $namespace): string;
11+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace AsyncAws\CodeGenerator\File\Location;
4+
5+
class StandalonePackageResolver implements DirectoryResolver
6+
{
7+
/**
8+
* @var string
9+
*/
10+
private $rootDirectory;
11+
12+
/**
13+
* @var string
14+
*/
15+
private $rootNamespace;
16+
17+
public function __construct(string $rootDirectory, string $rootNamespace)
18+
{
19+
$this->rootDirectory = $rootDirectory;
20+
$this->rootNamespace = $rootNamespace;
21+
}
22+
23+
public function resolveDirectory(string $namespace): string
24+
{
25+
if ($namespace === $this->rootNamespace) {
26+
return $this->rootDirectory . '/src';
27+
}
28+
29+
if (0 !== strpos($namespace, $this->rootNamespace . '\\')) {
30+
throw new \InvalidArgumentException('The namespace must belong to the generated package.');
31+
}
32+
33+
$relativeNamespace = substr($namespace, \strlen($this->rootNamespace . '\\'));
34+
35+
$parts = explode('\\', $relativeNamespace);
36+
if (isset($parts[0]) && 'Tests' === $parts[0]) {
37+
array_shift($parts); // Tests
38+
array_unshift($parts, 'tests');
39+
} else {
40+
array_unshift($parts, 'src');
41+
}
42+
43+
return sprintf('%s/%s', $this->rootDirectory, implode('/', $parts));
44+
}
45+
}

src/CodeGenerator/src/Runner.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace AsyncAws\CodeGenerator;
4+
5+
use AsyncAws\CodeGenerator\Command\GenerateCommand;
6+
use AsyncAws\CodeGenerator\File\Cache;
7+
use AsyncAws\CodeGenerator\File\CachedFileDumper;
8+
use AsyncAws\CodeGenerator\File\ClassWriter;
9+
use AsyncAws\CodeGenerator\File\ComposerWriter;
10+
use AsyncAws\CodeGenerator\File\Location\DirectoryResolver;
11+
use AsyncAws\CodeGenerator\Generator\ApiGenerator;
12+
use Symfony\Component\Console\Application;
13+
14+
final class Runner
15+
{
16+
public static function create(string $manifest, DirectoryResolver $directoryResolver, string $cacheDirectory): Application
17+
{
18+
$application = new Application('AsyncAws', '0.1.0');
19+
20+
$cache = new Cache($cacheDirectory);
21+
$command = new GenerateCommand($manifest, $cache, new ClassWriter($directoryResolver, new CachedFileDumper($cache)), new ComposerWriter($directoryResolver), new ApiGenerator());
22+
$application->add($command);
23+
$application->setDefaultCommand($command->getName(), true);
24+
25+
return $application;
26+
}
27+
}

0 commit comments

Comments
 (0)