UNPKG

@tai-kun/surrealdb

Version:

The SurrealDB SDK for JavaScript

137 lines (110 loc) 3.54 kB
import type { Encoded, Formatter } from "@tai-kun/surrealdb/formatter"; import type { SlotLike } from "@tai-kun/surrealdb/types"; import type { Writable } from "type-fest"; const passthrough = (v: unknown): any => v; export interface SlotOptions<TValue = any> { readonly parse?: ((value: unknown) => TValue) | undefined; readonly formatter?: Formatter | undefined; readonly defaultValue?: TValue | Encoded<TValue>; } export default class Slot< TName extends string = any, TRequired extends boolean = any, TValue = any, > implements SlotLike { protected readonly fmt?: Formatter; readonly _parse: (value: unknown) => TValue; readonly defaultValue?: TValue | Encoded<TValue>; constructor( readonly name: TName, readonly isRequired: TRequired, options: SlotOptions<TValue> = {}, ) { this._parse = options.parse || passthrough; if (options.formatter) { this.fmt = options.formatter; } if ("defaultValue" in options) { this.defaultValue = options.defaultValue; } } protected toOptions(): SlotOptions<TValue> { const options: Writable<SlotOptions> = { parse: this._parse, formatter: this.fmt, }; if ("defaultValue" in this) { options.defaultValue = this.defaultValue; } return options; } /** * @alias {@link type} */ as<TBind extends TValue>(): Slot<TName, true, TBind>; /** * @alias {@link type} */ as<TBind extends TValue>( parser: (value: unknown) => TBind, ): Slot<TName, true, TBind>; /** * @alias {@link type} */ as(parser: (value: unknown) => unknown = v => v): Slot<TName, true> { const This = this.constructor as typeof Slot; const options: Writable<SlotOptions> = { parse: parser }; if ("defaultValue" in this) { options.defaultValue = this.defaultValue; // TODO(tai-kun): ここでパースする必要あるか検討 (高コストだけどコンストラクター内でやる?) // options.defaultValue = parser(this.defaultValue); } return new This(this.name, true, options); } /** * @alias {@link as} */ type<TBind extends TValue>(): Slot<TName, true, TBind>; /** * @alias {@link as} */ type<TBind extends TValue>( parser: (value: unknown) => TBind, ): Slot<TName, true, TBind>; /** * @alias {@link as} */ type(parser: (value: unknown) => any = v => v): Slot<TName, true> { return this.as(parser); } rename<const TName extends string>( name: TName, ): Slot<TName, TRequired, TValue> { const This = this.constructor as typeof Slot; return new This(name, this.isRequired, this.toOptions()); } default(defaultValue: TValue): Slot<TName, false, TValue> { const This = this.constructor as typeof Slot; return new This(this.name, false, { parse: this._parse, formatter: this.fmt, defaultValue: this.fmt?.toEncoded?.(defaultValue) || defaultValue, // TODO(tai-kun): ここでパースする必要あるか検討 (高コストだけどコンストラクター内でやる?) // defaultValue: this._parse(defaultValue), }); } optional(): Slot<TName, false, TValue> { const This = this.constructor as typeof Slot; return new This(this.name, false, { parse: this._parse, formatter: this.fmt, }); } required(): Slot<TName, true, TValue> { const This = this.constructor as typeof Slot; return new This(this.name, true, { parse: this._parse, formatter: this.fmt, }); } }