Skip to content

Commit

Permalink
feat(cli): user types and route/path separation (#454)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: alvarius <89248902+alvrs@users.noreply.github.com>
  • Loading branch information
dk1a and alvrs authored Mar 8, 2023
1 parent fa921d4 commit 758bf03
Show file tree
Hide file tree
Showing 34 changed files with 1,044 additions and 132 deletions.
443 changes: 443 additions & 0 deletions packages/cli/contracts/src/tables/Table1.sol

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions packages/cli/contracts/src/types.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/* Autogenerated file. Do not edit manually. */

enum Enum1 {
E1,
E2,
E3
}

enum Enum2 {
E1
}
28 changes: 28 additions & 0 deletions packages/cli/contracts/test/Tablegen.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "forge-std/Test.sol";
import {StoreView} from "@latticexyz/store/src/StoreView.sol";
import {Table1, Table1Data} from "../src/tables/Table1.sol";
import {Enum1, Enum2} from "../src/types.sol";

contract TablegenTest is Test, StoreView {
function testTable1SetAndGet() public {
Table1.registerSchema();

uint256 k1 = 1;
int32 k2 = -1;
bytes16 k3 = hex"02";
address k4 = address(123);
bool k5 = true;
Enum1 k6 = Enum1.E3;
Enum2 k7 = Enum2.E1;

Table1.setV1(k1, k2, k3, k4, k5, k6, k7, 4);
assertEq(Table1.getV1(k1, k2, k3, k4, k5, k6, k7), 4);

Table1Data memory data = Table1Data(4, -5, hex"06", address(456), false, Enum1.E2, Enum2.E1);
Table1.set(k1, k2, k3, k4, k5, k6, k7, data);
assertEq(abi.encode(Table1.get(k1, k2, k3, k4, k5, k6, k7)), abi.encode(data));
}
}
7 changes: 4 additions & 3 deletions packages/cli/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ fuzz_runs = 256
optimizer = true
optimizer_runs = 3000
verbosity = 1
libs = ["../../node_modules", "../solecs", "../std-contracts"]
src = "src"
out = "out"
libs = ["../../node_modules", "../solecs", "../std-contracts", "../store"]
src = "contracts/src"
out = "contracts/out"
test = "contracts/test"
5 changes: 4 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@
},
"scripts": {
"prepare": "yarn build && chmod u+x git-install.sh",
"codegen": "ts-node --esm --files ./scripts/codegen.ts",
"lint": "eslint . --ext .ts",
"dev": "tsup --watch",
"build": "tsup",
"link": "yarn link",
"test": "vitest typecheck --run && echo 'todo: add tests'",
"test": "vitest typecheck --run && yarn test:contracts",
"test:contracts": "yarn codegen && forge test",
"git:install": "bash git-install.sh",
"release": "npm publish --access=public"
},
"devDependencies": {
"@latticexyz/store": "^1.34.0",
"@types/ejs": "^3.1.1",
"@types/glob": "^7.2.0",
"@types/node": "^17.0.34",
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ ds-test/=../../node_modules/ds-test/src/
forge-std/=../../node_modules/forge-std/src/
solmate/=../../node_modules/@rari-capital/solmate/src
std-contracts/=../../node_modules/@latticexyz/std-contracts/src/
solecs/=../../node_modules/@latticexyz/solecs/src/
solecs/=../../node_modules/@latticexyz/solecs/src/
@solidstate/=../../node_modules/@solidstate/
@latticexyz/=../../node_modules/@latticexyz/
55 changes: 55 additions & 0 deletions packages/cli/scripts/codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { SchemaType } from "@latticexyz/schema-type";
import { parseStoreConfig, StoreUserConfig } from "../src/config/index.js";
import { tablegen } from "../src/render-solidity/tablegen.js";
import { logError } from "../src/utils/errors.js";
import { getSrcDirectory } from "../src/utils/foundry.js";

// This config is used only for tests
const config: StoreUserConfig = {
tables: {
Table1: {
primaryKeys: {
k1: SchemaType.UINT256,
k2: SchemaType.INT32,
k3: SchemaType.BYTES16,
k4: SchemaType.ADDRESS,
k5: SchemaType.BOOL,
k6: "Enum1",
k7: "Enum2",
},
schema: {
v1: SchemaType.UINT256,
v2: SchemaType.INT32,
v3: SchemaType.BYTES16,
v4: SchemaType.ADDRESS,
v5: SchemaType.BOOL,
v6: "Enum1",
v7: "Enum2",
},
},
},

userTypes: {
enums: {
Enum1: ["E1", "E2", "E3"],
Enum2: ["E1"],
},
},
};

// Aside from avoiding `mud.config.mts` in cli package (could cause issues),
// this also tests that tablegen can work as a standalone function
const parsedConfig = await (async () => {
try {
return await parseStoreConfig(config);
} catch (error: unknown) {
logError(error);
}
})();

const srcDirectory = await getSrcDirectory();
if (parsedConfig !== undefined) {
tablegen(parsedConfig, srcDirectory);
} else {
process.exit(1);
}
20 changes: 3 additions & 17 deletions packages/cli/src/commands/tablegen.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import type { CommandModule } from "yargs";
import { mkdirSync, writeFileSync } from "fs";
import path from "path";
import { loadStoreConfig } from "../config/loadStoreConfig.js";
import { renderTablesFromConfig } from "../render-table/renderTablesFromConfig.js";
import { getSrcDirectory } from "../utils/foundry.js";
import { formatSolidity } from "../utils/format.js";
import { tablegen } from "../render-solidity/tablegen.js";

type Options = {
configPath?: string;
Expand All @@ -22,22 +19,11 @@ const commandModule: CommandModule<Options, Options> = {
},

async handler({ configPath }) {
const srcDir = await getSrcDirectory();
const srcDirectory = await getSrcDirectory();

const config = await loadStoreConfig(configPath);
const renderedTables = renderTablesFromConfig(config);

for (const { output, tableName } of renderedTables) {
const formattedOutput = await formatSolidity(output);

const tablePath = config.tables[tableName].route;
const outputDirectory = path.join(srcDir, tablePath);
mkdirSync(outputDirectory, { recursive: true });

const outputPath = path.join(outputDirectory, `${tableName}.sol`);
writeFileSync(outputPath, formattedOutput);
console.log(`Generated schema: ${outputPath}`);
}
tablegen(config, srcDirectory);

process.exit(0);
},
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/src/config/commonSchemas.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { getStaticByteLength, SchemaType } from "@latticexyz/schema-type";
import { z } from "zod";
import {
validateBaseRoute,
validateCapitalizedName,
validateDirectory,
validateEthereumAddress,
validateEnum,
validateName,
validateRoute,
validateSingleLevelRoute,
validateUncapitalizedName,
Expand All @@ -13,6 +16,10 @@ import {
export const ObjectName = z.string().superRefine(validateCapitalizedName);
/** Uncapitalized names of values, like keys and columns */
export const ValueName = z.string().superRefine(validateUncapitalizedName);
/** Name that can start with any case */
export const AnyCaseName = z.string().superRefine(validateName);
/** List of unique enum member names and 0 < length < 256 */
export const UserEnum = z.array(ObjectName).superRefine(validateEnum);

/** Ordinary routes */
export const OrdinaryRoute = z.string().superRefine(validateRoute);
Expand All @@ -26,3 +33,8 @@ export const Directory = z.string().superRefine(validateDirectory);

/** A valid Ethereum address */
export const EthereumAddress = z.string().superRefine(validateEthereumAddress);

/** Static subset of SchemaType enum */
export const StaticSchemaType = z
.nativeEnum(SchemaType)
.refine((arg) => getStaticByteLength(arg) > 0, "SchemaType must be static");
3 changes: 3 additions & 0 deletions packages/cli/src/config/parseStoreConfig.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ describe("StoreUserConfig", () => {
expectTypeOf<StoreUserConfig>().toEqualTypeOf<z.input<typeof StoreConfig>>();
// type equality isn't deep for optionals
expectTypeOf<StoreUserConfig["tables"][string]>().toEqualTypeOf<z.input<typeof StoreConfig>["tables"][string]>();
expectTypeOf<NonNullable<NonNullable<StoreUserConfig["userTypes"]>["enums"]>[string]>().toEqualTypeOf<
NonNullable<NonNullable<z.input<typeof StoreConfig>["userTypes"]>["enums"]>[string]
>();
// TODO If more nested schemas are added, provide separate tests for them
});
Loading

0 comments on commit 758bf03

Please sign in to comment.