@tai-kun/surrealdb
Version:
The SurrealDB SDK for JavaScript
137 lines (110 loc) • 3.54 kB
text/typescript
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,
});
}
}