3
3
namespace Slack\Hack\JsonSchema\Codegen ;
4
4
5
5
use namespace HH\Lib\{C , Math , Str , Vec} ;
6
- use type Facebook\HackCodegen\{CodegenMethod , CodegenType , HackBuilder , HackBuilderKeys , HackBuilderValues} ;
6
+ use type Facebook\HackCodegen\{
7
+ CodegenClass ,
8
+ CodegenMethod ,
9
+ CodegenType ,
10
+ HackBuilder ,
11
+ HackBuilderKeys ,
12
+ HackBuilderValues ,
13
+ };
7
14
8
15
type TUntypedSchema = shape(
9
16
?' anyOf' => vec <TSchema >,
20
27
21
28
class UntypedBuilder extends BaseBuilder <TUntypedSchema > {
22
29
protected static string $schema_name = ' Slack\hack\JsonSchema\Codegen\TUntypedSchema' ;
23
- private ?string $current_type = null ;
30
+ private Typing \Type $type_info ;
31
+
32
+ public function __construct (Context $ctx , string $suffix , TSchema $schema , ?CodegenClass $class = null ) {
33
+ parent :: __construct($ctx , $suffix , $schema , $class );
34
+ $this -> type_info = Typing \TypeSystem :: mixed();
35
+ }
24
36
25
37
<<__Override >>
26
38
public function build (): this {
@@ -31,6 +43,7 @@ public function build(): this {
31
43
32
44
$type = $this -> codegenType();
33
45
$this -> ctx -> getFile()-> addBeforeType($type );
46
+ Typing \TypeSystem :: registerAlias($this -> getType(), $this -> type_info );
34
47
35
48
return $this ;
36
49
}
@@ -128,6 +141,7 @@ private function generateNotChecks(vec<TSchema> $schemas, HackBuilder $hb): void
128
141
129
142
}
130
143
144
+ // TODO: Determine Lowest Upper Bound for oneOf constraint.
131
145
private function generateOneOfChecks (vec <TSchema > $schemas , HackBuilder $hb ): void {
132
146
$constraints = vec [];
133
147
foreach ($schemas as $index => $schema ) {
@@ -184,6 +198,7 @@ private function generateOneOfChecks(vec<TSchema> $schemas, HackBuilder $hb): vo
184
198
185
199
}
186
200
201
+ // TODO: Determine Greatest Lower Bound for allOf constraint.
187
202
private function generateAllOfChecks (vec <TSchema > $schemas , HackBuilder $hb ): void {
188
203
$merged_schema = $this -> getMergedAllOfChecks();
189
204
if ($merged_schema ) {
@@ -322,7 +337,6 @@ public function getMergedAllOfChecks(): ?TSchema {
322
337
private function generateMergedAllOfChecks (TSchema $schema , HackBuilder $hb ): void {
323
338
$schema_builder = new SchemaBuilder ($this -> ctx , $this -> generateClassName($this -> suffix , ' allOf' ), $schema );
324
339
$schema_builder -> build();
325
- $this -> current_type = $schema_builder -> getType();
326
340
$hb -> addReturnf(' %s::check($input, $pointer)' , $schema_builder -> getClassName());
327
341
}
328
342
@@ -469,47 +483,34 @@ private function getOptimizedAnyOfTypes(vec<SchemaBuilder> $schema_builders): ?T
469
483
}
470
484
471
485
private function generateGenericAnyOfChecks (vec <SchemaBuilder > $schema_builders , HackBuilder $hb ): void {
472
- $constraints = vec [];
486
+ $present_types = vec [];
487
+ $nonnull_builders = vec [];
473
488
foreach ($schema_builders as $schema_builder ) {
474
- $constraints [] = " {$schema_builder->getClassName()} ::check<>" ;
475
- }
476
-
477
- // Checks for the special case of one null and one non-null type in order to
478
- // form a nullable unified type.
479
- //
480
- // For unique references, we require that the schemas be built before
481
- // accessing the type. However, we want to filter out the `NullBuilder` in
482
- // this special case before it gets built. If we run into this case, we'll
483
- // store a reference to the `non_null_schema_builder` that we can reference
484
- // after we've filtered out the `NullBuilder` and have built all the
485
- // schemas. This lets us adjust the current type to be nullable
486
- // (`?<whatever>`)
487
- $non_null_schema_builder = null ;
488
- if (C \count ($schema_builders ) === 1 ) {
489
- $this -> current_type = $schema_builders [0 ]-> getType();
490
- } else if (C \count ($schema_builders ) === 2 ) {
491
- $without_null = Vec \filter ($schema_builders , $sb ==> ! ($sb -> getBuilder() is NullBuilder ));
492
-
493
- if (C \count ($without_null ) === 1 ) {
494
- $non_null_schema_builder = $without_null [0 ];
495
-
496
- $constraints = vec [" {$non_null_schema_builder->getClassName()} ::check<>" ];
497
- $schema_builders = vec [$non_null_schema_builder ];
498
-
499
- $hb
500
- -> startIfBlock(' $input === null' )
501
- -> addReturn(null , HackBuilderValues :: export())
502
- -> endIfBlock()
503
- -> ensureEmptyLine();
489
+ // Avoid building null validators as we're going to instead
490
+ // bail out using a guard.
491
+ if ($schema_builder -> getBuilder() is NullBuilder ) {
492
+ $present_types [] = Typing \TypeSystem :: null();
493
+ } else {
494
+ $schema_builder -> build();
495
+ $nonnull_builders [] = $schema_builder ;
496
+ $present_types [] = $schema_builder -> getTypeInfo();
504
497
}
505
498
}
506
499
507
- foreach ($schema_builders as $sb ) {
508
- $sb -> build();
500
+ $this -> type_info = Typing \TypeSystem :: union($present_types );
501
+ if ($this -> type_info -> isOptional()) {
502
+ // If the type is optional, don't bother building null builders
503
+ // and instead just short-circuit.
504
+ $hb
505
+ -> startIfBlock(' $input === null' )
506
+ -> addReturn(null , HackBuilderValues :: export())
507
+ -> endIfBlock()
508
+ -> ensureEmptyLine();
509
509
}
510
510
511
- if ($non_null_schema_builder is nonnull ) {
512
- $this -> current_type = ' ?' . $non_null_schema_builder -> getType();
511
+ $constraints = vec [];
512
+ foreach ($nonnull_builders as $schema_builder ) {
513
+ $constraints [] = " {$schema_builder->getClassName()} ::check<>" ;
513
514
}
514
515
515
516
$hb
@@ -628,7 +629,12 @@ private function codegenType(): CodegenType {
628
629
return $this -> ctx
629
630
-> getHackCodegenFactory()
630
631
-> codegenType($this -> getType())
631
- -> setType($this -> current_type ?? ' mixed ' );
632
+ -> setType($this -> type_info -> render() );
632
633
}
633
634
635
+ <<__Override >>
636
+ public function getTypeInfo (): Typing \Type {
637
+ invariant ($this -> type_info is nonnull , ' must call `build` method before accessing type info' );
638
+ return $this -> type_info ;
639
+ }
634
640
}
0 commit comments