From d296658f649f96de4c560b126f4954ba49394869 Mon Sep 17 00:00:00 2001 From: Madeorsk Date: Sun, 29 Jun 2025 15:59:52 +0200 Subject: [PATCH] Add model builder. --- src/model/builder.ts | 77 +++++++++++++++++++++++++++++++++++++++++++ src/model/index.ts | 4 +-- tests/builder.test.ts | 35 ++++++++++++++++++++ 3 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 src/model/builder.ts create mode 100644 tests/builder.test.ts diff --git a/src/model/builder.ts b/src/model/builder.ts new file mode 100644 index 0000000..6dd5447 --- /dev/null +++ b/src/model/builder.ts @@ -0,0 +1,77 @@ +import { + defineModel, + IdentifierDefinition, + ModelDefinition, + ModelShape, +} from "./model"; +import {ConstructorOf} from "../utils"; +import {Definition} from "./property-definition"; + +/** + * Model definition builder. + */ +export class ModelBuilder< + T extends object, + Shape extends ModelShape, + Identifier extends IdentifierDefinition, +> { + /** + * The built model definition. + */ + definition: ModelDefinition; + + /** + * Define a new property. + * @param name The new property name. + * @param definition The new property definition. + */ + property< + SerializedType, + PropertyName extends Exclude, + PropertyDefinition extends Definition, + >(name: PropertyName, definition: PropertyDefinition) { + (this.definition.properties[name] as Definition) = + definition; + + return this as unknown as ModelBuilder< + T, + Shape & {[k in PropertyName]: PropertyDefinition}, + Identifier + >; + } + + /** + * Set the model identifier. + * @param identifier The new model identifier. + */ + identifier>( + identifier: NewIdentifier, + ) { + (this.definition.identifier as unknown) = identifier; + return this as unknown as ModelBuilder; + } + + /** + * Define a model using the current model definition. + */ + define() { + return defineModel(this.definition); + } +} + +/** + * Initialize a model builder for the provided class. + * @param Class The class for which to build a model. + */ +export function newModel< + T extends object, + Shape extends ModelShape = object, + Identifier extends IdentifierDefinition = never, +>(Class: ConstructorOf): ModelBuilder { + const builder = new ModelBuilder(); + builder.definition = { + Class, + properties: {} as Shape, + }; + return builder; +} diff --git a/src/model/index.ts b/src/model/index.ts index a91c018..986894f 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -1,8 +1,8 @@ -import * as property from "./properties"; -export {property}; +export * as property from "./properties"; export * from "./model"; export {Definition} from "./property-definition"; +export {newModel, ModelBuilder} from "./builder"; export {ArrayType} from "./types/array"; export {BooleanType} from "./types/boolean"; diff --git a/tests/builder.test.ts b/tests/builder.test.ts new file mode 100644 index 0000000..b3df0d3 --- /dev/null +++ b/tests/builder.test.ts @@ -0,0 +1,35 @@ +import {describe, expect, test} from "vitest"; + +import {defineModel, newModel, s} from "../src/library"; + +class Testest { + foo: string; + bar: number; + baz: boolean; +} + +describe("model builder", () => { + const modelBuilder = newModel(Testest) + .property("foo", s.property.string()) + .property("bar", s.property.decimal()) + .property("baz", s.property.boolean()) + .identifier("foo"); + + test("build model definition", () => { + expect(modelBuilder.definition).toStrictEqual({ + Class: Testest, + identifier: "foo", + properties: { + foo: s.property.string(), + bar: s.property.decimal(), + baz: s.property.boolean(), + }, + }); + }); + + test("build a model", () => { + expect(modelBuilder.define()).toStrictEqual( + defineModel(modelBuilder.definition), + ); + }); +});