Skip to content

Commit 425ef79

Browse files
authored
Merge pull request #66 from slackhq/ih_top_level_ref_aliases
Alias top-level refs
2 parents 99b251b + 2378347 commit 425ef79

File tree

6 files changed

+949
-7
lines changed

6 files changed

+949
-7
lines changed

src/Codegen/Codegen.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
};
1717

1818
final class Codegen implements IBuilder {
19+
use Factory;
1920

2021
const type TValidatorRefsUniqueConfig = shape(
2122
/* The root path where all of the schemas live */
@@ -139,7 +140,7 @@ private function __construct(private dict<arraykey, mixed> $schema, private self
139140
$this->file->setNamespace($validatorConfig['namespace']);
140141
}
141142

142-
$context = new Context(
143+
$this->ctx = new Context(
143144
$this->config,
144145
$this->cg,
145146
$this->getJsonSchemaCodegenConfig(),
@@ -148,7 +149,7 @@ private function __construct(private dict<arraykey, mixed> $schema, private self
148149
$this->file,
149150
);
150151
$typed_schema = type_assert_type($this->schema, TSchema::class);
151-
$this->builder = new SchemaBuilder($context, '', $typed_schema, $this->class);
152+
$this->builder = new SchemaBuilder($this->ctx, '', $typed_schema, $this->class);
152153

153154
$this->sanitizeString = $validatorConfig['sanitize_string'] ?? null;
154155
}
@@ -258,8 +259,10 @@ public function build(): this {
258259
$this->builder->build();
259260

260261
if ($this->builder->isUniqueRef()) {
261-
// No need to do anything, we're building a top-level ref.
262-
return $this;
262+
// Add a type aliasing the referenced type
263+
$this->file->addBeforeType(
264+
$this->getHackCodegenFactory()->codegenType($this->getType())->setType($this->builder->getType()),
265+
);
263266
}
264267

265268
$this->class = $this->class
@@ -282,7 +285,12 @@ public function build(): this {
282285

283286
private function getCodegenClassProcessMethod(): CodegenMethod {
284287
$hb = new HackBuilder($this->getHackCodegenConfig());
285-
$hb->addMultilineCall('return self::check', vec['$this->input', '$this->pointer']);
288+
if ($this->builder->isUniqueRef()) {
289+
$obj = $this->builder->getClassName();
290+
} else {
291+
$obj = 'self';
292+
}
293+
$hb->addMultilineCall(Str\format('return %s::check', $obj), vec['$this->input', '$this->pointer']);
286294

287295
return $this->cg
288296
->codegenMethod('process')
@@ -301,11 +309,20 @@ public function getFile(): CodegenFile {
301309
}
302310

303311
public function getClassName(): string {
304-
return $this->builder->getClassName();
312+
// Whether or not we're generating a unique ref,
313+
// we'll always end up outputting a new class.
314+
return $this->generateClassName($this->class->getName());
305315
}
306316

307317
public function getType(): string {
308-
return $this->builder->getType();
318+
if ($this->builder->isUniqueRef()) {
319+
// Generating an alias to a unique ref.
320+
// Don't re-use the referenced type's name; instead,
321+
// generate a new type pointing to it.
322+
return $this->generateTypeName($this->getClassName());
323+
} else {
324+
return $this->builder->getType();
325+
}
309326
}
310327

311328
public function isArrayKeyType(): bool {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
namespace Slack\Hack\JsonSchema\Tests;
2+
3+
use type Slack\Hack\JsonSchema\Tests\Generated\TopLevelRefValidator;
4+
5+
final class TopLevelRefValidatorTest extends BaseCodegenTestCase {
6+
7+
<<__Override>>
8+
public static async function beforeFirstTestAsync(): Awaitable<void> {
9+
$ret = self::getBuilder(
10+
'top-level-ref.json',
11+
'TopLevelRefValidator',
12+
shape(
13+
'refs' => shape(
14+
'unique' => shape(
15+
'source_root' => __DIR__,
16+
'output_root' => __DIR__.'/examples/codegen',
17+
),
18+
),
19+
),
20+
);
21+
$ret['codegen']->build();
22+
require_once($ret['path']);
23+
}
24+
25+
public function testTopLevelRef(): void {
26+
$cases = vec[
27+
shape(
28+
'input' => darray['integer' => 1000],
29+
'output' => darray['integer' => 1000],
30+
'valid' => true,
31+
),
32+
shape(
33+
'input' => darray['integer' => 0],
34+
'output' => darray['integer' => 0],
35+
'valid' => true,
36+
),
37+
shape('input' => darray['integer' => '1000'], 'valid' => false),
38+
shape('input' => darray['integer' => 1000.00], 'valid' => false),
39+
];
40+
41+
$this->expectCases($cases, $input ==> new TopLevelRefValidator($input));
42+
}
43+
}

0 commit comments

Comments
 (0)