Skip to content

Commit

Permalink
Convert table_schema type to TableSchema object (#100)
Browse files Browse the repository at this point in the history
* Convert table_schema type to TableSchema object

* Remove unused use
  • Loading branch information
muglug committed May 8, 2023
1 parent 0a1e659 commit 05703b0
Show file tree
Hide file tree
Showing 18 changed files with 556 additions and 753 deletions.
130 changes: 91 additions & 39 deletions src/BuildSchemaCLI.php
Expand Up @@ -5,18 +5,17 @@
use namespace HH\Lib\{C, Regex};
use type Facebook\CLILib\CLIWithArguments;
use namespace Facebook\CLILib\CLIOptions;
use type Facebook\HackCodegen\{HackBuilderKeys, HackBuilderValues, HackCodegenConfig, HackCodegenFactory};

final class BuildSchemaCLI extends CLIWithArguments {
private string $constName = 'DB_SCHEMA';
private string $functionName = 'get_db_schema';

<<__Override>>
protected function getSupportedOptions(): vec<CLIOptions\CLIOption> {
return vec[CLIOptions\with_required_string(
$name ==> {
$this->constName = $name;
$this->functionName = $name;
},
'The name of the constant to generate. Defaults to DB_SCHEMA',
'The name of the function name to generate. Defaults to get_db_schema',
'--name',
)];
}
Expand Down Expand Up @@ -67,43 +66,96 @@ protected function getSupportedOptions(): vec<CLIOptions\CLIOption> {
$generated[$db] = $schema;
}

$cg = new HackCodegenFactory(new HackCodegenConfig());

$generated = $cg->codegenConstant($this->constName)
->setType('dict<string, dict<string, table_schema>>')
->setValue($generated, HackBuilderValues::dict(HackBuilderKeys::export(), HackBuilderValues::dict(
HackBuilderKeys::export(),
// special exporters are required to make shapes and enum values, ::export would turn them into arrays and strings
HackBuilderValues::shapeWithPerKeyRendering(
shape(
'name' => HackBuilderValues::export(),
'indexes' =>
HackBuilderValues::vec(HackBuilderValues::shapeWithUniformRendering(HackBuilderValues::export())),
'fields' => HackBuilderValues::vec(HackBuilderValues::shapeWithPerKeyRendering(
shape(
'name' => HackBuilderValues::export(),
'type' => HackBuilderValues::lambda(($_cfg, $str) ==> 'DataType::'.$str),
'length' => HackBuilderValues::export(),
'null' => HackBuilderValues::export(),
'hack_type' => HackBuilderValues::export(),
'default' => HackBuilderValues::export(),
'unsigned' => HackBuilderValues::export(),
),
)),
),
),
)))
->render();

$generated = <<<EOT
use type Slack\\SQLFake\\{table_schema, DataType};
EOT
.
$generated;
$generated = self::getRenderedHackTableSchemaWithClusters($this->functionName, $generated);

await $terminal->getStdout()->writeAllAsync($generated);
return 0;
}

//
// Write out a top level import target containing all of our generated db files.
//
// This also contains a memoized function that returns a lookup for each field in our DB tables and the type of those fields.
//

public static function getRenderedHackTableSchemaWithClusters(
string $function_name,
dict<string, dict<string, TableSchema>> $table_schemas,
): string {
$file_contents = '';

$file_contents .= "use type Slack\\SQLFake\\{Column, DataType, Index, TableSchema};\n";
$file_contents .= "\n";
$file_contents .= "<<__Memoize>>\n";
$file_contents .= "function {$function_name}(): dict<string, dict<string, table_schema>> {\n";
$file_contents .= "\treturn dict[\n";
foreach ($table_schemas as $cluster => $tables) {
$file_contents .= "\t\t'{$cluster}' => ".self::getRenderedHackTableSchema($tables, "\t\t");
}
$file_contents .= "\t];\n";
$file_contents .= "}\n";

return $file_contents;
}

public static function getRenderedHackTableSchema(
dict<string, TableSchema> $table_schemas,
string $indentation,
): string {
$file_contents = "dict[\n";
foreach ($table_schemas as $table_schema) {
$table_name = $table_schema->name;
$file_contents .= $indentation."\t'{$table_name}' => new TableSchema(\n";

//
// Write out the fields
//

$file_contents .= $indentation."\t\t'{$table_name}',\n";
$file_contents .= $indentation."\t\tvec[\n";
foreach ($table_schema->fields as $field) {
$file_contents .= $indentation."\t\t\tnew Column(\n";
$file_contents .= $indentation."\t\t\t\t'{$field->name}',\n";
$file_contents .= $indentation."\t\t\t\tDataType::{$field->type},\n";
$file_contents .= $indentation."\t\t\t\t{$field->length},\n";
$file_contents .= $indentation."\t\t\t\t" . ($field->null ? 'true' : 'false') . ",\n";
$file_contents .= $indentation."\t\t\t\t'{$field->hack_type}',\n";
if ($field->unsigned is nonnull || $field->default is nonnull) {
if ($field->unsigned is nonnull) {
$file_contents .= $indentation."\t\t\t\t" . ($field->unsigned ? 'true' : 'false') . ",\n";
} else {
$file_contents .= $indentation."\t\t\t\tnull,\n";
}
if ($field->default is nonnull) {
$file_contents .= $indentation."\t\t\t\t'{$field->default}',\n";
}
}
$file_contents .= $indentation."\t\t\t),\n";
}
$file_contents .= $indentation."\t\t],\n";

//
// Write out the indexes
//

$file_contents .= $indentation."\t\tvec[\n";
foreach ($table_schema->indexes as $index) {
$file_contents .= $indentation."\t\t\tnew Index(\n";
$file_contents .= $indentation."\t\t\t\t'{$index->name}',\n";
$file_contents .= $indentation."\t\t\t\t'{$index->type}',\n";
$fields = 'keyset[\''.\implode('\', \'', $index->fields).'\']';
$file_contents .= $indentation."\t\t\t\t{$fields},\n";
$file_contents .= $indentation."\t\t\t),\n";
}
$file_contents .= $indentation."\t\t],\n";
$file_contents .= $indentation."\t),\n";
}

$file_contents .= $indentation."],\n";
return $file_contents;
}

private static function varExportStringArray(Container<string> $array): string {
return C\is_empty($array) ? 'vec[]' : 'vec[\''.\HH\Lib\Str\join($array, '\', \'').'\']';
}
}
13 changes: 13 additions & 0 deletions src/Column.hack
@@ -0,0 +1,13 @@
namespace Slack\SQLFake;

final class Column {
public function __construct(
public string $name,
public DataType $type,
public int $length,
public bool $null,
public string $hack_type,
public ?bool $unsigned = null,
public ?string $default = null,
) {}
}

0 comments on commit 05703b0

Please sign in to comment.