import { any, AnyShape, array, ArrayShape, AssertIsShape, binary, BinaryShape, boolean, BoolShape, EnumShape, literal, LiteralShape, map, MapShape, NothingShape, NumberShape, RequiredKeys, SetShape, Shape, ShapeGuards, string, StringShape, TimestampShape, union, UnionShape, Value } from '@punchcard/shape'; import { Type, TypeClass, TypeShape} from '@punchcard/shape/lib/type'; // tslint:disable: ban-types export namespace AttributeValue { export type Tag = typeof Tag; export const Tag = Symbol.for('@punchcard/shape-dynamodb.AttributeValue.Tag'); export interface ShapeOfProps { /** * Will represent all sets as lists. * @default false */ setAsList?: boolean; } export type ShapeOf = T extends StringShape | TimestampShape ? typeof AttributeValue.String : T extends EnumShape ? AttributeValue.Enum : T extends NumberShape ? typeof AttributeValue.Number : T extends BoolShape ? typeof AttributeValue.Bool : T extends BinaryShape ? typeof AttributeValue.Binary : T extends NothingShape ? typeof AttributeValue.Nothing : T extends AnyShape ? AnyShape : T extends ArrayShape ? AttributeValue.List> : T extends MapShape ? AttributeValue.Map> : T extends SetShape ? Props['setAsList'] extends true ? AttributeValue.List> : I extends BinaryShape ? typeof AttributeValue.BinarySet : I extends StringShape ? typeof AttributeValue.StringSet : I extends NumberShape ? typeof AttributeValue.NumberSet : AttributeValue.List> : T extends TypeShape ? AttributeValue.Struct<{ [field in keyof Fields]: ShapeOf }> : T extends UnionShape ? UnionShape<{ [i in keyof I]: ShapeOf, Props> }> : T extends LiteralShape ? { [i in keyof I]: ShapeOf }[keyof I] : T extends { [Tag]: infer T2 } ? T2 : AnyShape ; /** * Derives a shape representing `T` in DynamoDB JSON. * * @param shape to derive a DynamoDB Shape for. */ export function shapeOf(shape: T, props?: Props): ShapeOf { (props as any) = props || { setAsList: false }; if (ShapeGuards.isStringShape(shape) || ShapeGuards.isTimestampShape(shape)) { return AttributeValue.String as ShapeOf; } else if (ShapeGuards.isNumberShape(shape)) { return AttributeValue.Number as ShapeOf; } else if (ShapeGuards.isBoolShape(shape)) { return AttributeValue.Bool as ShapeOf; } else if (ShapeGuards.isBinaryShape(shape)) { return AttributeValue.Binary as ShapeOf; } else if (ShapeGuards.isNothingShape(shape)) { return AttributeValue.Nothing as ShapeOf; } else if (ShapeGuards.isAnyShape(shape)) { return shape as ShapeOf; } else if (ShapeGuards.isArrayShape(shape)) { return AttributeValue.List(shapeOf(shape.Items, props)) as ShapeOf; } else if (ShapeGuards.isMapShape(shape)) { return AttributeValue.Map(shapeOf(shape.Items, props)) as ShapeOf; } else if (ShapeGuards.isSetShape(shape)) { if (props?.setAsList === true) { return AttributeValue.List(shapeOf(shape.Items, props)) as ShapeOf; } else if (ShapeGuards.isStringShape(shape.Items)) { return AttributeValue.StringSet as ShapeOf; } else if (ShapeGuards.isNumberShape(shape.Items)) { return AttributeValue.NumberSet as ShapeOf; } else if (ShapeGuards.isBinaryShape(shape.Items)) { return AttributeValue.BinarySet as ShapeOf; } else { return AttributeValue.List(shapeOf(shape.Items, props)) as ShapeOf; } } else if (ShapeGuards.isRecordShape(shape)) { return AttributeValue.Struct(Object.entries(shape.Members).map(([name, field]) => ({ [name]: shapeOf(field) })).reduce((a, b) => ({...a,...b}))) as ShapeOf; } else if (ShapeGuards.isUnionShape(shape)) { return union(...shape.Items.map(item => shapeOf(item))) as ShapeOf; } return any as ShapeOf; } } export type AttributeValue = ( | AnyShape | typeof AttributeValue.Nothing | typeof AttributeValue.Binary | typeof AttributeValue.BinarySet | typeof AttributeValue.Bool | typeof AttributeValue.Number | typeof AttributeValue.NumberSet | typeof AttributeValue.String | typeof AttributeValue.StringSet | AttributeValue.List | AttributeValue.Map | AttributeValue.Struct ); export namespace AttributeValue { export class Nothing extends Type({ NULL: literal(boolean, true as const) }) {} export class Binary extends Type({ B: binary }) {} export class BinarySet extends Type({ BS: array(binary) }) {} export class Bool extends Type({ BOOL: boolean }) {} export class Number extends Type({ N: string }) {} export class NumberSet extends Type({ NS: array(string) }) {} export class String extends Type({ S: string }) {} export interface Enum { S: E } export const Enum = (e: E) => Type({ S: e }); export class StringSet extends Type({ SS: array(string) }) {} export interface List extends TypeShape<{ L: ArrayShape; }> {} export const List = (item: T): List => Type({ L: array(item) }) as any as List; export interface Map extends TypeClass<{ M: MapShape; }> {} export const Map = (item: T): Map => Type({ M: map(item) }) as any as Map; export interface StructFields { [fieldName: string]: AttributeValue; } export interface Struct extends TypeClass<{ M: TypeShape; }> {} export const Struct = (fields: F): Struct => Type({ M: Type(fields) }); }