feat: added support typescript
This commit is contained in:
+635
@@ -0,0 +1,635 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/edmand46/arpack/parser"
|
||||
)
|
||||
|
||||
// GenerateTypeScript generates TypeScript code for the given messages
|
||||
func GenerateTypeScript(messages []parser.Message, namespace string) ([]byte, error) {
|
||||
return GenerateTypeScriptSchema(parser.Schema{Messages: messages}, namespace)
|
||||
}
|
||||
|
||||
// GenerateTypeScriptSchema generates TypeScript code from a schema
|
||||
func GenerateTypeScriptSchema(schema parser.Schema, namespace string) ([]byte, error) {
|
||||
messages := schema.Messages
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("// <auto-generated> arpack </auto-generated>\n")
|
||||
b.WriteString("// Code generated by arpack. DO NOT EDIT.\n\n")
|
||||
|
||||
enumNames := make(map[string]struct{}, len(schema.Enums))
|
||||
for _, enum := range schema.Enums {
|
||||
enumNames[enum.Name] = struct{}{}
|
||||
}
|
||||
|
||||
// Generate enums
|
||||
for _, enum := range schema.Enums {
|
||||
writeTSEnum(&b, enum)
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
// Generate messages
|
||||
for i, msg := range messages {
|
||||
if err := writeTSMessage(&b, msg, enumNames); err != nil {
|
||||
return nil, fmt.Errorf("message %s: %w", msg.Name, err)
|
||||
}
|
||||
if i < len(messages)-1 {
|
||||
b.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
return []byte(b.String()), nil
|
||||
}
|
||||
|
||||
func writeTSEnum(b *strings.Builder, enum parser.Enum) {
|
||||
fmt.Fprintf(b, "export enum %s {\n", enum.Name)
|
||||
for i, value := range enum.Values {
|
||||
fmt.Fprintf(b, " %s = %s", value.Name, value.Value)
|
||||
if i < len(enum.Values)-1 {
|
||||
b.WriteString(",")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
b.WriteString("}\n")
|
||||
}
|
||||
|
||||
func writeTSMessage(b *strings.Builder, msg parser.Message, enumNames map[string]struct{}) error {
|
||||
segs := segmentFields(msg.Fields)
|
||||
|
||||
fmt.Fprintf(b, "export class %s {\n", msg.Name)
|
||||
|
||||
// Field declarations with defaults
|
||||
for _, f := range msg.Fields {
|
||||
defaultValue := tsDefaultValue(f)
|
||||
fmt.Fprintf(b, " %s: %s = %s;\n", toCamelCase(f.Name), tsTypeName(f, enumNames), defaultValue)
|
||||
}
|
||||
b.WriteString("\n")
|
||||
|
||||
// Serialize method
|
||||
b.WriteString(" serialize(view: DataView, offset: number): number {\n")
|
||||
b.WriteString(" let pos = offset;\n")
|
||||
for i, seg := range segs {
|
||||
if seg.single != nil {
|
||||
if err := writeTSSerializeField(b, "this", *seg.single, " ", enumNames); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
writeTSBoolGroupSerialize(b, "this", seg.bools, i, " ")
|
||||
}
|
||||
}
|
||||
b.WriteString(" return pos - offset;\n")
|
||||
b.WriteString(" }\n\n")
|
||||
|
||||
// Deserialize method
|
||||
fmt.Fprintf(b, " static deserialize(view: DataView, offset: number): [%s, number] {\n", msg.Name)
|
||||
b.WriteString(" let pos = offset;\n")
|
||||
b.WriteString(fmt.Sprintf(" const msg = new %s();\n", msg.Name))
|
||||
for i, seg := range segs {
|
||||
if seg.single != nil {
|
||||
if err := writeTSDeserializeField(b, "msg", *seg.single, " ", enumNames); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
writeTSBoolGroupDeserialize(b, "msg", seg.bools, i, " ")
|
||||
}
|
||||
}
|
||||
b.WriteString(" return [msg, pos - offset];\n")
|
||||
b.WriteString(" }\n")
|
||||
|
||||
b.WriteString("}\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSBoolGroupSerialize(b *strings.Builder, recv string, bools []parser.Field, groupIdx int, indent string) {
|
||||
varName := fmt.Sprintf("_boolByte%d", groupIdx)
|
||||
fmt.Fprintf(b, "%slet %s = 0;\n", indent, varName)
|
||||
for bit, f := range bools {
|
||||
fmt.Fprintf(b, "%sif (%s.%s) %s |= 1 << %d;\n", indent, recv, toCamelCase(f.Name), varName, bit)
|
||||
}
|
||||
fmt.Fprintf(b, "%sview.setUint8(pos, %s);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
}
|
||||
|
||||
func writeTSBoolGroupDeserialize(b *strings.Builder, recv string, bools []parser.Field, groupIdx int, indent string) {
|
||||
varName := fmt.Sprintf("_boolByte%d", groupIdx)
|
||||
fmt.Fprintf(b, "%sconst %s = view.getUint8(pos);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
for bit, f := range bools {
|
||||
fmt.Fprintf(b, "%s%s.%s = (%s & (1 << %d)) !== 0;\n", indent, recv, toCamelCase(f.Name), varName, bit)
|
||||
}
|
||||
}
|
||||
|
||||
func writeTSSerializeField(b *strings.Builder, recv string, f parser.Field, indent string, enumNames map[string]struct{}) error {
|
||||
access := recv + "." + toCamelCase(f.Name)
|
||||
switch f.Kind {
|
||||
case parser.KindPrimitive:
|
||||
return writeTSSerializePrimitive(b, access, f, indent, enumNames)
|
||||
case parser.KindNested:
|
||||
fmt.Fprintf(b, "%spos += %s.serialize(view, pos);\n", indent, access)
|
||||
case parser.KindFixedArray:
|
||||
iVar := "_i" + f.Name
|
||||
fmt.Fprintf(b, "%sfor (let %s = 0; %s < %d; %s++) {\n",
|
||||
indent, iVar, iVar, f.FixedLen, iVar)
|
||||
elemField := parser.Field{
|
||||
Name: f.Name + "[" + iVar + "]",
|
||||
Kind: f.Elem.Kind,
|
||||
Primitive: f.Elem.Primitive,
|
||||
NamedType: f.Elem.NamedType,
|
||||
Quant: f.Elem.Quant,
|
||||
TypeName: f.Elem.TypeName,
|
||||
Elem: f.Elem.Elem,
|
||||
FixedLen: f.Elem.FixedLen,
|
||||
}
|
||||
if err := writeTSSerializeField(b, recv, elemField, indent+" ", enumNames); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(b, "%s}\n", indent)
|
||||
case parser.KindSlice:
|
||||
fmt.Fprintf(b, "%sview.setUint16(pos, %s.length, true);\n", indent, access)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
iVar := "_i" + f.Name
|
||||
fmt.Fprintf(b, "%sfor (const %s of %s) {\n", indent, iVar, access)
|
||||
elemField := parser.Field{
|
||||
Name: iVar,
|
||||
Kind: f.Elem.Kind,
|
||||
Primitive: f.Elem.Primitive,
|
||||
NamedType: f.Elem.NamedType,
|
||||
Quant: f.Elem.Quant,
|
||||
TypeName: f.Elem.TypeName,
|
||||
Elem: f.Elem.Elem,
|
||||
FixedLen: f.Elem.FixedLen,
|
||||
}
|
||||
if err := writeTSSerializeFieldElement(b, iVar, elemField, indent+" ", enumNames); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(b, "%s}\n", indent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSSerializeFieldElement(b *strings.Builder, access string, f parser.Field, indent string, enumNames map[string]struct{}) error {
|
||||
switch f.Kind {
|
||||
case parser.KindPrimitive:
|
||||
return writeTSSerializePrimitiveElement(b, access, f, indent, enumNames)
|
||||
case parser.KindNested:
|
||||
fmt.Fprintf(b, "%spos += %s.serialize(view, pos);\n", indent, access)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSSerializePrimitiveElement(b *strings.Builder, access string, f parser.Field, indent string, enumNames map[string]struct{}) error {
|
||||
if f.Quant != nil {
|
||||
return writeTSSerializeQuantElement(b, access, f, indent)
|
||||
}
|
||||
|
||||
valueExpr := tsSerializeValueExpr(access, f, enumNames)
|
||||
switch f.Primitive {
|
||||
case parser.KindFloat32:
|
||||
fmt.Fprintf(b, "%sview.setFloat32(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindFloat64:
|
||||
fmt.Fprintf(b, "%sview.setFloat64(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindInt8:
|
||||
fmt.Fprintf(b, "%sview.setInt8(pos, %s);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindUint8:
|
||||
fmt.Fprintf(b, "%sview.setUint8(pos, %s);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindBool:
|
||||
fmt.Fprintf(b, "%sview.setUint8(pos, %s ? 1 : 0);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindInt16:
|
||||
fmt.Fprintf(b, "%sview.setInt16(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
case parser.KindUint16:
|
||||
fmt.Fprintf(b, "%sview.setUint16(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
case parser.KindInt32:
|
||||
fmt.Fprintf(b, "%sview.setInt32(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindUint32:
|
||||
fmt.Fprintf(b, "%sview.setUint32(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindInt64:
|
||||
fmt.Fprintf(b, "%sview.setBigInt64(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindUint64:
|
||||
fmt.Fprintf(b, "%sview.setBigUint64(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindString:
|
||||
lenVar := "_slen" + sanitizeVarName(access)
|
||||
fmt.Fprintf(b, "%sconst %s = new TextEncoder().encode(%s);\n", indent, lenVar, valueExpr)
|
||||
fmt.Fprintf(b, "%sview.setUint16(pos, %s.length, true);\n", indent, lenVar)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
fmt.Fprintf(b, "%snew Uint8Array(view.buffer, pos, %s.length).set(%s);\n", indent, lenVar, lenVar)
|
||||
fmt.Fprintf(b, "%spos += %s.length;\n", indent, lenVar)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSSerializePrimitive(b *strings.Builder, access string, f parser.Field, indent string, enumNames map[string]struct{}) error {
|
||||
if f.Quant != nil {
|
||||
return writeTSSerializeQuant(b, access, f, indent)
|
||||
}
|
||||
|
||||
valueExpr := tsSerializeValueExpr(access, f, enumNames)
|
||||
switch f.Primitive {
|
||||
case parser.KindFloat32:
|
||||
fmt.Fprintf(b, "%sview.setFloat32(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindFloat64:
|
||||
fmt.Fprintf(b, "%sview.setFloat64(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindInt8:
|
||||
fmt.Fprintf(b, "%sview.setInt8(pos, %s);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindUint8:
|
||||
fmt.Fprintf(b, "%sview.setUint8(pos, %s);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindBool:
|
||||
fmt.Fprintf(b, "%sview.setUint8(pos, %s ? 1 : 0);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindInt16:
|
||||
fmt.Fprintf(b, "%sview.setInt16(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
case parser.KindUint16:
|
||||
fmt.Fprintf(b, "%sview.setUint16(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
case parser.KindInt32:
|
||||
fmt.Fprintf(b, "%sview.setInt32(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindUint32:
|
||||
fmt.Fprintf(b, "%sview.setUint32(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindInt64:
|
||||
fmt.Fprintf(b, "%sview.setBigInt64(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindUint64:
|
||||
fmt.Fprintf(b, "%sview.setBigUint64(pos, %s, true);\n", indent, valueExpr)
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindString:
|
||||
lenVar := "_slen" + sanitizeVarName(access)
|
||||
fmt.Fprintf(b, "%sconst %s = new TextEncoder().encode(%s);\n", indent, lenVar, valueExpr)
|
||||
fmt.Fprintf(b, "%sview.setUint16(pos, %s.length, true);\n", indent, lenVar)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
fmt.Fprintf(b, "%snew Uint8Array(view.buffer, pos, %s.length).set(%s);\n", indent, lenVar, lenVar)
|
||||
fmt.Fprintf(b, "%spos += %s.length;\n", indent, lenVar)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSSerializeQuant(b *strings.Builder, access string, f parser.Field, indent string) error {
|
||||
q := f.Quant
|
||||
maxUint := q.MaxUint()
|
||||
varName := "_q" + sanitizeVarName(access)
|
||||
if q.Bits == 8 {
|
||||
fmt.Fprintf(b, "%sconst %s = Math.round((%s - (%g)) / (%g - (%g)) * %g);\n",
|
||||
indent, varName, access, q.Min, q.Max, q.Min, maxUint)
|
||||
fmt.Fprintf(b, "%sview.setUint8(pos, %s);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
} else {
|
||||
fmt.Fprintf(b, "%sconst %s = Math.round((%s - (%g)) / (%g - (%g)) * %g);\n",
|
||||
indent, varName, access, q.Min, q.Max, q.Min, maxUint)
|
||||
fmt.Fprintf(b, "%sview.setUint16(pos, %s, true);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSSerializeQuantElement(b *strings.Builder, access string, f parser.Field, indent string) error {
|
||||
q := f.Quant
|
||||
maxUint := q.MaxUint()
|
||||
varName := "_q" + sanitizeVarName(access)
|
||||
if q.Bits == 8 {
|
||||
fmt.Fprintf(b, "%sconst %s = Math.round((%s - (%g)) / (%g - (%g)) * %g);\n",
|
||||
indent, varName, access, q.Min, q.Max, q.Min, maxUint)
|
||||
fmt.Fprintf(b, "%sview.setUint8(pos, %s);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
} else {
|
||||
fmt.Fprintf(b, "%sconst %s = Math.round((%s - (%g)) / (%g - (%g)) * %g);\n",
|
||||
indent, varName, access, q.Min, q.Max, q.Min, maxUint)
|
||||
fmt.Fprintf(b, "%sview.setUint16(pos, %s, true);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSDeserializeField(b *strings.Builder, recv string, f parser.Field, indent string, enumNames map[string]struct{}) error {
|
||||
access := recv + "." + toCamelCase(f.Name)
|
||||
switch f.Kind {
|
||||
case parser.KindPrimitive:
|
||||
return writeTSDeserializePrimitive(b, access, f, indent, enumNames)
|
||||
case parser.KindNested:
|
||||
fmt.Fprintf(b, "%sconst [_%s, _n%s] = %s.deserialize(view, pos);\n", indent, f.Name, f.Name, f.TypeName)
|
||||
fmt.Fprintf(b, "%s%s = _%s;\n", indent, access, f.Name)
|
||||
fmt.Fprintf(b, "%spos += _n%s;\n", indent, f.Name)
|
||||
case parser.KindFixedArray:
|
||||
iVar := "_i" + f.Name
|
||||
fmt.Fprintf(b, "%s%s = new Array(%d);\n", indent, access, f.FixedLen)
|
||||
fmt.Fprintf(b, "%sfor (let %s = 0; %s < %d; %s++) {\n",
|
||||
indent, iVar, iVar, f.FixedLen, iVar)
|
||||
elemField := parser.Field{
|
||||
Name: f.Name + "[" + iVar + "]",
|
||||
Kind: f.Elem.Kind,
|
||||
Primitive: f.Elem.Primitive,
|
||||
NamedType: f.Elem.NamedType,
|
||||
Quant: f.Elem.Quant,
|
||||
TypeName: f.Elem.TypeName,
|
||||
Elem: f.Elem.Elem,
|
||||
FixedLen: f.Elem.FixedLen,
|
||||
}
|
||||
if err := writeTSDeserializeField(b, recv, elemField, indent+" ", enumNames); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(b, "%s}\n", indent)
|
||||
case parser.KindSlice:
|
||||
lenVar := "_len" + f.Name
|
||||
fmt.Fprintf(b, "%sconst %s = view.getUint16(pos, true);\n", indent, lenVar)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
fmt.Fprintf(b, "%s%s = new Array(%s);\n", indent, access, lenVar)
|
||||
iVar := "_i" + f.Name
|
||||
fmt.Fprintf(b, "%sfor (let %s = 0; %s < %s; %s++) {\n", indent, iVar, iVar, lenVar, iVar)
|
||||
elemField := parser.Field{
|
||||
Name: f.Name + "[" + iVar + "]",
|
||||
Kind: f.Elem.Kind,
|
||||
Primitive: f.Elem.Primitive,
|
||||
NamedType: f.Elem.NamedType,
|
||||
Quant: f.Elem.Quant,
|
||||
TypeName: f.Elem.TypeName,
|
||||
Elem: f.Elem.Elem,
|
||||
FixedLen: f.Elem.FixedLen,
|
||||
}
|
||||
if err := writeTSDeserializeFieldElement(b, recv, elemField, indent+" ", enumNames); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(b, "%s}\n", indent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSDeserializeFieldElement(b *strings.Builder, recv string, f parser.Field, indent string, enumNames map[string]struct{}) error {
|
||||
access := recv + "." + toCamelCase(f.Name)
|
||||
switch f.Kind {
|
||||
case parser.KindPrimitive:
|
||||
return writeTSDeserializePrimitiveElement(b, access, f, indent, enumNames)
|
||||
case parser.KindNested:
|
||||
fmt.Fprintf(b, "%sconst [_elem, _nElem] = %s.deserialize(view, pos);\n", indent, f.TypeName)
|
||||
fmt.Fprintf(b, "%s%s = _elem;\n", indent, access)
|
||||
fmt.Fprintf(b, "%spos += _nElem;\n", indent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSDeserializePrimitiveElement(b *strings.Builder, access string, f parser.Field, indent string, enumNames map[string]struct{}) error {
|
||||
if f.Quant != nil {
|
||||
return writeTSDeserializeQuantElement(b, access, f, indent)
|
||||
}
|
||||
|
||||
switch f.Primitive {
|
||||
case parser.KindFloat32:
|
||||
expr := fmt.Sprintf("view.getFloat32(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindFloat64:
|
||||
expr := fmt.Sprintf("view.getFloat64(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindInt8:
|
||||
expr := fmt.Sprintf("view.getInt8(pos)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindUint8:
|
||||
expr := fmt.Sprintf("view.getUint8(pos)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindBool:
|
||||
expr := fmt.Sprintf("view.getUint8(pos) !== 0")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindInt16:
|
||||
expr := fmt.Sprintf("view.getInt16(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
case parser.KindUint16:
|
||||
expr := fmt.Sprintf("view.getUint16(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
case parser.KindInt32:
|
||||
expr := fmt.Sprintf("view.getInt32(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindUint32:
|
||||
expr := fmt.Sprintf("view.getUint32(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindInt64:
|
||||
expr := fmt.Sprintf("view.getBigInt64(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindUint64:
|
||||
expr := fmt.Sprintf("view.getBigUint64(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindString:
|
||||
lenVar := "_slen" + sanitizeVarName(access)
|
||||
fmt.Fprintf(b, "%sconst %s = view.getUint16(pos, true);\n", indent, lenVar)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
expr := fmt.Sprintf("new TextDecoder().decode(new Uint8Array(view.buffer, pos, %s))", lenVar)
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
fmt.Fprintf(b, "%spos += %s;\n", indent, lenVar)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSDeserializePrimitive(b *strings.Builder, access string, f parser.Field, indent string, enumNames map[string]struct{}) error {
|
||||
if f.Quant != nil {
|
||||
return writeTSDeserializeQuant(b, access, f, indent)
|
||||
}
|
||||
|
||||
switch f.Primitive {
|
||||
case parser.KindFloat32:
|
||||
expr := fmt.Sprintf("view.getFloat32(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindFloat64:
|
||||
expr := fmt.Sprintf("view.getFloat64(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindInt8:
|
||||
expr := fmt.Sprintf("view.getInt8(pos)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindUint8:
|
||||
expr := fmt.Sprintf("view.getUint8(pos)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindBool:
|
||||
expr := fmt.Sprintf("view.getUint8(pos) !== 0")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
case parser.KindInt16:
|
||||
expr := fmt.Sprintf("view.getInt16(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
case parser.KindUint16:
|
||||
expr := fmt.Sprintf("view.getUint16(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
case parser.KindInt32:
|
||||
expr := fmt.Sprintf("view.getInt32(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindUint32:
|
||||
expr := fmt.Sprintf("view.getUint32(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 4;\n", indent))
|
||||
case parser.KindInt64:
|
||||
expr := fmt.Sprintf("view.getBigInt64(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindUint64:
|
||||
expr := fmt.Sprintf("view.getBigUint64(pos, true)")
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
b.WriteString(fmt.Sprintf("%spos += 8;\n", indent))
|
||||
case parser.KindString:
|
||||
lenVar := "_slen" + sanitizeVarName(access)
|
||||
fmt.Fprintf(b, "%sconst %s = view.getUint16(pos, true);\n", indent, lenVar)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
expr := fmt.Sprintf("new TextDecoder().decode(new Uint8Array(view.buffer, pos, %s))", lenVar)
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, enumNames))
|
||||
fmt.Fprintf(b, "%spos += %s;\n", indent, lenVar)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSDeserializeQuant(b *strings.Builder, access string, f parser.Field, indent string) error {
|
||||
q := f.Quant
|
||||
maxUint := q.MaxUint()
|
||||
varName := "_q" + sanitizeVarName(access)
|
||||
if q.Bits == 8 {
|
||||
fmt.Fprintf(b, "%sconst %s = view.getUint8(pos);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
expr := fmt.Sprintf("%s / %g * (%g - (%g)) + (%g)", varName, maxUint, q.Max, q.Min, q.Min)
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, nil))
|
||||
} else {
|
||||
fmt.Fprintf(b, "%sconst %s = view.getUint16(pos, true);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
expr := fmt.Sprintf("%s / %g * (%g - (%g)) + (%g)", varName, maxUint, q.Max, q.Min, q.Min)
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, nil))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTSDeserializeQuantElement(b *strings.Builder, access string, f parser.Field, indent string) error {
|
||||
q := f.Quant
|
||||
maxUint := q.MaxUint()
|
||||
varName := "_q" + sanitizeVarName(access)
|
||||
if q.Bits == 8 {
|
||||
fmt.Fprintf(b, "%sconst %s = view.getUint8(pos);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 1;\n", indent))
|
||||
expr := fmt.Sprintf("%s / %g * (%g - (%g)) + (%g)", varName, maxUint, q.Max, q.Min, q.Min)
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, nil))
|
||||
} else {
|
||||
fmt.Fprintf(b, "%sconst %s = view.getUint16(pos, true);\n", indent, varName)
|
||||
b.WriteString(fmt.Sprintf("%spos += 2;\n", indent))
|
||||
expr := fmt.Sprintf("%s / %g * (%g - (%g)) + (%g)", varName, maxUint, q.Max, q.Min, q.Min)
|
||||
fmt.Fprintf(b, "%s%s = %s;\n", indent, access, tsDeserializeValueExpr(expr, f, nil))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func tsTypeName(f parser.Field, enumNames map[string]struct{}) string {
|
||||
switch f.Kind {
|
||||
case parser.KindPrimitive:
|
||||
if tsIsEnumType(f, enumNames) {
|
||||
return f.NamedType
|
||||
}
|
||||
return tsPrimitiveTypeName(f.Primitive)
|
||||
case parser.KindNested:
|
||||
return f.TypeName
|
||||
case parser.KindFixedArray, parser.KindSlice:
|
||||
return tsTypeName(*f.Elem, enumNames) + "[]"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func tsPrimitiveTypeName(k parser.PrimitiveKind) string {
|
||||
switch k {
|
||||
case parser.KindFloat32, parser.KindFloat64:
|
||||
return "number"
|
||||
case parser.KindInt8, parser.KindInt16, parser.KindInt32, parser.KindUint8, parser.KindUint16, parser.KindUint32:
|
||||
return "number"
|
||||
case parser.KindInt64, parser.KindUint64:
|
||||
return "bigint"
|
||||
case parser.KindBool:
|
||||
return "boolean"
|
||||
case parser.KindString:
|
||||
return "string"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func tsDefaultValue(f parser.Field) string {
|
||||
switch f.Kind {
|
||||
case parser.KindPrimitive:
|
||||
if tsIsEnumType(f, nil) {
|
||||
return "0"
|
||||
}
|
||||
switch f.Primitive {
|
||||
case parser.KindFloat32, parser.KindFloat64, parser.KindInt8, parser.KindInt16, parser.KindInt32,
|
||||
parser.KindUint8, parser.KindUint16, parser.KindUint32:
|
||||
return "0"
|
||||
case parser.KindInt64, parser.KindUint64:
|
||||
return "0n"
|
||||
case parser.KindBool:
|
||||
return "false"
|
||||
case parser.KindString:
|
||||
return `""`
|
||||
}
|
||||
case parser.KindNested:
|
||||
return fmt.Sprintf("new %s()", f.TypeName)
|
||||
case parser.KindFixedArray:
|
||||
elemDefault := tsDefaultValue(*f.Elem)
|
||||
elemType := tsTypeName(*f.Elem, nil)
|
||||
return fmt.Sprintf("new Array<%s>(%d).fill(%s)", elemType, f.FixedLen, elemDefault)
|
||||
case parser.KindSlice:
|
||||
return "[]"
|
||||
}
|
||||
return "undefined"
|
||||
}
|
||||
|
||||
func tsSerializeValueExpr(access string, f parser.Field, enumNames map[string]struct{}) string {
|
||||
if !tsIsEnumType(f, enumNames) {
|
||||
return access
|
||||
}
|
||||
return access + " as " + tsPrimitiveTypeName(f.Primitive)
|
||||
}
|
||||
|
||||
func tsDeserializeValueExpr(expr string, f parser.Field, enumNames map[string]struct{}) string {
|
||||
if !tsIsEnumType(f, enumNames) {
|
||||
return expr
|
||||
}
|
||||
return "(" + expr + " as " + f.NamedType + ")"
|
||||
}
|
||||
|
||||
func tsIsEnumType(f parser.Field, enumNames map[string]struct{}) bool {
|
||||
if f.NamedType == "" || enumNames == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := enumNames[f.NamedType]
|
||||
return ok
|
||||
}
|
||||
|
||||
// toCamelCase converts PascalCase to camelCase (e.g., EntityID -> entityID)
|
||||
func toCamelCase(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
// First character to lowercase
|
||||
result := strings.ToLower(s[:1]) + s[1:]
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/edmand46/arpack/parser"
|
||||
)
|
||||
|
||||
func TestGenerateTypeScript_Primitives(t *testing.T) {
|
||||
schema := parser.Schema{
|
||||
Messages: []parser.Message{
|
||||
{
|
||||
PackageName: "test",
|
||||
Name: "PrimitiveMessage",
|
||||
Fields: []parser.Field{
|
||||
{Name: "F32", Kind: parser.KindPrimitive, Primitive: parser.KindFloat32},
|
||||
{Name: "F64", Kind: parser.KindPrimitive, Primitive: parser.KindFloat64},
|
||||
{Name: "I8", Kind: parser.KindPrimitive, Primitive: parser.KindInt8},
|
||||
{Name: "I16", Kind: parser.KindPrimitive, Primitive: parser.KindInt16},
|
||||
{Name: "I32", Kind: parser.KindPrimitive, Primitive: parser.KindInt32},
|
||||
{Name: "I64", Kind: parser.KindPrimitive, Primitive: parser.KindInt64},
|
||||
{Name: "U8", Kind: parser.KindPrimitive, Primitive: parser.KindUint8},
|
||||
{Name: "U16", Kind: parser.KindPrimitive, Primitive: parser.KindUint16},
|
||||
{Name: "U32", Kind: parser.KindPrimitive, Primitive: parser.KindUint32},
|
||||
{Name: "U64", Kind: parser.KindPrimitive, Primitive: parser.KindUint64},
|
||||
{Name: "B", Kind: parser.KindPrimitive, Primitive: parser.KindBool},
|
||||
{Name: "S", Kind: parser.KindPrimitive, Primitive: parser.KindString},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
src, err := GenerateTypeScriptSchema(schema, "Test")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateTypeScriptSchema: %v", err)
|
||||
}
|
||||
|
||||
code := string(src)
|
||||
|
||||
// Check field declarations (now using camelCase)
|
||||
if !strings.Contains(code, "f32: number = 0;") {
|
||||
t.Error("Missing f32 field")
|
||||
}
|
||||
if !strings.Contains(code, "i64: bigint = 0n;") {
|
||||
t.Error("Missing i64 field with bigint type")
|
||||
}
|
||||
if !strings.Contains(code, "u64: bigint = 0n;") {
|
||||
t.Error("Missing u64 field with bigint type")
|
||||
}
|
||||
if !strings.Contains(code, "b: boolean = false;") {
|
||||
t.Error("Missing b field")
|
||||
}
|
||||
if !strings.Contains(code, "s: string = \"\";") {
|
||||
t.Error("Missing s field")
|
||||
}
|
||||
|
||||
// Check serialize method exists
|
||||
if !strings.Contains(code, "serialize(view: DataView, offset: number): number") {
|
||||
t.Error("Missing serialize method")
|
||||
}
|
||||
|
||||
// Check deserialize method exists
|
||||
if !strings.Contains(code, "static deserialize(view: DataView, offset: number): [PrimitiveMessage, number]") {
|
||||
t.Error("Missing deserialize method")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateTypeScript_QuantizedFloats(t *testing.T) {
|
||||
schema := parser.Schema{
|
||||
Messages: []parser.Message{
|
||||
{
|
||||
PackageName: "test",
|
||||
Name: "QuantMessage",
|
||||
Fields: []parser.Field{
|
||||
{
|
||||
Name: "Q8",
|
||||
Kind: parser.KindPrimitive,
|
||||
Primitive: parser.KindFloat32,
|
||||
Quant: &parser.QuantInfo{Min: 0, Max: 100, Bits: 8},
|
||||
},
|
||||
{
|
||||
Name: "Q16",
|
||||
Kind: parser.KindPrimitive,
|
||||
Primitive: parser.KindFloat32,
|
||||
Quant: &parser.QuantInfo{Min: -500, Max: 500, Bits: 16},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
src, err := GenerateTypeScriptSchema(schema, "Test")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateTypeScriptSchema: %v", err)
|
||||
}
|
||||
|
||||
code := string(src)
|
||||
|
||||
// Check 8-bit quantization (using camelCase field names)
|
||||
if !strings.Contains(code, "Math.round((this.q8 - (0)) / (100 - (0)) * 255)") {
|
||||
t.Error("Missing 8-bit quantization code")
|
||||
}
|
||||
|
||||
// Check 16-bit quantization (using camelCase field names)
|
||||
if !strings.Contains(code, "Math.round((this.q16 - (-500)) / (500 - (-500)) * 65535)") {
|
||||
t.Error("Missing 16-bit quantization code")
|
||||
}
|
||||
|
||||
// Check deserialization with dequantization
|
||||
if !strings.Contains(code, "/ 255 * (100 - (0)) + (0)") {
|
||||
t.Error("Missing 8-bit dequantization")
|
||||
}
|
||||
if !strings.Contains(code, "/ 65535 * (500 - (-500)) + (-500)") {
|
||||
t.Error("Missing 16-bit dequantization")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateTypeScript_BoolPacking(t *testing.T) {
|
||||
schema := parser.Schema{
|
||||
Messages: []parser.Message{
|
||||
{
|
||||
PackageName: "test",
|
||||
Name: "BoolMessage",
|
||||
Fields: []parser.Field{
|
||||
{Name: "A", Kind: parser.KindPrimitive, Primitive: parser.KindBool},
|
||||
{Name: "B", Kind: parser.KindPrimitive, Primitive: parser.KindBool},
|
||||
{Name: "C", Kind: parser.KindPrimitive, Primitive: parser.KindBool},
|
||||
{Name: "X", Kind: parser.KindPrimitive, Primitive: parser.KindUint32},
|
||||
{Name: "D", Kind: parser.KindPrimitive, Primitive: parser.KindBool},
|
||||
{Name: "E", Kind: parser.KindPrimitive, Primitive: parser.KindBool},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
src, err := GenerateTypeScriptSchema(schema, "Test")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateTypeScriptSchema: %v", err)
|
||||
}
|
||||
|
||||
code := string(src)
|
||||
|
||||
// Check that consecutive bools are packed (using camelCase field names)
|
||||
if !strings.Contains(code, "let _boolByte0 = 0;") {
|
||||
t.Error("Missing first bool group packing")
|
||||
}
|
||||
if !strings.Contains(code, "if (this.a) _boolByte0 |= 1 << 0;") {
|
||||
t.Error("Missing a bool packing")
|
||||
}
|
||||
if !strings.Contains(code, "if (this.b) _boolByte0 |= 1 << 1;") {
|
||||
t.Error("Missing b bool packing")
|
||||
}
|
||||
if !strings.Contains(code, "if (this.c) _boolByte0 |= 1 << 2;") {
|
||||
t.Error("Missing c bool packing")
|
||||
}
|
||||
|
||||
// Check second bool group after uint32 (index is 2, not 4, based on segment index)
|
||||
if !strings.Contains(code, "let _boolByte2 = 0;") {
|
||||
t.Error("Missing second bool group packing")
|
||||
}
|
||||
|
||||
// Check deserialization (using camelCase field names)
|
||||
if !strings.Contains(code, "msg.a = (_boolByte0 & (1 << 0)) !== 0;") {
|
||||
t.Error("Missing a bool unpacking")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateTypeScript_NestedTypes(t *testing.T) {
|
||||
schema := parser.Schema{
|
||||
Messages: []parser.Message{
|
||||
{
|
||||
PackageName: "test",
|
||||
Name: "Inner",
|
||||
Fields: []parser.Field{
|
||||
{Name: "Value", Kind: parser.KindPrimitive, Primitive: parser.KindInt32},
|
||||
},
|
||||
},
|
||||
{
|
||||
PackageName: "test",
|
||||
Name: "Outer",
|
||||
Fields: []parser.Field{
|
||||
{Name: "Inner", Kind: parser.KindNested, TypeName: "Inner"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
src, err := GenerateTypeScriptSchema(schema, "Test")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateTypeScriptSchema: %v", err)
|
||||
}
|
||||
|
||||
code := string(src)
|
||||
|
||||
// Check nested type default value (using camelCase field name)
|
||||
if !strings.Contains(code, "inner: Inner = new Inner();") {
|
||||
t.Error("Missing nested type field with default")
|
||||
}
|
||||
|
||||
// Check serialize calls nested serialize (using camelCase field name)
|
||||
if !strings.Contains(code, "pos += this.inner.serialize(view, pos);") {
|
||||
t.Error("Missing nested serialize call")
|
||||
}
|
||||
|
||||
// Check deserialize calls nested deserialize
|
||||
if !strings.Contains(code, "const [_Inner, _nInner] = Inner.deserialize(view, pos);") {
|
||||
t.Error("Missing nested deserialize call")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateTypeScript_FixedArrays(t *testing.T) {
|
||||
schema := parser.Schema{
|
||||
Messages: []parser.Message{
|
||||
{
|
||||
PackageName: "test",
|
||||
Name: "ArrayMessage",
|
||||
Fields: []parser.Field{
|
||||
{
|
||||
Name: "Values",
|
||||
Kind: parser.KindFixedArray,
|
||||
FixedLen: 3,
|
||||
Elem: &parser.Field{
|
||||
Kind: parser.KindPrimitive,
|
||||
Primitive: parser.KindFloat32,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
src, err := GenerateTypeScriptSchema(schema, "Test")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateTypeScriptSchema: %v", err)
|
||||
}
|
||||
|
||||
code := string(src)
|
||||
|
||||
// Check default value (using camelCase field name)
|
||||
if !strings.Contains(code, "values: number[] = new Array<number>(3).fill(0);") {
|
||||
t.Error("Missing fixed array field with default")
|
||||
}
|
||||
|
||||
// Check serialization loop (using camelCase field name)
|
||||
if !strings.Contains(code, "for (let _iValues = 0; _iValues < 3; _iValues++)") {
|
||||
t.Error("Missing fixed array serialization loop")
|
||||
}
|
||||
|
||||
// Check deserialization loop (using camelCase field name)
|
||||
if !strings.Contains(code, "msg.values = new Array(3);") {
|
||||
t.Error("Missing fixed array allocation in deserialize")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateTypeScript_Slices(t *testing.T) {
|
||||
schema := parser.Schema{
|
||||
Messages: []parser.Message{
|
||||
{
|
||||
PackageName: "test",
|
||||
Name: "SliceMessage",
|
||||
Fields: []parser.Field{
|
||||
{
|
||||
Name: "Items",
|
||||
Kind: parser.KindSlice,
|
||||
Elem: &parser.Field{
|
||||
Kind: parser.KindPrimitive,
|
||||
Primitive: parser.KindInt32,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
src, err := GenerateTypeScriptSchema(schema, "Test")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateTypeScriptSchema: %v", err)
|
||||
}
|
||||
|
||||
code := string(src)
|
||||
|
||||
// Check default value (using camelCase field name)
|
||||
if !strings.Contains(code, "items: number[] = [];") {
|
||||
t.Error("Missing slice field with default")
|
||||
}
|
||||
|
||||
// Check length prefix in serialize (using camelCase field name)
|
||||
if !strings.Contains(code, "view.setUint16(pos, this.items.length, true);") {
|
||||
t.Error("Missing slice length prefix in serialize")
|
||||
}
|
||||
|
||||
// Check length reading in deserialize
|
||||
if !strings.Contains(code, "const _lenItems = view.getUint16(pos, true);") {
|
||||
t.Error("Missing slice length reading in deserialize")
|
||||
}
|
||||
|
||||
// Check array allocation in deserialize (using camelCase field name)
|
||||
if !strings.Contains(code, "msg.items = new Array(_lenItems);") {
|
||||
t.Error("Missing slice allocation in deserialize")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateTypeScript_Enums(t *testing.T) {
|
||||
schema := parser.Schema{
|
||||
Enums: []parser.Enum{
|
||||
{
|
||||
Name: "Status",
|
||||
Primitive: parser.KindUint16,
|
||||
Values: []parser.EnumValue{
|
||||
{Name: "Pending", Value: "0"},
|
||||
{Name: "Active", Value: "1"},
|
||||
{Name: "Done", Value: "2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Messages: []parser.Message{
|
||||
{
|
||||
PackageName: "test",
|
||||
Name: "EnumMessage",
|
||||
Fields: []parser.Field{
|
||||
{
|
||||
Name: "Status",
|
||||
Kind: parser.KindPrimitive,
|
||||
Primitive: parser.KindUint16,
|
||||
NamedType: "Status",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
src, err := GenerateTypeScriptSchema(schema, "Test")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateTypeScriptSchema: %v", err)
|
||||
}
|
||||
|
||||
code := string(src)
|
||||
|
||||
// Check enum definition
|
||||
if !strings.Contains(code, "export enum Status {") {
|
||||
t.Error("Missing enum definition")
|
||||
}
|
||||
if !strings.Contains(code, "Pending = 0,") {
|
||||
t.Error("Missing Pending enum value")
|
||||
}
|
||||
if !strings.Contains(code, "Active = 1,") {
|
||||
t.Error("Missing Active enum value")
|
||||
}
|
||||
|
||||
// Check enum field type (using camelCase field name)
|
||||
if !strings.Contains(code, "status: Status = 0;") {
|
||||
t.Error("Missing enum field with correct type")
|
||||
}
|
||||
|
||||
// Check enum serialization (cast to number, using camelCase field name)
|
||||
if !strings.Contains(code, "view.setUint16(pos, this.status as number, true);") {
|
||||
t.Error("Missing enum cast in serialize")
|
||||
}
|
||||
|
||||
// Check enum deserialization (cast from number, using camelCase field name)
|
||||
if !strings.Contains(code, "msg.status = (view.getUint16(pos, true) as Status);") {
|
||||
t.Error("Missing enum cast in deserialize")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateTypeScript_Strings(t *testing.T) {
|
||||
schema := parser.Schema{
|
||||
Messages: []parser.Message{
|
||||
{
|
||||
PackageName: "test",
|
||||
Name: "StringMessage",
|
||||
Fields: []parser.Field{
|
||||
{Name: "Name", Kind: parser.KindPrimitive, Primitive: parser.KindString},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
src, err := GenerateTypeScriptSchema(schema, "Test")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateTypeScriptSchema: %v", err)
|
||||
}
|
||||
|
||||
code := string(src)
|
||||
|
||||
// Check TextEncoder usage
|
||||
if !strings.Contains(code, "new TextEncoder().encode(") {
|
||||
t.Error("Missing TextEncoder in serialize")
|
||||
}
|
||||
|
||||
// Check length prefix
|
||||
if !strings.Contains(code, "view.setUint16(pos, _slen") {
|
||||
t.Error("Missing string length prefix in serialize")
|
||||
}
|
||||
|
||||
// Check TextDecoder usage
|
||||
if !strings.Contains(code, "new TextDecoder().decode(") {
|
||||
t.Error("Missing TextDecoder in deserialize")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user