package generate // import "fmt" import "strings" import "testing" import "git.tebibyte.media/sashakoshka/goparse" var testGenerateCorrect = `package protocol /* # Do not edit this package by hand! * * This file was automatically generated by the Holanet PDL compiler. The * source file is located at input.pdl * Please edit that file instead, and re-compile it to this location. * * HOPP, TAPE, METADAPT, PDL/0 (c) 2025 holanet.xyz */ import "git.tebibyte.media/sashakoshka/hopp/tape" // Table is a KTV table with an undefined schema. type Table map[uint16] any // Message is any message that can be sent along this protocol. type Message interface { tape.Encodable tape.Decodable // Method returns the method code of the message. Method() uint16 } // User represents the protocol data type User. type User struct { Name string Bio string Followers uint32 } // EncodeValue encodes the value of this type without the tag. The value is // encoded according to the parameters specified by the tag, if possible. func (this *User) EncodeValue(encoder *tape.Encoder) (n int, err error) { nn, err := tape.WriteTableHeader(2) n += nn; if err != nil { return n, err } nn, err := encoder.WriteUint16(0x0000) n += nn; if err != nil { return n, err } nn, err := tape.WriteString(encoder, this.Name) n += nn; if err != nil { return n, err } nn, err := encoder.WriteUint16(0x0001) n += nn; if err != nil { return n, err } nn, err := tape.WriteString(encoder, this.Bio) n += nn; if err != nil { return n, err } return n, nil } // Decode replaces the data in this User with information from the decoder. func (this *User) Decode(decoder *tape.Decoder) (n int, err error) { pull, nn, err := tape.ReadTableHeader(decoder) n += nn; if err != nil { return n, err } for { key, tag, end, nn, err := pull() n += nn; if err != nil { return n, err } if end { break } switch key { case 0x0000: value, nn, err := tape.ReadString(decoder) n += nn; if err != nil { return n, err } this.Name = value case 0x0001: value, nn, err := tape.ReadString(decoder) n += nn; if err != nil { return n, err } this.Bio = value } } return n, nil } // MessageConnect represents the protocol message M0000 Connect. type MessageConnect struct { Name string Password string } // Method returns the method code, M0000. func (this *MessageConnect) Method() uint16 { return 0x0000 } // Encode encodes the message to the encoder. func (this *MessageConnect) Encode(encoder *tape.Encoder) (n int, err error) { nn, err := tape.WriteTableHeader(2) n += nn; if err != nil { return n, err } nn, err := encoder.WriteUint16(0x0000) n += nn; if err != nil { return n, err } nn, err := tape.WriteString(encoder, this.Name) n += nn; if err != nil { return n, err } nn, err := encoder.WriteUint16(0x0001) n += nn; if err != nil { return n, err } nn, err := tape.WriteString(encoder, this.Password) n += nn; if err != nil { return n, err } return n, nil } // Decode replaces the data in this message with information from the decoder. func (this *MessageConnect) Decode(decoder *tape.Decoder) (n int, err error) { pull, nn, err := tape.ReadTableHeader(decoder) n += nn; if err != nil { return n, err } for { key, tag, end, nn, err := pull() n += nn; if err != nil { return n, err } if end { break } switch key { case 0x0000: value, nn, err := tape.ReadString(decoder) n += nn; if err != nil { return n, err } this.Name = value case 0x0001: value, nn, err := tape.ReadString(decoder) n += nn; if err != nil { return n, err } this.Password = value } } return n, nil } // MessageUserList represents the protocol message M0001 UserList. type MessageUserList struct { Users []User } // Method returns the method code, M0001. func (this *MessageUserList) Method() uint16 { return 0x0001 } // TODO methods ` func TestGenerate(test *testing.T) { protocol := defaultProtocol() protocol.Messages[0x0000] = Message { Name: "Connect", Type: TypeTableDefined { Fields: map[uint16] Field { 0x0000: Field { Name: "Name", Type: TypeString { } }, 0x0001: Field { Name: "Password", Type: TypeString { } }, }, }, } protocol.Messages[0x0001] = Message { Name: "UserList", Type: TypeTableDefined { Fields: map[uint16] Field { 0x0000: Field { Name: "Users", Type: TypeArray { Element: TypeNamed { Name: "User" } } }, }, }, } protocol.Types["User"] = TypeTableDefined { Fields: map[uint16] Field { 0x0000: Field { Name: "Name", Type: TypeString { } }, 0x0001: Field { Name: "Bio", Type: TypeString { } }, 0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } }, }, } correct := testGenerateCorrect builder := strings.Builder { } generator := Generator { Output: &builder } /* TODO test n: */ _, err := generator.Generate(&protocol) if err != nil { test.Fatal(parse.Format(err)) } got := builder.String() test.Log("CORRECT:") test.Log(correct) test.Log("GOT:") test.Log(got) if correct != got { test.Error("not equal") for index := range min(len(correct), len(got)) { if correct[index] == got[index] { continue } test.Log("C:", correct[max(0, index - 8):min(len(correct), index + 8)]) test.Log("G:", got[max(0, index - 8):min(len(got), index + 8)]) break } test.FailNow() } } func TestGenerateRun(test *testing.T) { protocol := defaultProtocol() protocol.Messages[0x0000] = Message { Name: "Connect", Type: TypeTableDefined { Fields: map[uint16] Field { 0x0000: Field { Name: "Name", Type: TypeString { } }, 0x0001: Field { Name: "Password", Type: TypeString { } }, }, }, } protocol.Messages[0x0001] = Message { Name: "UserList", Type: TypeTableDefined { Fields: map[uint16] Field { 0x0000: Field { Name: "Users", Type: TypeArray { Element: TypeNamed { Name: "User" } } }, }, }, } protocol.Types["User"] = TypeTableDefined { Fields: map[uint16] Field { 0x0000: Field { Name: "Name", Type: TypeString { } }, 0x0001: Field { Name: "Bio", Type: TypeString { } }, 0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } }, }, } testGenerateRun(test, &protocol, ` // imports `, ` // test case messageConnect := MessageConnect { Name: "rarity", Password: "gems", } testEncode( &messageConnect, 0x0) // TODO `) }