diff --git a/examples/quickstart/client/src/module_bindings/message.ts b/examples/quickstart/client/src/module_bindings/message.ts index 93f04fb..56e9db0 100644 --- a/examples/quickstart/client/src/module_bindings/message.ts +++ b/examples/quickstart/client/src/module_bindings/message.ts @@ -6,7 +6,6 @@ import { __SPACETIMEDB__, AlgebraicType, ProductType, - BuiltinType, ProductTypeElement, SumType, SumTypeVariant, @@ -41,19 +40,17 @@ export class Message extends IDatabaseTable { AlgebraicType.createProductType([ new ProductTypeElement( "__identity_bytes", - AlgebraicType.createArrayType( - AlgebraicType.createPrimitiveType(BuiltinType.Type.U8) - ) + AlgebraicType.createBytesType() ), ]) ), new ProductTypeElement( "sent", - AlgebraicType.createPrimitiveType(BuiltinType.Type.U64) + AlgebraicType.createU64Type() ), new ProductTypeElement( "text", - AlgebraicType.createPrimitiveType(BuiltinType.Type.String) + AlgebraicType.createStringType() ), ]); } diff --git a/examples/quickstart/client/src/module_bindings/send_message_reducer.ts b/examples/quickstart/client/src/module_bindings/send_message_reducer.ts index 1ba25b3..02c8336 100644 --- a/examples/quickstart/client/src/module_bindings/send_message_reducer.ts +++ b/examples/quickstart/client/src/module_bindings/send_message_reducer.ts @@ -6,7 +6,6 @@ import { __SPACETIMEDB__, AlgebraicType, ProductType, - BuiltinType, ProductTypeElement, IDatabaseTable, AlgebraicValue, @@ -22,16 +21,14 @@ export class SendMessageReducer { public static call(_text: string) { if (__SPACETIMEDB__.spacetimeDBClient) { const serializer = __SPACETIMEDB__.spacetimeDBClient.getSerializer(); - let _textType = AlgebraicType.createPrimitiveType( - BuiltinType.Type.String - ); + let _textType = AlgebraicType.createStringType(); serializer.write(_textType, _text); __SPACETIMEDB__.spacetimeDBClient.call("send_message", serializer); } } public static deserializeArgs(adapter: ReducerArgsAdapter): any[] { - let textType = AlgebraicType.createPrimitiveType(BuiltinType.Type.String); + let textType = AlgebraicType.createStringType(); let textValue = AlgebraicValue.deserialize(textType, adapter.next()); let text = textValue.asString(); return [text]; diff --git a/examples/quickstart/client/src/module_bindings/set_name_reducer.ts b/examples/quickstart/client/src/module_bindings/set_name_reducer.ts index 6bc900e..fc03a1c 100644 --- a/examples/quickstart/client/src/module_bindings/set_name_reducer.ts +++ b/examples/quickstart/client/src/module_bindings/set_name_reducer.ts @@ -6,7 +6,6 @@ import { __SPACETIMEDB__, AlgebraicType, ProductType, - BuiltinType, ProductTypeElement, IDatabaseTable, AlgebraicValue, @@ -22,16 +21,14 @@ export class SetNameReducer { public static call(_name: string) { if (__SPACETIMEDB__.spacetimeDBClient) { const serializer = __SPACETIMEDB__.spacetimeDBClient.getSerializer(); - let _nameType = AlgebraicType.createPrimitiveType( - BuiltinType.Type.String - ); + let _nameType = AlgebraicType.createStringType(); serializer.write(_nameType, _name); __SPACETIMEDB__.spacetimeDBClient.call("set_name", serializer); } } public static deserializeArgs(adapter: ReducerArgsAdapter): any[] { - let nameType = AlgebraicType.createPrimitiveType(BuiltinType.Type.String); + let nameType = AlgebraicType.createStringType(); let nameValue = AlgebraicValue.deserialize(nameType, adapter.next()); let name = nameValue.asString(); return [name]; diff --git a/examples/quickstart/client/src/module_bindings/user.ts b/examples/quickstart/client/src/module_bindings/user.ts index 3557b71..f54be41 100644 --- a/examples/quickstart/client/src/module_bindings/user.ts +++ b/examples/quickstart/client/src/module_bindings/user.ts @@ -6,7 +6,6 @@ import { __SPACETIMEDB__, AlgebraicType, ProductType, - BuiltinType, ProductTypeElement, SumType, SumTypeVariant, @@ -47,9 +46,7 @@ export class User extends IDatabaseTable { AlgebraicType.createProductType([ new ProductTypeElement( "__identity_bytes", - AlgebraicType.createArrayType( - AlgebraicType.createPrimitiveType(BuiltinType.Type.U8) - ) + AlgebraicType.createBytesType() ), ]) ), @@ -58,14 +55,14 @@ export class User extends IDatabaseTable { AlgebraicType.createSumType([ new SumTypeVariant( "some", - AlgebraicType.createPrimitiveType(BuiltinType.Type.String) + AlgebraicType.createStringType() ), new SumTypeVariant("none", AlgebraicType.createProductType([])), ]) ), new ProductTypeElement( "online", - AlgebraicType.createPrimitiveType(BuiltinType.Type.Bool) + AlgebraicType.createBoolType() ), ]); } diff --git a/src/algebraic_type.ts b/src/algebraic_type.ts index 0c93a85..5a1ca1d 100644 --- a/src/algebraic_type.ts +++ b/src/algebraic_type.ts @@ -1,6 +1,6 @@ /** * A variant of a sum type. - * + * * NOTE: Each element has an implicit element tag based on its order. * Uniquely identifies an element similarly to protobuf tags. */ @@ -15,29 +15,29 @@ export class SumTypeVariant { } /** -* Unlike most languages, sums in SATS are *[structural]* and not nominal. -* When checking whether two nominal types are the same, -* their names and/or declaration sites (e.g., module / namespace) are considered. -* Meanwhile, a structural type system would only check the structure of the type itself, -* e.g., the names of its variants and their inner data types in the case of a sum. -* -* This is also known as a discriminated union (implementation) or disjoint union. -* Another name is [coproduct (category theory)](https://ncatlab.org/nlab/show/coproduct). -* -* These structures are known as sum types because the number of possible values a sum -* ```ignore -* { N_0(T_0), N_1(T_1), ..., N_n(T_n) } -* ``` -* is: -* ```ignore -* Σ (i ∈ 0..n). values(T_i) -* ``` -* so for example, `values({ A(U64), B(Bool) }) = values(U64) + values(Bool)`. -* -* See also: https://ncatlab.org/nlab/show/sum+type. -* -* [structural]: https://en.wikipedia.org/wiki/Structural_type_system -*/ + * Unlike most languages, sums in SATS are *[structural]* and not nominal. + * When checking whether two nominal types are the same, + * their names and/or declaration sites (e.g., module / namespace) are considered. + * Meanwhile, a structural type system would only check the structure of the type itself, + * e.g., the names of its variants and their inner data types in the case of a sum. + * + * This is also known as a discriminated union (implementation) or disjoint union. + * Another name is [coproduct (category theory)](https://ncatlab.org/nlab/show/coproduct). + * + * These structures are known as sum types because the number of possible values a sum + * ```ignore + * { N_0(T_0), N_1(T_1), ..., N_n(T_n) } + * ``` + * is: + * ```ignore + * Σ (i ∈ 0..n). values(T_i) + * ``` + * so for example, `values({ A(U64), B(Bool) }) = values(U64) + values(Bool)`. + * + * See also: https://ncatlab.org/nlab/show/sum+type. + * + * [structural]: https://en.wikipedia.org/wiki/Structural_type_system + */ export class SumType { public variants: SumTypeVariant[]; @@ -46,14 +46,14 @@ export class SumType { } } -/** -* A factor / element of a product type. -* -* An element consist of an optional name and a type. -* -* NOTE: Each element has an implicit element tag based on its order. -* Uniquely identifies an element similarly to protobuf tags. -*/ +/** + * A factor / element of a product type. + * + * An element consist of an optional name and a type. + * + * NOTE: Each element has an implicit element tag based on its order. + * Uniquely identifies an element similarly to protobuf tags. + */ export class ProductTypeElement { public name: string; public algebraicType: AlgebraicType; @@ -65,30 +65,30 @@ export class ProductTypeElement { } /** -* A structural product type of the factors given by `elements`. -* -* This is also known as `struct` and `tuple` in many languages, -* but note that unlike most languages, products in SATs are *[structural]* and not nominal. -* When checking whether two nominal types are the same, -* their names and/or declaration sites (e.g., module / namespace) are considered. -* Meanwhile, a structural type system would only check the structure of the type itself, -* e.g., the names of its fields and their types in the case of a record. -* The name "product" comes from category theory. -* -* See also: https://ncatlab.org/nlab/show/product+type. -* -* These structures are known as product types because the number of possible values in product -* ```ignore -* { N_0: T_0, N_1: T_1, ..., N_n: T_n } -* ``` -* is: -* ```ignore -* Π (i ∈ 0..n). values(T_i) -* ``` -* so for example, `values({ A: U64, B: Bool }) = values(U64) * values(Bool)`. -* -* [structural]: https://en.wikipedia.org/wiki/Structural_type_system -*/ + * A structural product type of the factors given by `elements`. + * + * This is also known as `struct` and `tuple` in many languages, + * but note that unlike most languages, products in SATs are *[structural]* and not nominal. + * When checking whether two nominal types are the same, + * their names and/or declaration sites (e.g., module / namespace) are considered. + * Meanwhile, a structural type system would only check the structure of the type itself, + * e.g., the names of its fields and their types in the case of a record. + * The name "product" comes from category theory. + * + * See also: https://ncatlab.org/nlab/show/product+type. + * + * These structures are known as product types because the number of possible values in product + * ```ignore + * { N_0: T_0, N_1: T_1, ..., N_n: T_n } + * ``` + * is: + * ```ignore + * Π (i ∈ 0..n). values(T_i) + * ``` + * so for example, `values({ A: U64, B: Bool }) = values(U64) * values(Bool)`. + * + * [structural]: https://en.wikipedia.org/wiki/Structural_type_system + */ export class ProductType { public elements: ProductTypeElement[]; @@ -112,113 +112,75 @@ export class MapType { } } -export class BuiltinType { - public type: BuiltinType.Type; - public arrayType: AlgebraicType | undefined; - public mapType: MapType | undefined; - - constructor( - type: BuiltinType.Type, - arrayOrMapType: AlgebraicType | MapType | undefined - ) { - this.type = type; - if (arrayOrMapType !== undefined) { - if (arrayOrMapType.constructor === MapType) { - this.mapType = arrayOrMapType; - } else if (arrayOrMapType.constructor === AlgebraicType) { - this.arrayType = arrayOrMapType; - } - } - } -} - -// exporting BuiltinType as a namespace as well as a class allows to add -// export types on the namespace, so we can use BuiltinType.Type -/* -* Represents the built-in types in SATS. -* -* Some of these types are nominal in our otherwise structural type system. -*/ -export namespace BuiltinType { - export enum Type { - Bool = "Bool", - I8 = "I8", - U8 = "U8", - I16 = "I16", - U16 = "U16", - I32 = "I32", - U32 = "U32", - I64 = "I64", - U64 = "U64", - I128 = "I128", - U128 = "U128", - F32 = "F32", - F64 = "F64", - /** UTF-8 encoded */ - String = "String", - /** This is a SATS `ArrayType` - * - * An array type is a **homogeneous** product type of dynamic length. - * - * That is, it is a product type - * where every element / factor / field is of the same type - * and where the length is statically unknown. - */ - Array = "Array", - /** This is a SATS `MapType` */ - Map = "Map", - } -} - +type ArrayBaseType = AlgebraicType; type TypeRef = null; type None = null; -export type EnumLabel = { label: string }; -type AnyType = ProductType | SumType | BuiltinType | EnumLabel | TypeRef | None; +type AnyType = ProductType | SumType | ArrayBaseType | MapType | TypeRef | None; /** -* The SpacetimeDB Algebraic Type System (SATS) is a structural type system in -* which a nominal type system can be constructed. -* -* The type system unifies the concepts sum types, product types, and built-in -* primitive types into a single type system. -*/ + * The SpacetimeDB Algebraic Type System (SATS) is a structural type system in + * which a nominal type system can be constructed. + * + * The type system unifies the concepts sum types, product types, and built-in + * primitive types into a single type system. + */ export class AlgebraicType { type!: Type; type_?: AnyType; + private setter(type: Type, payload: AnyType | undefined) { + this.type_ = payload; + this.type = payload === undefined ? Type.None : type; + } + public get product(): ProductType { - if (this.type !== Type.ProductType) { + if (this.type !== Type.Product) { throw "product type was requested, but the type is not ProductType"; } return this.type_ as ProductType; } - public set product(value: ProductType | undefined) { - this.type_ = value; - this.type = value == undefined ? Type.None : Type.ProductType; + this.setter(Type.Product, value); } public get sum(): SumType { - if (this.type !== Type.SumType) { + if (this.type !== Type.Sum) { throw "sum type was requested, but the type is not SumType"; } return this.type_ as SumType; } public set sum(value: SumType | undefined) { - this.type_ = value; - this.type = value == undefined ? Type.None : Type.SumType; + this.setter(Type.Sum, value); } - public get builtin(): BuiltinType { - if (this.type !== Type.BuiltinType) { - throw "builtin type was requested, but the type is not BuiltinType"; + public get array(): AlgebraicType { + if (this.type !== Type.Array) { + throw "array type was requested, but the type is not Array"; } - return this.type_ as BuiltinType; + return this.type_ as AlgebraicType; } - public set builtin(value: BuiltinType | undefined) { - this.type_ = value; - this.type = value == undefined ? Type.None : Type.BuiltinType; + public set array(value: AlgebraicType | undefined) { + this.setter(Type.Array, value); + } + + public get map(): MapType { + if (this.type !== Type.Map) { + throw "map type was requested, but the type is not MapType"; + } + return this.type_ as MapType; + } + public set map(value: MapType | undefined) { + this.setter(Type.Map, value); + } + + private static createType( + type: Type, + payload: AnyType | undefined + ): AlgebraicType { + let at = new AlgebraicType(); + at.setter(type, payload); + return at; } public static createProductType( @@ -229,38 +191,120 @@ export class AlgebraicType { return type; } - public static createArrayType(elementType: AlgebraicType) { + public static createSumType(variants: SumTypeVariant[]): AlgebraicType { let type = new AlgebraicType(); - type.builtin = new BuiltinType(BuiltinType.Type.Array, elementType); + type.sum = new SumType(variants); return type; } - public static createSumType(variants: SumTypeVariant[]): AlgebraicType { + public static createArrayType(elementType: AlgebraicType): AlgebraicType { let type = new AlgebraicType(); - type.sum = new SumType(variants); + type.array = elementType; return type; } - public static createPrimitiveType(type: BuiltinType.Type) { - let algebraicType = new AlgebraicType(); - algebraicType.builtin = new BuiltinType(type, undefined); - return algebraicType; + public static createMapType( + key: AlgebraicType, + val: AlgebraicType + ): AlgebraicType { + let type = new AlgebraicType(); + type.map = new MapType(key, val); + return type; + } + + public static createBoolType(): AlgebraicType { + return this.createType(Type.Bool, null); + } + public static createI8Type(): AlgebraicType { + return this.createType(Type.I8, null); + } + public static createU8Type(): AlgebraicType { + return this.createType(Type.U8, null); + } + public static createI16Type(): AlgebraicType { + return this.createType(Type.I16, null); + } + public static createU16Type(): AlgebraicType { + return this.createType(Type.U16, null); + } + public static createI32Type(): AlgebraicType { + return this.createType(Type.I32, null); + } + public static createU32Type(): AlgebraicType { + return this.createType(Type.U32, null); + } + public static createI64Type(): AlgebraicType { + return this.createType(Type.I64, null); + } + public static createU64Type(): AlgebraicType { + return this.createType(Type.U64, null); + } + public static createI128Type(): AlgebraicType { + return this.createType(Type.I128, null); + } + public static createU128Type(): AlgebraicType { + return this.createType(Type.U128, null); + } + public static createF32Type(): AlgebraicType { + return this.createType(Type.F32, null); + } + public static createF64Type(): AlgebraicType { + return this.createType(Type.F64, null); + } + public static createStringType(): AlgebraicType { + return this.createType(Type.String, null); + } + public static createBytesType(): AlgebraicType { + return this.createArrayType(this.createU8Type()); } public isProductType(): boolean { - return this.type === Type.ProductType; + return this.type === Type.Product; } public isSumType(): boolean { - return this.type === Type.SumType; + return this.type === Type.Sum; + } + + public isArrayType(): boolean { + return this.type === Type.Array; + } + + public isMapType(): boolean { + return this.type === Type.Map; } } export namespace AlgebraicType { export enum Type { - SumType = "SumType", - ProductType = "ProductType", - BuiltinType = "BuiltinType", + Sum = "SumType", + Product = "ProductType", + /** This is a SATS `ArrayType` + * + * An array type is a **homogeneous** product type of dynamic length. + * + * That is, it is a product type + * where every element / factor / field is of the same type + * and where the length is statically unknown. + */ + Array = "Array", + /** This is a SATS `MapType` */ + Map = "Map", + Bool = "Bool", + I8 = "I8", + U8 = "U8", + I16 = "I16", + U16 = "U16", + I32 = "I32", + U32 = "U32", + I64 = "I64", + U64 = "U64", + I128 = "I128", + U128 = "U128", + F32 = "F32", + F64 = "F64", + /** UTF-8 encoded */ + String = "String", None = "None", } } diff --git a/src/algebraic_value.ts b/src/algebraic_value.ts index cd2cb99..33edcf6 100644 --- a/src/algebraic_value.ts +++ b/src/algebraic_value.ts @@ -1,11 +1,4 @@ -import { - ProductType, - SumType, - AlgebraicType, - BuiltinType, - // EnumLabel, - MapType, -} from "./algebraic_type"; +import { ProductType, SumType, AlgebraicType, MapType } from "./algebraic_type"; import BinaryReader from "./binary_reader"; export interface ReducerArgsAdapter { @@ -43,20 +36,14 @@ export class BinaryReducerArgsAdapter { } } -/** Defines the interface for deserialize `AlgebraicValue`s*/ +/** Defines the interface for deserializing `AlgebraicValue`s*/ export interface ValueAdapter { - readUInt8Array: () => Uint8Array; - readArray: (type: AlgebraicType) => AlgebraicValue[]; - readMap: ( - keyType: AlgebraicType, - valueType: AlgebraicType - ) => Map; - readString: () => string; readSum: (type: SumType) => SumValue; readProduct: (type: ProductType) => ProductValue; - + readUInt8Array: () => Uint8Array; + readArray: (type: AlgebraicType) => AlgebraicValue[]; + readMap: (keyType: AlgebraicType, valueType: AlgebraicType) => MapValue; readBool: () => boolean; - readByte: () => number; readI8: () => number; readU8: () => number; readI16: () => number; @@ -69,6 +56,8 @@ export interface ValueAdapter { readI128: () => BigInt; readF32: () => number; readF64: () => number; + readString: () => string; + readByte: () => number; callMethod(methodName: K): any; } @@ -305,10 +294,10 @@ export class JSONAdapter implements ValueAdapter { export class SumValue { /** A tag representing the choice of one variant of the sum type's variants. */ public tag: number; - /** - * Given a variant `Var(Ty)` in a sum type `{ Var(Ty), ... }`, - * this provides the `value` for `Ty`. - */ + /** + * Given a variant `Var(Ty)` in a sum type `{ Var(Ty), ... }`, + * this provides the `value` for `Ty`. + */ public value: AlgebraicValue; constructor(tag: number, value: AlgebraicValue) { @@ -329,12 +318,12 @@ export class SumValue { } } -/** -* A product value is made of a list of -* "elements" / "fields" / "factors" of other `AlgebraicValue`s. -* -* The type of product value is a [product type](`ProductType`). -*/ +/** + * A product value is made of a list of + * "elements" / "fields" / "factors" of other `AlgebraicValue`s. + * + * The type of product value is a [product type](`ProductType`). + */ export class ProductValue { elements: AlgebraicValue[]; @@ -354,122 +343,29 @@ export class ProductValue { } } -/** A built-in value of a [`BuiltinType`]. */ -type BuiltinValueType = +type MapValue = Map; + +type AnyValue = + | SumValue + | ProductValue | boolean | string | number | AlgebraicValue[] | BigInt - | Map + | MapValue | Uint8Array; -export class BuiltinValue { - value: BuiltinValueType; - - constructor(value: BuiltinValueType) { - this.value = value; - } - - public static deserialize( - type: BuiltinType, - adapter: ValueAdapter - ): BuiltinValue { - switch (type.type) { - case BuiltinType.Type.Array: - let arrayBuiltinType: BuiltinType.Type | undefined = - type.arrayType && - type.arrayType.type === AlgebraicType.Type.BuiltinType - ? type.arrayType.builtin.type - : undefined; - if ( - arrayBuiltinType !== undefined && - arrayBuiltinType === BuiltinType.Type.U8 - ) { - const value = adapter.readUInt8Array(); - return new this(value); - } else { - const arrayResult = adapter.readArray( - type.arrayType as AlgebraicType - ); - return new this(arrayResult); - } - case BuiltinType.Type.Map: - let keyType: AlgebraicType = (type.mapType as MapType).keyType; - let valueType: AlgebraicType = (type.mapType as MapType).valueType; - const mapResult = adapter.readMap(keyType, valueType); - return new this(mapResult); - case BuiltinType.Type.String: - const result = adapter.readString(); - return new this(result); - default: - const methodName: string = "read" + type.type; - return new this(adapter.callMethod(methodName as keyof ValueAdapter)); - } - } - - public asString(): string { - return this.value as string; - } - - public asArray(): AlgebraicValue[] { - return this.value as AlgebraicValue[]; - } - - public asJsArray(type: string): any[] { - return this.asArray().map((el) => - el.callMethod(("as" + type) as keyof AlgebraicValue) - ); - } - - public asNumber(): number { - return this.value as number; - } - - public asBool(): boolean { - return this.value as boolean; - } - - public asBigInt(): BigInt { - return this.value as BigInt; - } - - public asBoolean(): boolean { - return this.value as boolean; - } - - public asBytes(): Uint8Array { - return this.value as Uint8Array; - } -} - -type AnyValue = SumValue | ProductValue | BuiltinValue; - /** A value in SATS. */ export class AlgebraicValue { - /** A structural sum value. */ - sum: SumValue | undefined; - /** A structural product value. */ - product: ProductValue | undefined; - /** A builtin value that has a builtin type */ - builtin: BuiltinValue | undefined; + value: AnyValue; constructor(value: AnyValue | undefined) { if (value === undefined) { // TODO: possibly get rid of it throw "value is undefined"; } - switch (value.constructor) { - case SumValue: - this.sum = value as SumValue; - break; - case ProductValue: - this.product = value as ProductValue; - break; - case BuiltinValue: - this.builtin = value as BuiltinValue; - break; - } + this.value = value; } callMethod(methodName: K): any { @@ -478,75 +374,88 @@ export class AlgebraicValue { public static deserialize(type: AlgebraicType, adapter: ValueAdapter) { switch (type.type) { - case AlgebraicType.Type.ProductType: - return new this(ProductValue.deserialize(type.product, adapter)); - case AlgebraicType.Type.SumType: + case AlgebraicType.Type.Sum: return new this(SumValue.deserialize(type.sum, adapter)); - case AlgebraicType.Type.BuiltinType: - return new this(BuiltinValue.deserialize(type.builtin, adapter)); + case AlgebraicType.Type.Product: + return new this(ProductValue.deserialize(type.product, adapter)); + case AlgebraicType.Type.Array: + let elemType = type.array; + if (elemType.type === AlgebraicType.Type.U8) { + return new this(adapter.readUInt8Array()); + } else { + return new this(adapter.readArray(elemType)); + } + case AlgebraicType.Type.Map: + let mapType = type.map; + return new this(adapter.readMap(mapType.keyType, mapType.valueType)); + case AlgebraicType.Type.Bool: + return new this(adapter.readBool()); + case AlgebraicType.Type.I8: + return new this(adapter.readI8()); + case AlgebraicType.Type.U8: + return new this(adapter.readU8()); + case AlgebraicType.Type.I16: + return new this(adapter.readI16()); + case AlgebraicType.Type.U16: + return new this(adapter.readU16()); + case AlgebraicType.Type.I32: + return new this(adapter.readI32()); + case AlgebraicType.Type.U32: + return new this(adapter.readU32()); + case AlgebraicType.Type.I64: + return new this(adapter.readI64()); + case AlgebraicType.Type.U64: + return new this(adapter.readU64()); + case AlgebraicType.Type.I128: + return new this(adapter.readI128()); + case AlgebraicType.Type.U128: + return new this(adapter.readU128()); + case AlgebraicType.Type.String: + return new this(adapter.readString()); default: - throw new Error("not implemented"); - } - } - - public asProductValue(): ProductValue { - if (!this.product) { - throw "AlgebraicValue is not a ProductValue and product was requested"; + throw new Error(`not implemented, ${type.type}`); } - return this.product as ProductValue; - } - - public asBuiltinValue(): BuiltinValue { - this.assertBuiltin(); - return this.builtin as BuiltinValue; } public asSumValue(): SumValue { - if (!this.sum) { - throw "AlgebraicValue is not a SumValue and a sum value was requested"; - } + return this.value as SumValue; + } - return this.sum as SumValue; + public asProductValue(): ProductValue { + return this.value as ProductValue; } public asArray(): AlgebraicValue[] { - this.assertBuiltin(); - return (this.builtin as BuiltinValue).asArray(); + return this.value as AlgebraicValue[]; } - public asString(): string { - this.assertBuiltin(); - return (this.builtin as BuiltinValue).asString(); + public asMap(): MapValue { + return this.value as MapValue; } - public asNumber(): number { - this.assertBuiltin(); - return (this.builtin as BuiltinValue).asNumber(); + public asBool(): boolean { + return this.value as boolean; } - public asBool(): boolean { - this.assertBuiltin(); - return (this.builtin as BuiltinValue).asBool(); + public asNumber(): number { + return this.value as number; } public asBigInt(): BigInt { - this.assertBuiltin(); - return (this.builtin as BuiltinValue).asBigInt(); + return this.value as BigInt; } - public asBoolean(): boolean { - this.assertBuiltin(); - return (this.builtin as BuiltinValue).asBool(); + public asString(): string { + return this.value as string; } public asBytes(): Uint8Array { - this.assertBuiltin(); - return (this.builtin as BuiltinValue).asBytes(); + return this.value as Uint8Array; } - private assertBuiltin() { - if (!this.builtin) { - throw "AlgebraicValue is not a BuiltinValue and a string was requested"; - } + public asJsArray(type: string): any[] { + return this.asArray().map((el) => + el.callMethod(("as" + type) as keyof AlgebraicValue) + ); } } diff --git a/src/serializer.ts b/src/serializer.ts index 1c2c487..0bcb4e9 100644 --- a/src/serializer.ts +++ b/src/serializer.ts @@ -1,4 +1,4 @@ -import { AlgebraicType, BuiltinType } from "./algebraic_type"; +import { AlgebraicType } from "./algebraic_type"; import BinaryWriter from "./binary_writer"; import { Identity } from "./identity"; @@ -19,28 +19,10 @@ export class JSONSerializer { return this.content; } - serializeBuiltinType(type: BuiltinType, value: any): any { - switch (type.type) { - case BuiltinType.Type.Array: - const returnArray: any[] = []; - for (const element of value) { - returnArray.push( - this.serializeType(type.arrayType as AlgebraicType, element) - ); - } - return returnArray; - case BuiltinType.Type.Map: - break; - default: - return value; - } - } serializeType(type: AlgebraicType, value: any): any { switch (type.type) { - case AlgebraicType.Type.BuiltinType: - return this.serializeBuiltinType(type.builtin, value); - case AlgebraicType.Type.ProductType: + case AlgebraicType.Type.Product: let serializedArray: any[] = []; for (const element of type.product.elements) { let serialized: any; @@ -59,23 +41,32 @@ export class JSONSerializer { serializedArray.push(serialized); } return serializedArray; - case AlgebraicType.Type.SumType: + case AlgebraicType.Type.Sum: + let sum = type.sum; if ( - type.sum.variants.length == 2 && - type.sum.variants[0].name === "some" && - type.sum.variants[1].name === "none" + sum.variants.length == 2 && + sum.variants[0].name === "some" && + sum.variants[1].name === "none" ) { return value; } else { - const variant = type.sum.variants.find((v) => v.name === value.tag); + const variant = sum.variants.find((v) => v.name === value.tag); if (!variant) { throw `Can't serialize a sum type, couldn't find ${value.tag} tag`; } return this.serializeType(variant.algebraicType, value.value); } - default: + case AlgebraicType.Type.Array: + const returnArray: any[] = []; + for (const element of value) { + returnArray.push(this.serializeType(type.array, element)); + } + return returnArray; + case AlgebraicType.Type.Map: break; + default: + return value; } } @@ -102,10 +93,7 @@ export class BinarySerializer { write(type: AlgebraicType, value: any) { switch (type.type) { - case AlgebraicType.Type.BuiltinType: - this.writeBuiltinType(type.builtin, value); - break; - case AlgebraicType.Type.ProductType: + case AlgebraicType.Type.Product: for (const element of type.product.elements) { this.write( element.algebraicType, @@ -117,88 +105,82 @@ export class BinarySerializer { ); } break; - case AlgebraicType.Type.SumType: + case AlgebraicType.Type.Sum: + let sum = type.sum; if ( - type.sum.variants.length == 2 && - type.sum.variants[0].name === "some" && - type.sum.variants[1].name === "none" + sum.variants.length == 2 && + sum.variants[0].name === "some" && + sum.variants[1].name === "none" ) { if (value) { this.writeByte(0); - this.write(type.sum.variants[0].algebraicType, value); + this.write(sum.variants[0].algebraicType, value); } else { this.writeByte(1); } } else { - const index = type.sum.variants.findIndex( - (v) => v.name === value.tag - ); + const index = sum.variants.findIndex((v) => v.name === value.tag); if (index < 0) { throw `Can't serialize a sum type, couldn't find ${value.tag} tag`; } this.writeByte(index); - this.write(type.sum.variants[index].algebraicType, value.value); + this.write(sum.variants[index].algebraicType, value.value); } break; - default: - break; - } - } - - writeBuiltinType(type: BuiltinType, value: any) { - switch (type.type) { - case BuiltinType.Type.Array: + case AlgebraicType.Type.Array: const array = value as any[]; this.writer.writeU32(array.length); for (const element of array) { - this.write(type.arrayType as AlgebraicType, element); + this.write(type.array, element); } break; - case BuiltinType.Type.Map: + case AlgebraicType.Type.Map: break; - case BuiltinType.Type.String: + case AlgebraicType.Type.String: this.writer.writeString(value); break; - case BuiltinType.Type.Bool: + case AlgebraicType.Type.Bool: this.writer.writeBool(value); break; - case BuiltinType.Type.I8: + case AlgebraicType.Type.I8: this.writer.writeI8(value); break; - case BuiltinType.Type.U8: + case AlgebraicType.Type.U8: this.writer.writeU8(value); break; - case BuiltinType.Type.I16: + case AlgebraicType.Type.I16: this.writer.writeI16(value); break; - case BuiltinType.Type.U16: + case AlgebraicType.Type.U16: this.writer.writeU16(value); break; - case BuiltinType.Type.I32: + case AlgebraicType.Type.I32: this.writer.writeI32(value); break; - case BuiltinType.Type.U32: + case AlgebraicType.Type.U32: this.writer.writeU32(value); break; - case BuiltinType.Type.I64: + case AlgebraicType.Type.I64: this.writer.writeI64(value); break; - case BuiltinType.Type.U64: + case AlgebraicType.Type.U64: this.writer.writeU64(value); break; - case BuiltinType.Type.I128: + case AlgebraicType.Type.I128: this.writer.writeI128(value); break; - case BuiltinType.Type.U128: + case AlgebraicType.Type.U128: this.writer.writeU128(value); break; - case BuiltinType.Type.F32: + case AlgebraicType.Type.F32: this.writer.writeF32(value); break; - case BuiltinType.Type.F64: + case AlgebraicType.Type.F64: this.writer.writeF64(value); break; + default: + break; } } diff --git a/src/spacetimedb.ts b/src/spacetimedb.ts index cee446c..11bf022 100644 --- a/src/spacetimedb.ts +++ b/src/spacetimedb.ts @@ -21,7 +21,6 @@ import { ProductTypeElement, SumType, SumTypeVariant, - BuiltinType, } from "./algebraic_type"; import { EventType } from "./types"; import { Identity } from "./identity"; @@ -42,7 +41,6 @@ export { ProductTypeElement, SumType, SumTypeVariant, - BuiltinType, ProtobufMessage, BinarySerializer, }; diff --git a/tests/algebraic_value.test.ts b/tests/algebraic_value.test.ts index 3c9544d..f3d559c 100644 --- a/tests/algebraic_value.test.ts +++ b/tests/algebraic_value.test.ts @@ -2,13 +2,11 @@ import { AlgebraicType, ProductType, ProductTypeElement, - BuiltinType, } from "../src/algebraic_type"; import { ProductValue, AlgebraicValue, SumValue, - BuiltinValue, BinaryAdapter, JSONAdapter, } from "../src/algebraic_value"; @@ -19,57 +17,38 @@ describe("AlgebraicValue", () => { let value = new ProductValue([]); let av = new AlgebraicValue(value); - expect(av.product).toBe(value); expect(av.asProductValue()).toBe(value); }); test("when created with a SumValue it assigns the sum property", () => { - let value = new SumValue(1, new AlgebraicValue(new BuiltinValue(1))); + let value = new SumValue(1, new AlgebraicValue(1)); let av = new AlgebraicValue(value); - expect(av.sum).toBe(value); expect(av.asSumValue()).toBe(value); }); - test("when created with a BuiltinValue it assigns the builtin property", () => { - let value = new BuiltinValue(1); - let av = new AlgebraicValue(value); - - expect(av.builtin).toBe(value); - expect(av.asBuiltinValue()).toBe(value); - }); - - test("when created with a BuiltinValue(string) it can be requested as a string", () => { - let value = new BuiltinValue("foo"); - let av = new AlgebraicValue(value); + test("when created with a AlgebraicValue(string) it can be requested as a string", () => { + let av = new AlgebraicValue("foo"); expect(av.asString()).toBe("foo"); }); - test("when created with a BuiltinValue(AlgebraicValue[]) it can be requested as an array", () => { - let array: AlgebraicValue[] = [new AlgebraicValue(new BuiltinValue(1))]; - let value = new BuiltinValue(array); - let av = new AlgebraicValue(value); + test("when created with a AlgebraicValue(AlgebraicValue[]) it can be requested as an array", () => { + let array: AlgebraicValue[] = [new AlgebraicValue(1)]; + let av = new AlgebraicValue(array); expect(av.asArray()).toBe(array); }); }); -describe("BuiltinValue", () => { +describe("primitive values", () => { describe("deserialize with a binary adapter", () => { test("should correctly deserialize array with U8 type", () => { const input = new Uint8Array([2, 0, 0, 0, 10, 20]); const reader = new BinaryReader(input); const adapter: BinaryAdapter = new BinaryAdapter(reader); - const elementType = AlgebraicType.createPrimitiveType( - BuiltinType.Type.U8 - ); - const type: BuiltinType = new BuiltinType( - BuiltinType.Type.Array, - elementType - ); - - const result = BuiltinValue.deserialize(type, adapter); + const type = AlgebraicType.createBytesType(); + const result = AlgebraicValue.deserialize(type, adapter); expect(result.asBytes()).toEqual(new Uint8Array([10, 20])); }); @@ -85,15 +64,8 @@ describe("BuiltinValue", () => { ]); const reader = new BinaryReader(input); const adapter: BinaryAdapter = new BinaryAdapter(reader); - const elementType = AlgebraicType.createPrimitiveType( - BuiltinType.Type.U128 - ); - const type: BuiltinType = new BuiltinType( - BuiltinType.Type.Array, - elementType - ); - - const result = BuiltinValue.deserialize(type, adapter); + const type = AlgebraicType.createArrayType(AlgebraicType.createU128Type()); + const result = AlgebraicValue.deserialize(type, adapter); const u128_max = BigInt(2) ** BigInt(128) - BigInt(1); expect(result.asJsArray("BigInt")).toEqual([ @@ -111,8 +83,8 @@ describe("BuiltinValue", () => { ]); const reader = new BinaryReader(input); const adapter: BinaryAdapter = new BinaryAdapter(reader); - const result = BuiltinValue.deserialize( - new BuiltinType(BuiltinType.Type.U128, undefined), + const result = AlgebraicValue.deserialize( + AlgebraicType.createU128Type(), adapter ); @@ -125,8 +97,8 @@ describe("BuiltinValue", () => { const input = new Uint8Array([1]); const reader = new BinaryReader(input); const adapter: BinaryAdapter = new BinaryAdapter(reader); - const result = BuiltinValue.deserialize( - new BuiltinType(BuiltinType.Type.Bool, undefined), + const result = AlgebraicValue.deserialize( + AlgebraicType.createBoolType(), adapter ); @@ -145,8 +117,8 @@ describe("BuiltinValue", () => { const reader = new BinaryReader(input); const adapter: BinaryAdapter = new BinaryAdapter(reader); - const result = BuiltinValue.deserialize( - new BuiltinType(BuiltinType.Type.String, undefined), + const result = AlgebraicValue.deserialize( + AlgebraicType.createStringType(), adapter ); @@ -158,15 +130,8 @@ describe("BuiltinValue", () => { test("should correctly deserialize array with U8 type", () => { const value = "0002FF"; const adapter: JSONAdapter = new JSONAdapter(value); - const elementType = AlgebraicType.createPrimitiveType( - BuiltinType.Type.U8 - ); - const type: BuiltinType = new BuiltinType( - BuiltinType.Type.Array, - elementType - ); - - const result = BuiltinValue.deserialize(type, adapter); + const type = AlgebraicType.createBytesType(); + const result = AlgebraicValue.deserialize(type, adapter); expect(result.asBytes()).toEqual(new Uint8Array([0, 2, 255])); }); @@ -175,15 +140,8 @@ describe("BuiltinValue", () => { const u128_max = BigInt(2) ** BigInt(128) - BigInt(1); const value = [BigInt(1), u128_max, BigInt(10)]; const adapter: JSONAdapter = new JSONAdapter(value); - const elementType = AlgebraicType.createPrimitiveType( - BuiltinType.Type.U128 - ); - const type: BuiltinType = new BuiltinType( - BuiltinType.Type.Array, - elementType - ); - - const result = BuiltinValue.deserialize(type, adapter); + const type = AlgebraicType.createArrayType(AlgebraicType.createU128Type()); + const result = AlgebraicValue.deserialize(type, adapter); expect(result.asJsArray("BigInt")).toEqual([ BigInt(1), @@ -195,8 +153,8 @@ describe("BuiltinValue", () => { test("should correctly deserialize an U128 type", () => { const value = BigInt("123456789123456789"); const adapter: JSONAdapter = new JSONAdapter(value); - const result = BuiltinValue.deserialize( - new BuiltinType(BuiltinType.Type.U128, undefined), + const result = AlgebraicValue.deserialize( + AlgebraicType.createU128Type(), adapter ); @@ -205,8 +163,8 @@ describe("BuiltinValue", () => { test("should correctly deserialize a boolean type", () => { const adapter: JSONAdapter = new JSONAdapter(true); - const result = BuiltinValue.deserialize( - new BuiltinType(BuiltinType.Type.Bool, undefined), + const result = AlgebraicValue.deserialize( + AlgebraicType.createBoolType(), adapter ); @@ -216,8 +174,8 @@ describe("BuiltinValue", () => { test("should correctly deserialize a string type", () => { const text = "zażółć gęślą jaźń"; const adapter: JSONAdapter = new JSONAdapter(text); - const result = BuiltinValue.deserialize( - new BuiltinType(BuiltinType.Type.String, undefined), + const result = AlgebraicValue.deserialize( + AlgebraicType.createStringType(), adapter ); diff --git a/tests/types/create_player_reducer.ts b/tests/types/create_player_reducer.ts index b4250d2..26f8b6d 100644 --- a/tests/types/create_player_reducer.ts +++ b/tests/types/create_player_reducer.ts @@ -6,7 +6,6 @@ import { __SPACETIMEDB__, AlgebraicType, ProductType, - BuiltinType, ProductTypeElement, IDatabaseTable, AlgebraicValue, @@ -21,7 +20,7 @@ export class CreatePlayerReducer { public static call(name: string, location: Point) {} public static deserializeArgs(adapter: ReducerArgsAdapter): any[] { - let nameType = AlgebraicType.createPrimitiveType(BuiltinType.Type.String); + let nameType = AlgebraicType.createStringType(); let nameValue = AlgebraicValue.deserialize(nameType, adapter.next()); let name = nameValue.asString(); let locationType = Point.getAlgebraicType(); diff --git a/tests/types/player.ts b/tests/types/player.ts index 3db0443..1676220 100644 --- a/tests/types/player.ts +++ b/tests/types/player.ts @@ -6,7 +6,6 @@ import { __SPACETIMEDB__, AlgebraicType, ProductType, - BuiltinType, ProductTypeElement, SumType, SumTypeVariant, @@ -40,11 +39,11 @@ export class Player extends IDatabaseTable { return AlgebraicType.createProductType([ new ProductTypeElement( "owner_id", - AlgebraicType.createPrimitiveType(BuiltinType.Type.String) + AlgebraicType.createStringType() ), new ProductTypeElement( "name", - AlgebraicType.createPrimitiveType(BuiltinType.Type.String) + AlgebraicType.createStringType() ), new ProductTypeElement("location", Point.getAlgebraicType()), ]); diff --git a/tests/types/point.ts b/tests/types/point.ts index 234bfac..27129e1 100644 --- a/tests/types/point.ts +++ b/tests/types/point.ts @@ -6,7 +6,6 @@ import { __SPACETIMEDB__, AlgebraicType, ProductType, - BuiltinType, ProductTypeElement, SumType, SumTypeVariant, @@ -36,11 +35,11 @@ export class Point extends IDatabaseTable { return AlgebraicType.createProductType([ new ProductTypeElement( "x", - AlgebraicType.createPrimitiveType(BuiltinType.Type.U16) + AlgebraicType.createU16Type() ), new ProductTypeElement( "y", - AlgebraicType.createPrimitiveType(BuiltinType.Type.U16) + AlgebraicType.createU16Type() ), ]); } diff --git a/tests/types/user.ts b/tests/types/user.ts index f88b13f..028afa4 100644 --- a/tests/types/user.ts +++ b/tests/types/user.ts @@ -6,7 +6,6 @@ import { __SPACETIMEDB__, AlgebraicType, ProductType, - BuiltinType, ProductTypeElement, SumType, SumTypeVariant, @@ -40,15 +39,13 @@ export class User extends IDatabaseTable { AlgebraicType.createProductType([ new ProductTypeElement( "__identity_bytes", - AlgebraicType.createArrayType( - AlgebraicType.createPrimitiveType(BuiltinType.Type.U8) - ) + AlgebraicType.createBytesType() ), ]) ), new ProductTypeElement( "username", - AlgebraicType.createPrimitiveType(BuiltinType.Type.String) + AlgebraicType.createStringType() ), ]); }