diff --git a/Core/IO/BinarySchemaWriter.cs b/Core/IO/BinarySchemaWriter.cs index 23615229..f29eb3a0 100644 --- a/Core/IO/BinarySchemaWriter.cs +++ b/Core/IO/BinarySchemaWriter.cs @@ -178,7 +178,17 @@ private void WriteDecorator(SchemaDecorator decorator) WriteString(decorator.Identifier); var arguments = decorator.Arguments; // argument count - _writer.Write((byte)arguments.Count); + var count = (byte)arguments.Count; + if (count == 0) + { + _writer.Write((byte)0); + return; + } + if (count > byte.MaxValue) + { + throw new CompilerException($"Decorator {decorator.Identifier} has too many arguments: {count}"); + } + _writer.Write(count); var definition = decorator.Definition; foreach (var argument in arguments) { diff --git a/Core/Meta/WellKnownTypes.cs b/Core/Meta/WellKnownTypes.cs index d6836c70..7e8d6b04 100644 --- a/Core/Meta/WellKnownTypes.cs +++ b/Core/Meta/WellKnownTypes.cs @@ -13,7 +13,7 @@ public static class ReservedWords public const string CompilerName = "bebopc"; public const string SchemaExt = "bop"; - public const byte SchemaLangVersion = 2; + public const byte SchemaLangVersion = 3; public static HashSet Identifiers = new() { diff --git a/Repl/Pages/Index.razor b/Repl/Pages/Index.razor index e3b1fc6a..80769f3d 100644 --- a/Repl/Pages/Index.razor +++ b/Repl/Pages/Index.razor @@ -112,7 +112,7 @@ @functions { - private static readonly CompilerHost host = CompilerHost.CompilerHostBuilder.Create().WithDefaults().Build(); + private static readonly CompilerHost host = CompilerHost.CompilerHostBuilder.Create("").WithDefaults().Build(); [JSInvokable] public static string GetExampleSchema() { @@ -196,7 +196,7 @@ map[guid, Song] songs; return new Repl.CompilerOutput { IsOk = !hasErrorDiagonstics, - Result = hasErrorDiagonstics ? string.Empty : generator.Compile(schema, generatorConfig), + Result = hasErrorDiagonstics ? string.Empty : generator.Compile(schema, generatorConfig).GetAwaiter().GetResult(), Diagonstics = diagonstics }; } diff --git a/Runtime/TypeScript/binary.ts b/Runtime/TypeScript/binary.ts index 00e7eac0..96d6ea96 100644 --- a/Runtime/TypeScript/binary.ts +++ b/Runtime/TypeScript/binary.ts @@ -51,19 +51,19 @@ enum WireTypeKind { Enum, } -type Attributes = { [identifier: string]: Attribute }; -interface Attribute { - arguments?: { [identifier: string]: AttributeArgument }; +type Decorators = { [identifier: string]: Decorator }; +interface Decorator { + arguments?: { [identifier: string]: DecoratorArgument }; } -interface AttributeArgument { +interface DecoratorArgument { typeId: number; - argumentValue: string | number | bigint | Guid | null; + value: string | number | bigint | Guid | null; } interface EnumMember { name: string; - attributes: Attributes; + decorators: Decorators; value: number | bigint | null; } @@ -71,7 +71,7 @@ interface Field { name: string; typeId: number; fieldProperties: FieldTypes; - attributes: Attributes; + decorators: Decorators; constantValue?: number | null; } @@ -79,7 +79,8 @@ interface Definition { index: number; name: string; kind: WireTypeKind; - attributes: Attributes; + minimalEncodeSize: number; + decorators: Decorators; } interface Enum extends Definition { @@ -89,7 +90,8 @@ interface Enum extends Definition { } interface Struct extends Definition { - isReadOnly: boolean; + isMutable: boolean; + isFixedSize: boolean; fields: { [fieldName: string]: Field }; } @@ -109,22 +111,22 @@ interface Union extends Definition { interface Service { name: string; - attributes: Attributes; + decorators: Decorators; methods: { [methodName: string]: ServiceMethod }; } interface ServiceMethod { name: string; - attributes: Attributes; + decorators: Decorators; requestTypeId: number; responseTypeId: number; methodType: WireMethodType; id: number; } -interface ParsedSchema { +interface SchemaAst { bebopVersion: number; - definedTypes: { [typeName: string]: Definition }; + definitions: { [typeName: string]: Definition }; services?: { [serviceName: string]: Service }; } @@ -186,7 +188,7 @@ export class RecordReader { throw new BebopRuntimeError(`Missing field ${field.name}`); } }); - if (definition.isReadOnly) { + if (!definition.isMutable) { Object.freeze(record); } return record; @@ -802,7 +804,7 @@ export class RecordWriter { * `BinarySchema` represents a class that allows parsing of a Bebop schema in binary form. * * This class holds the DataView representation of the binary data, its parsing position, - * and contains methods to parse each specific type of Bebop schema structure. + * and contains methods to get each specific type of Bebop schema structure. */ export class BinarySchema { private readonly view: DataView; @@ -810,7 +812,7 @@ export class BinarySchema { private pos: number; private readonly ArrayType = -14; private readonly MapType = -15; - private parsedSchema?: ParsedSchema; + private parsedSchema?: SchemaAst; private indexToDefinition: { [index: number]: Definition } = {}; private nameToDefinition: { [name: string]: Definition } = {}; public reader: RecordReader; @@ -856,42 +858,46 @@ export class BinarySchema { } /** - * Parse the schema. + * Get the schema. * This method should only be called once per instance. */ - public parse(): void { + public get(): void { if (this.parsedSchema !== undefined) { return; } - const schemaVersion = this.parseUint8(); - const numDefinedTypes = this.parseUint32(); + const schemaVersion = this.getUint8(); + const numDefinedTypes = this.getUint32(); let definedTypes: { [typeName: string]: Definition } = {}; for (let i = 0; i < numDefinedTypes; i++) { - const def = this.parseDefinedType(i); + const def = this.getDefinedType(i); definedTypes[def.name] = def; this.indexToDefinition[i] = def; this.nameToDefinition[def.name] = def; } - const serviceCount = this.parseUint32(); + const serviceCount = this.getUint32(); let services: { [serviceName: string]: Service } = {}; for (let i = 0; i < serviceCount; i++) { - const service = this.parseServiceDefinition(); + const service = this.getServiceDefinition(); services[service.name] = service; } - this.parsedSchema = { bebopVersion: schemaVersion, definedTypes, services }; + this.parsedSchema = { + bebopVersion: schemaVersion, + definitions: definedTypes, + services, + }; Object.freeze(this.parsedSchema); } /** - * Returns the parsed schema. + * Returns the getd schema. */ - public get ast(): ParsedSchema { + public get ast(): Readonly { if (this.parsedSchema === undefined) { - this.parse(); + this.get(); } return this.parsedSchema!; } @@ -919,189 +925,181 @@ export class BinarySchema { return definition; } - private parseDefinedType(index: number): Definition { - const typeName = this.parseString(); - const typeKind = this.parseUint8() as WireTypeKind; - const typeAttributes = this.parseAttributes(); - - switch (typeKind) { + private getDefinedType(index: number): Definition { + const name = this.getString(); + const kind = this.getUint8() as WireTypeKind; + const decorators = this.getDecorators(); + switch (kind) { case WireTypeKind.Enum: - return this.parseEnumDefinition( - typeName, - typeKind, - typeAttributes, - index - ); + return this.getEnumDefinition(name, kind, decorators, index); case WireTypeKind.Union: - return this.parseUnionDefinition( - typeName, - typeKind, - typeAttributes, - index - ); + return this.getUnionDefinition(name, kind, decorators, index); case WireTypeKind.Struct: - return this.parseStructDefinition( - typeName, - typeKind, - typeAttributes, - index - ); + return this.getStructDefinition(name, kind, decorators, index); case WireTypeKind.Message: - return this.parseMessageDefinition( - typeName, - typeKind, - typeAttributes, - index - ); + return this.getMessageDefinition(name, kind, decorators, index); default: - throw new BebopRuntimeError(`Unknown type kind: ${typeKind}`); + throw new BebopRuntimeError(`Unknown type kind: ${kind}`); } } - private parseAttributes() { - const numAttributes = this.parseUint8(); - const attributes: Attributes = {}; - for (let i = 0; i < numAttributes; i++) { - const identifier = this.parseString(); - attributes[identifier] = this.parseAttribute(); + private getDecorators() { + const decoratorCount = this.getUint8(); + const decorators: Decorators = {}; + if (decoratorCount === 0) { + return decorators; } - return attributes; + for (let i = 0; i < decoratorCount; i++) { + const identifier = this.getString(); + decorators[identifier] = this.getDecorator(); + } + return decorators; } - private parseAttribute(): Attribute { - const argCount = this.parseUint8(); - const args: { [name: string]: AttributeArgument } = {}; + private getDecorator(): Decorator { + const argCount = this.getUint8(); + const args: { [name: string]: DecoratorArgument } = {}; for (let i = 0; i < argCount; i++) { - const identifier = this.parseString(); - const typeId = this.parseTypeId(); - const argumentValue = this.parseConstantValue(typeId); + const identifier = this.getString(); + const typeId = this.getTypeId(); + const argumentValue = this.getConstantValue(typeId); args[identifier] = { typeId, - argumentValue, + value: argumentValue, }; } return { arguments: args }; } - private parseEnumDefinition( - typeName: string, - typeKind: WireTypeKind, - typeAttributes: Attributes, + private getEnumDefinition( + name: string, + kind: WireTypeKind, + decorators: Decorators, index: number ): Enum { - const baseType = this.parseTypeId(); - const isBitFlags = this.parseBool(); - const memberCount = this.parseUint8(); + const baseType = this.getTypeId(); + const isBitFlags = this.getBool(); + const minimalEncodeSize = this.getInt32(); + const memberCount = this.getUint8(); const members: { [name: string]: EnumMember } = {}; for (let i = 0; i < memberCount; i++) { - const member = this.parseEnumMember(baseType); + const member = this.getEnumMember(baseType); members[member.name] = member; } return { index, - name: typeName, + name: name, isBitFlags, - kind: typeKind, - attributes: typeAttributes, + kind: kind, + decorators: decorators, + minimalEncodeSize, baseType, members, }; } - private parseEnumMember(baseType: number): EnumMember { - const name = this.parseString(); - const attributes = this.parseAttributes(); - const value = this.parseConstantValue(baseType) as number; - return { name, attributes, value }; + private getEnumMember(baseType: number): EnumMember { + const name = this.getString(); + const decorators = this.getDecorators(); + const value = this.getConstantValue(baseType) as number; + return { name, decorators, value }; } - private parseUnionDefinition( - typeName: string, - typeKind: WireTypeKind, - typeAttributes: { [name: string]: Attribute }, + private getUnionDefinition( + name: string, + kind: WireTypeKind, + decorators: { [name: string]: Decorator }, index: number ): Union { - const branchCount = this.parseUint8(); + const minimalEncodeSize = this.getInt32(); + const branchCount = this.getUint8(); const branches = new Array(branchCount) .fill(null) - .map(() => this.parseUnionBranch()); + .map(() => this.getUnionBranch()); return { index, - name: typeName, - kind: typeKind, - attributes: typeAttributes, + name: name, + kind: kind, + decorators: decorators, + minimalEncodeSize, branchCount, branches, }; } - private parseUnionBranch(): UnionBranch { - const discriminator = this.parseUint8(); - const typeId = this.parseTypeId(); + private getUnionBranch(): UnionBranch { + const discriminator = this.getUint8(); + const typeId = this.getTypeId(); return { discriminator, typeId }; } - private parseStructDefinition( - typeName: string, - typeKind: WireTypeKind, - typeAttributes: { [name: string]: Attribute }, + private getStructDefinition( + name: string, + kind: WireTypeKind, + decorators: { [name: string]: Decorator }, index: number ): Struct { - const isReadOnly = this.parseBool(); - const fields = this.parseFields(typeKind); + const isMutable = this.getBool(); + const minimalEncodeSize = this.getInt32(); + const isFixedSize = this.getBool(); + const fields = this.getFields(kind); return { index, - name: typeName, - kind: typeKind, - attributes: typeAttributes, - isReadOnly, + name: name, + kind: kind, + decorators: decorators, + isMutable, + minimalEncodeSize, + isFixedSize, fields, }; } - private parseMessageDefinition( - typeName: string, - typeKind: WireTypeKind, - typeAttributes: { [name: string]: Attribute }, + private getMessageDefinition( + name: string, + kind: WireTypeKind, + decorators: { [name: string]: Decorator }, index: number ): Message { - const fields = this.parseFields(typeKind); + const minimalEncodeSize = this.getInt32(); + const fields = this.getFields(kind); return { index, - name: typeName, - kind: typeKind, - attributes: typeAttributes, + minimalEncodeSize, + name: name, + kind: kind, + decorators: decorators, fields, }; } - private parseFields(parentKind: WireTypeKind): { [name: string]: Field } { - const numFields = this.parseUint8(); + private getFields(parentKind: WireTypeKind): { [name: string]: Field } { + const numFields = this.getUint8(); const fields: { [name: string]: Field } = {}; for (let i = 0; i < numFields; i++) { - const field = this.parseField(parentKind); + const field = this.getField(parentKind); fields[field.name] = field; } return fields; } - private parseField(parentKind: WireTypeKind): Field { - const fieldName = this.parseString(); - let fieldTypeId = this.parseTypeId(); + private getField(parentKind: WireTypeKind): Field { + const fieldName = this.getString(); + let fieldTypeId = this.getTypeId(); let fieldProperties: FieldTypes; if (fieldTypeId === this.ArrayType || fieldTypeId === this.MapType) { - fieldProperties = this.parseNestedType( + fieldProperties = this.getNestedType( fieldTypeId === this.ArrayType ? "array" : "map" ); } else { fieldProperties = { type: "scalar" }; } - const attributes = this.parseAttributes(); + const decorators = this.getDecorators(); const constantValue = ( parentKind === WireTypeKind.Message - ? this.parseConstantValue(WireBaseType.Byte) + ? this.getConstantValue(WireBaseType.Byte) : null ) as any; @@ -1109,25 +1107,25 @@ export class BinarySchema { name: fieldName, typeId: fieldTypeId, fieldProperties, - attributes, + decorators: decorators, constantValue, }; } - private parseNestedType(parentType: string): FieldTypes { + private getNestedType(parentType: string): FieldTypes { if (parentType === "array") { - const depth = this.parseUint8(); - const memberTypeId = this.parseTypeId(); + const depth = this.getUint8(); + const memberTypeId = this.getTypeId(); return { type: parentType, memberTypeId: memberTypeId, depth }; } if (parentType === "map") { - const keyTypeId = this.parseTypeId(); - const valueTypeId = this.parseTypeId(); + const keyTypeId = this.getTypeId(); + const valueTypeId = this.getTypeId(); let nestedType: FieldTypes | undefined; if (valueTypeId === this.ArrayType || valueTypeId === this.MapType) { - nestedType = this.parseNestedType( + nestedType = this.getNestedType( valueTypeId === this.ArrayType ? "array" : "map" ); } @@ -1142,54 +1140,54 @@ export class BinarySchema { throw new BebopRuntimeError("Invalid initial type"); } - private parseConstantValue( + private getConstantValue( typeId: number ): string | number | bigint | Guid | null { switch (typeId) { case WireBaseType.Bool: - return this.parseBool() ? 1 : 0; + return this.getBool() ? 1 : 0; case WireBaseType.Byte: - return this.parseUint8(); + return this.getUint8(); case WireBaseType.UInt16: - return this.parseUint16(); + return this.getUint16(); case WireBaseType.Int16: - return this.parseInt16(); + return this.getInt16(); case WireBaseType.UInt32: - return this.parseUint32(); + return this.getUint32(); case WireBaseType.Int32: - return this.parseInt32(); + return this.getInt32(); case WireBaseType.UInt64: - return BigInt(this.parseUint64()) as bigint; + return BigInt(this.getUint64()) as bigint; case WireBaseType.Int64: - return BigInt(this.parseInt64()); + return BigInt(this.getInt64()); case WireBaseType.Float32: - return this.parseFloat32(); + return this.getFloat32(); case WireBaseType.Float64: - return this.parseFloat64(); + return this.getFloat64(); case WireBaseType.String: - return this.parseString(); + return this.getString(); case WireBaseType.Guid: - return Guid.fromBytes(this.parseGuid(), 0); + return Guid.fromBytes(this.getGuid(), 0); default: throw new BebopRuntimeError(`Unsupported constant type ID: ${typeId}`); } } - private parseServiceDefinition(): Service { - let name = this.parseString(); - let attributes = this.parseAttributes(); + private getServiceDefinition(): Service { + let name = this.getString(); + let decorators = this.getDecorators(); let methods: { [name: string]: ServiceMethod } = {}; - let methodCount = this.parseUint32(); + let methodCount = this.getUint32(); for (let i = 0; i < methodCount; i++) { - let methodName = this.parseString(); - let methodAttributes = this.parseAttributes(); - let methodType = this.parseUint8() as WireMethodType; - let requestTypeId = this.parseTypeId(); - let responseTypeId = this.parseTypeId(); - let id = this.parseUint32(); + let methodName = this.getString(); + let methodDecorators = this.getDecorators(); + let methodType = this.getUint8() as WireMethodType; + let requestTypeId = this.getTypeId(); + let responseTypeId = this.getTypeId(); + let id = this.getUint32(); methods[methodName] = { name: methodName, - attributes: methodAttributes, + decorators: methodDecorators, methodType: methodType, requestTypeId: requestTypeId, responseTypeId: responseTypeId, @@ -1198,12 +1196,12 @@ export class BinarySchema { } return { name: name, - attributes: attributes, + decorators: decorators, methods: methods, }; } - private parseString(): string { + private getString(): string { const start = this.pos; while (this.pos < this.data.length && this.data[this.pos] !== 0) { this.pos++; @@ -1216,71 +1214,71 @@ export class BinarySchema { return decoder.decode(strBytes); } - private parseUint8() { + private getUint8() { let value = this.view.getUint8(this.pos); this.pos++; return value; } - private parseUint16() { + private getUint16() { let value = this.view.getUint16(this.pos, true); this.pos += 2; return value; } - private parseInt16() { + private getInt16() { let value = this.view.getInt16(this.pos, true); this.pos += 2; return value; } - private parseUint32() { + private getUint32() { let value = this.view.getUint32(this.pos, true); this.pos += 4; return value; } - private parseInt32() { + private getInt32() { let value = this.view.getInt32(this.pos, true); this.pos += 4; return value; } - private parseUint64() { + private getUint64() { let value = this.view.getBigUint64(this.pos, true); this.pos += 8; return Number(value); } - private parseInt64() { + private getInt64() { let value = this.view.getBigInt64(this.pos, true); this.pos += 8; return Number(value); } - private parseFloat32() { + private getFloat32() { let value = this.view.getFloat32(this.pos, true); this.pos += 4; return value; } - private parseFloat64() { + private getFloat64() { let value = this.view.getFloat64(this.pos, true); this.pos += 8; return value; } - private parseBool() { - return this.parseUint8() !== 0; + private getBool() { + return this.getUint8() !== 0; } - private parseTypeId() { + private getTypeId() { let typeId = this.view.getInt32(this.pos, true); this.pos += 4; return typeId; } - private parseGuid() { + private getGuid() { let value = this.data.subarray(this.pos, this.pos + 16); this.pos += 16; return value; diff --git a/Runtime/TypeScript/bs.test.ts b/Runtime/TypeScript/bs.test.ts index 6a6e20fb..26cc3d92 100644 --- a/Runtime/TypeScript/bs.test.ts +++ b/Runtime/TypeScript/bs.test.ts @@ -3,68 +3,94 @@ import { BebopRuntimeError, BinarySchema, Guid } from "./index"; import { describe, expect, it } from "vitest"; const schemaData = new Uint8Array([ - 2, 4, 0, 0, 0, 83, 116, 97, 116, 117, 115, 0, 4, 0, 254, 255, 255, 255, 2, 79, - 107, 97, 121, 0, 0, 1, 68, 111, 97, 98, 108, 101, 0, 0, 2, 80, 111, 105, 110, - 116, 0, 1, 0, 1, 6, 120, 0, 250, 255, 255, 255, 0, 121, 0, 250, 255, 255, 255, - 0, 110, 97, 109, 101, 0, 245, 255, 255, 255, 0, 115, 116, 97, 116, 117, 115, - 0, 0, 0, 0, 0, 0, 110, 117, 109, 98, 101, 114, 115, 0, 242, 255, 255, 255, 0, - 250, 255, 255, 255, 0, 97, 77, 97, 112, 0, 241, 255, 255, 255, 245, 255, 255, - 255, 245, 255, 255, 255, 0, 67, 111, 111, 114, 100, 105, 110, 97, 116, 101, 0, - 2, 0, 3, 112, 111, 105, 110, 116, 0, 1, 0, 0, 0, 0, 1, 105, 100, 0, 244, 255, - 255, 255, 0, 2, 110, 101, 115, 116, 101, 100, 0, 241, 255, 255, 255, 245, 255, - 255, 255, 241, 255, 255, 255, 245, 255, 255, 255, 242, 255, 255, 255, 1, 245, - 255, 255, 255, 0, 3, 80, 111, 119, 101, 114, 0, 3, 0, 2, 1, 1, 0, 0, 0, 2, 2, - 0, 0, 0, 0, 0, 0, 0, + 3, 5, 0, 0, 0, 82, 105, 103, 104, 116, 115, 0, 4, 1, 102, 108, 97, 103, 115, + 0, 0, 254, 255, 255, 255, 1, 1, 0, 0, 0, 4, 68, 101, 102, 97, 117, 108, 116, + 0, 0, 0, 85, 115, 101, 114, 0, 0, 1, 77, 111, 100, 0, 0, 2, 65, 100, 109, 105, + 110, 0, 0, 3, 65, 112, 105, 82, 101, 113, 117, 101, 115, 116, 0, 1, 0, 0, 12, + 0, 0, 0, 0, 2, 117, 114, 105, 0, 245, 255, 255, 255, 0, 116, 114, 97, 99, 101, + 0, 249, 255, 255, 255, 0, 68, 101, 102, 97, 117, 108, 116, 82, 101, 115, 112, + 111, 110, 115, 101, 0, 1, 0, 0, 5, 0, 0, 0, 0, 2, 114, 105, 103, 104, 116, + 115, 0, 0, 0, 0, 0, 0, 117, 115, 101, 114, 110, 97, 109, 101, 0, 245, 255, + 255, 255, 0, 69, 114, 114, 111, 114, 82, 101, 115, 112, 111, 110, 115, 101, 0, + 2, 0, 5, 0, 0, 0, 3, 114, 101, 97, 115, 111, 110, 0, 245, 255, 255, 255, 0, 1, + 99, 111, 100, 101, 0, 251, 255, 255, 255, 0, 2, 105, 100, 0, 244, 255, 255, + 255, 1, 100, 101, 112, 114, 101, 99, 97, 116, 101, 100, 0, 1, 114, 101, 97, + 115, 111, 110, 0, 245, 255, 255, 255, 106, 117, 115, 116, 32, 98, 101, 99, 97, + 117, 115, 101, 0, 3, 65, 112, 105, 82, 101, 115, 112, 111, 110, 115, 101, 0, + 3, 0, 10, 0, 0, 0, 2, 1, 2, 0, 0, 0, 2, 3, 0, 0, 0, 1, 0, 0, 0, 71, 114, 101, + 101, 116, 101, 114, 0, 0, 1, 0, 0, 0, 115, 97, 121, 72, 101, 108, 108, 111, 0, + 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 85, 246, 254, 77, ]); const recordData = new Uint8Array([ - 160, 0, 0, 0, 2, 156, 0, 0, 0, 1, 232, 3, 0, 0, 208, 7, 0, 0, 11, 0, 0, 0, 72, - 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 2, 2, 0, 0, 0, 0, 8, 0, 0, - 192, 33, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 104, 101, 108, 108, 111, 5, 0, 0, 0, - 119, 111, 114, 108, 100, 2, 207, 129, 243, 159, 71, 35, 167, 74, 162, 56, 120, - 38, 2, 125, 63, 63, 3, 1, 0, 0, 0, 9, 0, 0, 0, 111, 117, 116, 101, 114, 75, - 101, 121, 49, 1, 0, 0, 0, 9, 0, 0, 0, 105, 110, 110, 101, 114, 75, 101, 121, - 49, 2, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 118, 97, 108, 49, 4, 0, 0, 0, 118, 97, - 108, 50, 2, 0, 0, 0, 4, 0, 0, 0, 118, 97, 108, 51, 4, 0, 0, 0, 118, 97, 108, - 52, 0, + 9, 0, 0, 0, 1, 3, 4, 0, 0, 0, 116, 101, 115, 116, +]); + +const recordData2 = new Uint8Array([ + 39, 0, 0, 0, 2, 35, 0, 0, 0, 1, 7, 0, 0, 0, 102, 97, 105, 108, 117, 114, 101, + 2, 244, 1, 0, 0, 3, 13, 118, 226, 151, 228, 248, 79, 67, 167, 238, 53, 223, + 133, 176, 67, 209, 0, ]); const schema = new BinarySchema(schemaData); describe("binary schema", () => { - it("can parse", () => { - schema.parse(); + it("can read", () => { + schema.get(); const ast = schema.ast; + console.log(JSON.stringify(ast, null, 2)); + expect(ast).toBeDefined(); - expect(ast.bebopVersion).toBe(2); - expect(ast.definedTypes).keys(["Coordinate", "Point", "Power", "Status"]); - expect(ast.definedTypes["Status"]).toBeDefined(); - expect(ast.definedTypes["Status"].kind).toBe(4); - expect(ast.definedTypes["Status"]["members"]).keys(["Okay", "Doable"]); + expect(ast.bebopVersion).toBe(3); + expect(ast.definitions).keys([ + "Rights", + "ApiRequest", + "ApiResponse", + "DefaultResponse", + "ErrorResponse", + ]); + expect(ast.definitions["ApiResponse"]).toBeDefined(); + expect(ast.definitions["ApiResponse"].kind).toBe(3); + expect(ast.definitions["Rights"]["members"]).keys([ + "Default", + "User", + "Mod", + "Admin", + ]); }); it("can read a record using the schema", () => { - const record = schema.reader.read("Power", recordData); + const record = schema.reader.read("ApiResponse", recordData); + expect(record).toBeDefined(); + expect(record["discriminator"]).toBe(1); + expect(record["value"]).keys(["rights", "username"]); + const value = record["value"] as Record; + expect(value["rights"]).toBe(3); + expect(value["username"]).toBe("test"); + }); + + it("can read a record with guid using the schema", () => { + const record = schema.reader.read("ApiResponse", recordData2); expect(record).toBeDefined(); expect(record["discriminator"]).toBe(2); - expect(record["value"]).keys(["id", "nested", "point"]); + expect(record["value"]).keys(["code", "reason", "id"]); const value = record["value"] as Record; - expect(value["id"]).toBeInstanceOf(Guid); - expect((value["id"] as Guid).toString()).toBe( - "9ff381cf-2347-4aa7-a238-7826027d3f3f" + expect(value["code"]).toBe(500); + expect(value["reason"]).toBe("failure"); + expect(value["id"]).toStrictEqual( + Guid.parseGuid("97e2760d-f8e4-434f-a7ee-35df85b043d1") ); }); it("can write a record using the schema", () => { - const record = schema.reader.read("Power", recordData); + const record = schema.reader.read("ApiResponse", recordData); expect(record).toBeDefined(); - const data = schema.writer.write("Power", record); + const data = schema.writer.write("ApiResponse", record); expect(data).toBeDefined(); expect(data).toEqual(recordData); - const read = schema.reader.read("Power", data); + const read = schema.reader.read("ApiResponse", data); expect(read).toEqual(record); }); - it("cannot modify schema data", () => { expect(() => { schema.raw[0] = 0;