MONOREPO
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
|
||||
func TypeToPacketIdentifier(d queser.Direction, s queser.State, t any) (i string) {
|
||||
switch d {
|
||||
case queser.ToServer:
|
||||
switch s {
|
||||
case queser.Handshaking:
|
||||
i = HandshakingToServerTypeToPacketIdentifier(t)
|
||||
case queser.Status:
|
||||
i = StatusToServerTypeToPacketIdentifier(t)
|
||||
case queser.Login:
|
||||
i = LoginToServerTypeToPacketIdentifier(t)
|
||||
case queser.Configuration:
|
||||
i = ConfigurationToServerTypeToPacketIdentifier(t)
|
||||
case queser.Play:
|
||||
i = PlayToServerTypeToPacketIdentifier(t)
|
||||
}
|
||||
case queser.ToClient:
|
||||
switch s {
|
||||
case queser.Handshaking:
|
||||
i = HandshakingToClientTypeToPacketIdentifier(t)
|
||||
case queser.Status:
|
||||
i = StatusToClientTypeToPacketIdentifier(t)
|
||||
case queser.Login:
|
||||
i = LoginToClientTypeToPacketIdentifier(t)
|
||||
case queser.Configuration:
|
||||
i = ConfigurationToClientTypeToPacketIdentifier(t)
|
||||
case queser.Play:
|
||||
i = PlayToClientTypeToPacketIdentifier(t)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PacketIdentifierToType(d queser.Direction, s queser.State, i string) (t any) {
|
||||
switch d {
|
||||
case queser.ToServer:
|
||||
switch s {
|
||||
case queser.Handshaking:
|
||||
t = HandshakingToServerPacketIdentifierToType(i)
|
||||
case queser.Status:
|
||||
t = StatusToServerPacketIdentifierToType(i)
|
||||
case queser.Login:
|
||||
t = LoginToServerPacketIdentifierToType(i)
|
||||
case queser.Configuration:
|
||||
t = ConfigurationToServerPacketIdentifierToType(i)
|
||||
case queser.Play:
|
||||
t = PlayToServerPacketIdentifierToType(i)
|
||||
}
|
||||
case queser.ToClient:
|
||||
switch s {
|
||||
case queser.Handshaking:
|
||||
t = HandshakingToClientPacketIdentifierToType(i)
|
||||
case queser.Status:
|
||||
t = StatusToClientPacketIdentifierToType(i)
|
||||
case queser.Login:
|
||||
t = LoginToClientPacketIdentifierToType(i)
|
||||
case queser.Configuration:
|
||||
t = ConfigurationToClientPacketIdentifierToType(i)
|
||||
case queser.Play:
|
||||
t = PlayToClientPacketIdentifierToType(i)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DecodePacket(d queser.Direction, s queser.State, r io.Reader) (params any, err error) {
|
||||
switch d {
|
||||
case queser.ToServer:
|
||||
switch s {
|
||||
case queser.Handshaking:
|
||||
var packet HandshakingToServerPacket
|
||||
packet, err = HandshakingToServerPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
case queser.Status:
|
||||
var packet StatusToServerPacket
|
||||
packet, err = StatusToServerPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
case queser.Login:
|
||||
var packet LoginToServerPacket
|
||||
packet, err = LoginToServerPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
case queser.Configuration:
|
||||
var packet ConfigurationToServerPacket
|
||||
packet, err = ConfigurationToServerPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
case queser.Play:
|
||||
var packet PlayToServerPacket
|
||||
packet, err = PlayToServerPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
}
|
||||
case queser.ToClient:
|
||||
switch s {
|
||||
case queser.Handshaking:
|
||||
var packet HandshakingToClientPacket
|
||||
packet, err = HandshakingToClientPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
case queser.Status:
|
||||
var packet StatusToClientPacket
|
||||
packet, err = StatusToClientPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
case queser.Login:
|
||||
var packet LoginToClientPacket
|
||||
packet, err = LoginToClientPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
case queser.Configuration:
|
||||
var packet ConfigurationToClientPacket
|
||||
packet, err = ConfigurationToClientPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
case queser.Play:
|
||||
var packet PlayToClientPacket
|
||||
packet, err = PlayToClientPacket{}.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params = packet.Params
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func EncodePacket(d queser.Direction, s queser.State, i string, p any, w io.Writer) (err error) {
|
||||
switch d {
|
||||
case queser.ToServer:
|
||||
switch s {
|
||||
case queser.Handshaking:
|
||||
err = HandshakingToServerPacket{i, p}.Encode(w)
|
||||
case queser.Status:
|
||||
err = StatusToServerPacket{i, p}.Encode(w)
|
||||
case queser.Login:
|
||||
err = LoginToServerPacket{i, p}.Encode(w)
|
||||
case queser.Configuration:
|
||||
err = ConfigurationToServerPacket{i, p}.Encode(w)
|
||||
case queser.Play:
|
||||
err = PlayToServerPacket{i, p}.Encode(w)
|
||||
}
|
||||
case queser.ToClient:
|
||||
switch s {
|
||||
case queser.Handshaking:
|
||||
err = HandshakingToClientPacket{i, p}.Encode(w)
|
||||
case queser.Status:
|
||||
err = StatusToClientPacket{i, p}.Encode(w)
|
||||
case queser.Login:
|
||||
err = LoginToClientPacket{i, p}.Encode(w)
|
||||
case queser.Configuration:
|
||||
err = ConfigurationToClientPacket{i, p}.Encode(w)
|
||||
case queser.Play:
|
||||
err = PlayToClientPacket{i, p}.Encode(w)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
_ "embed"
|
||||
|
||||
"github.com/admin-else/queser/data"
|
||||
)
|
||||
|
||||
//go:embed helper_append.txt
|
||||
var helperAppend string
|
||||
|
||||
func GenerateHelperTypes(t Types, prefix string, f *ast.File) (err error) {
|
||||
for k, v := range t.Types {
|
||||
if k != "packet" {
|
||||
continue
|
||||
}
|
||||
v := v.([]any)[1].([]any)[1].(map[string]any)["type"].([]any)[1].(map[string]any)["fields"].(map[string]any)
|
||||
m := make(map[string]string)
|
||||
for k2, v2 := range v {
|
||||
v2 := v2.(string)
|
||||
if strings.HasPrefix(v2, "packet_common") {
|
||||
m[k2] = CamelCase(v2)
|
||||
} else if v2 == "void" {
|
||||
m[k2] = "queser.Void"
|
||||
} else {
|
||||
m[k2] = prefix + CamelCase(v2)
|
||||
}
|
||||
}
|
||||
err = PacketIdentifierToTypeGenerator(prefix, f, m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m = ReverseMap(m)
|
||||
err = TypeToPacketIdentifierGenerator(prefix, f, m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateHelperState(s State, prefix string, f *ast.File) (err error) {
|
||||
err = GenerateHelperTypes(s.ToServer, prefix+"ToServer", f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return GenerateHelperTypes(s.ToClient, prefix+"ToClient", f)
|
||||
}
|
||||
|
||||
func GenerateHelperProtocol(p Protocol, f *ast.File) (err error) {
|
||||
err = GenerateHelperState(p.Handshaking, "Handshaking", f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = GenerateHelperState(p.Status, "Status", f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = GenerateHelperState(p.Login, "Login", f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = GenerateHelperState(p.Configuration, "Configuration", f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = GenerateHelperState(p.Play, "Play", f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PacketIdentifierToTypeGenerator func PacketIdentifierToType(s string) any {
|
||||
// switch s {
|
||||
// case "abilities":
|
||||
// return PlayToClientPacketAbilities{}
|
||||
// default:
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
func PacketIdentifierToTypeGenerator(funcNamePrefix string, f *ast.File, m map[string]string) (err error) {
|
||||
var s []ast.Stmt
|
||||
for _, k := range OrderedKeys(m) {
|
||||
v := m[k]
|
||||
s = append(s, Case(Exprs(StrLit(k)), Stmts(Assign121(Ident("t"), CompLit(Ident(v), nil)))))
|
||||
}
|
||||
s = append(s, Case(nil, Stmts(Assign121(Ident("t"), Nil()))))
|
||||
fn := NewFunc(funcNamePrefix+"PacketIdentifierToType", []NameAndType{{"s", Ident("string")}}, []NameAndType{{"t", Ident("any")}})
|
||||
fn.Body = NewBlockEllipsis(SwitchStmt(Ident("s"), NewBlock(s)), Return())
|
||||
AppendDecl(f, fn)
|
||||
return
|
||||
}
|
||||
|
||||
func TypeToPacketIdentifierGenerator(funcNamePrefix string, f *ast.File, m map[string]string) (err error) {
|
||||
var s []ast.Stmt
|
||||
for _, k := range OrderedKeys(m) {
|
||||
v := m[k]
|
||||
s = append(s, Case(Exprs(Ident(k)), Stmts(Assign121(Ident("s"), StrLit(v)))))
|
||||
}
|
||||
fn := NewFunc(funcNamePrefix+"TypeToPacketIdentifier", []NameAndType{{"t", Ident("any")}}, []NameAndType{{"s", Ident("string")}})
|
||||
fn.Body = NewBlockEllipsis(TypeSwitch(ExprStmt(TypeAssert(Ident("t"), Ident("type"))), NewBlock(s)), Return())
|
||||
AppendDecl(f, fn)
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateHelpers(version string, w io.Writer) (err error) {
|
||||
protocol := Protocol{}
|
||||
err = data.LoadVersionedJson(version, "protocol", &protocol)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f := NewFile("v" + strings.ReplaceAll(version, ".", "_"))
|
||||
AppendDecl(f, Import("io", "github.com/admin-else/queser"))
|
||||
err = GenerateHelperProtocol(protocol, f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = PrintToFile(f, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write([]byte(helperAppend))
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/admin-else/queser/data"
|
||||
)
|
||||
|
||||
var ToDoError = errors.New("to do")
|
||||
|
||||
type Types struct {
|
||||
Types map[string]any
|
||||
}
|
||||
|
||||
type State struct {
|
||||
ToClient, ToServer Types
|
||||
}
|
||||
|
||||
type Protocol struct {
|
||||
Types
|
||||
Handshaking, Status, Login, Configuration, Play State
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
ReplaceWithTodoNames []string
|
||||
}
|
||||
|
||||
type ContainerStackEntry struct {
|
||||
Data []struct {
|
||||
Name string
|
||||
Type any
|
||||
Anon bool
|
||||
}
|
||||
VarToSet ast.Expr
|
||||
}
|
||||
|
||||
type Generator struct {
|
||||
// These persist the entire generate-call
|
||||
Settings Settings
|
||||
Natives map[string]ExprGeneratorFunc
|
||||
DecoderNatives map[string]FunctionGeneratorFunc
|
||||
EncoderNatives map[string]FunctionGeneratorFunc
|
||||
CompareToNatives map[string]CompareToGeneratorFunc
|
||||
Protocol Protocol
|
||||
File *ast.File
|
||||
|
||||
// These persist in the current GenerateType call
|
||||
CurrentlyGeneratingTypes Types
|
||||
|
||||
// These persist 1 type generation
|
||||
CurrentlyGeneratingTypesPrefix string
|
||||
CurrentlyGeneratingTypeName string
|
||||
|
||||
// Change during type generation
|
||||
Depth int
|
||||
Declared []string
|
||||
ContainerStack []ContainerStackEntry
|
||||
VtsStack []ast.Expr
|
||||
}
|
||||
|
||||
func (g *Generator) Decl(name string, t token.Token, e ast.Expr) {
|
||||
if slices.Contains(g.Declared, name) {
|
||||
return
|
||||
}
|
||||
g.Declared = append(g.Declared, name)
|
||||
AppendDecl(g.File, &ast.GenDecl{
|
||||
Tok: t,
|
||||
Specs: []ast.Spec{
|
||||
&ast.ValueSpec{
|
||||
Names: []*ast.Ident{
|
||||
Ident(name),
|
||||
},
|
||||
Values: []ast.Expr{e},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func ParseType(t any) (tName string, tData any, err error) {
|
||||
switch t := t.(type) {
|
||||
case string:
|
||||
tName = t
|
||||
return
|
||||
case []any:
|
||||
if len(t) != 2 {
|
||||
err = fmt.Errorf("unable to parse type %v", t)
|
||||
return
|
||||
}
|
||||
var ok bool
|
||||
tName, ok = t[0].(string)
|
||||
if !ok {
|
||||
err = fmt.Errorf("unable to parse type %v", t)
|
||||
return
|
||||
}
|
||||
tData = t[1]
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("unable to parse type %v", t)
|
||||
return
|
||||
}
|
||||
|
||||
type ExprGeneratorFunc func(g *Generator, data any) (ast.Expr, error)
|
||||
|
||||
type CompareToGeneratorFunc func(g *Generator, parts []string, inExpr ast.Expr, data any) (ast.Expr, error)
|
||||
|
||||
type FunctionGeneratorFunc func(g *Generator, varToSet ast.Expr, data any, name string) ([]ast.Stmt, error)
|
||||
|
||||
func (g *Generator) VisitType(data any) (e ast.Expr, err error) {
|
||||
tName, tData, err := ParseType(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitNameAndData(tName, tData)
|
||||
}
|
||||
|
||||
func (g *Generator) VisitNameAndData(tName string, tData any) (e ast.Expr, err error) {
|
||||
g.Depth += 1
|
||||
defer func() { g.Depth -= 1 }()
|
||||
n, found := g.Natives[tName]
|
||||
if found {
|
||||
return n(g, tData)
|
||||
}
|
||||
t, found := g.Protocol.Types.Types[tName]
|
||||
if t != "native" && found {
|
||||
return Ident(CamelCase(tName)), nil
|
||||
}
|
||||
t, found = g.CurrentlyGeneratingTypes.Types[tName]
|
||||
if found {
|
||||
return Ident(g.CurrentlyGeneratingTypesPrefix + CamelCase(tName)), nil
|
||||
}
|
||||
err = fmt.Errorf("unknown type %s", tName)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (g *Generator) VisitDecoder(varToSet ast.Expr, data any, name string) (e []ast.Stmt, err error) {
|
||||
g.Depth += 1
|
||||
defer func() { g.Depth -= 1 }()
|
||||
|
||||
tName, tData, err := ParseType(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
d, found := g.DecoderNatives[tName]
|
||||
if found {
|
||||
return d(g, varToSet, tData, name)
|
||||
}
|
||||
t, found := g.Protocol.Types.Types[tName]
|
||||
if t == "native" {
|
||||
err = fmt.Errorf("decoder native not implemented %v", tName)
|
||||
return
|
||||
}
|
||||
return DefaultDecoder(g, varToSet, tData, name)
|
||||
}
|
||||
|
||||
func (g *Generator) VisitEncoder(varToSet ast.Expr, data any, name string) (e []ast.Stmt, err error) {
|
||||
g.Depth += 1
|
||||
defer func() { g.Depth -= 1 }()
|
||||
|
||||
tName, tData, err := ParseType(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
d, found := g.EncoderNatives[tName]
|
||||
if found {
|
||||
return d(g, varToSet, tData, name)
|
||||
}
|
||||
t, found := g.Protocol.Types.Types[tName]
|
||||
if t == "native" {
|
||||
err = fmt.Errorf("encoder native not implemented %v", tName)
|
||||
return
|
||||
}
|
||||
return DefaultEncoder(g, varToSet, tData, name)
|
||||
}
|
||||
|
||||
// FixPointerReceivers
|
||||
// We cannot declare a function on a ptr type as described by https://go.dev/ref/spec#Method_declarations.
|
||||
// So we wrap them in a struct.
|
||||
func FixPointerReceivers(e ast.Expr) (td ast.Expr, ret ast.Expr) {
|
||||
_, isStruct := e.(*ast.StructType)
|
||||
ret = Ident("ret")
|
||||
if isStruct {
|
||||
td = e
|
||||
return
|
||||
}
|
||||
ret = Selector("ret", "Val")
|
||||
s := NewStruct()
|
||||
AddFieldToStruct(s, "Val", e)
|
||||
td = s
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) GenerateTypes(prefix string, types Types) error {
|
||||
g.CurrentlyGeneratingTypes = types
|
||||
g.CurrentlyGeneratingTypesPrefix = prefix
|
||||
for _, k := range OrderedKeys(types.Types) {
|
||||
v := types.Types[k]
|
||||
if slices.Contains(g.Settings.ReplaceWithTodoNames, k) {
|
||||
v = "todo"
|
||||
}
|
||||
g.Depth = 0
|
||||
e, err := g.VisitType(v)
|
||||
if errors.Is(err, ToDoError) {
|
||||
e = Selector("queser", "ToDo")
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e == nil { // This is for types we implement ourselves
|
||||
continue
|
||||
}
|
||||
|
||||
tName := prefix + CamelCase(k)
|
||||
g.CurrentlyGeneratingTypeName = tName
|
||||
|
||||
e, retExpr := FixPointerReceivers(e)
|
||||
AppendDecl(g.File, TypeDecl(tName, e))
|
||||
|
||||
args := []NameAndType{{"r", Ident("io.Reader")}}
|
||||
rets := []NameAndType{{"ret", Ident(tName)}, {"err", Ident("error")}}
|
||||
decodeFunction := NewFuncWithReceiver("Decode", "_", tName, args, rets)
|
||||
g.Depth = 0
|
||||
s, err := g.VisitDecoder(retExpr, v, tName)
|
||||
if err != nil {
|
||||
s = ToDoStmts
|
||||
if !errors.Is(err, ToDoError) {
|
||||
fmt.Println("failed to make decoder for", k, err)
|
||||
}
|
||||
}
|
||||
decodeFunction.Body = NewBlock(append(s, Return()))
|
||||
AppendDecl(g.File, decodeFunction)
|
||||
|
||||
args = []NameAndType{{"w", Ident("io.Writer")}}
|
||||
rets = []NameAndType{{"err", Ident("error")}}
|
||||
encodeFunction := NewFuncWithReceiver("Encode", "ret", tName, args, rets)
|
||||
g.Depth = 0
|
||||
s, err = g.VisitEncoder(retExpr, v, tName)
|
||||
if err != nil {
|
||||
s = ToDoStmts
|
||||
if !errors.Is(err, ToDoError) {
|
||||
fmt.Println("failed to make encoder for", k, err)
|
||||
}
|
||||
}
|
||||
encodeFunction.Body = NewBlock(append(s, Return()))
|
||||
AppendDecl(g.File, encodeFunction)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Generator) GenerateState(prefix string, state State) (err error) {
|
||||
err = g.GenerateTypes(prefix+"ToServer", state.ToServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = g.GenerateTypes(prefix+"ToClient", state.ToClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) GenerateProtocol(protocol Protocol) (err error) {
|
||||
err = g.GenerateTypes("", protocol.Types)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = g.GenerateState("Handshaking", protocol.Handshaking)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = g.GenerateState("Status", protocol.Status)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = g.GenerateState("Login", protocol.Login)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = g.GenerateState("Configuration", protocol.Configuration)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = g.GenerateState("Play", protocol.Play)
|
||||
return
|
||||
}
|
||||
|
||||
func Generate(version string, w io.Writer) (err error) {
|
||||
protocol := Protocol{}
|
||||
err = data.LoadVersionedJson(version, "protocol", &protocol)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g := &Generator{Protocol: protocol}
|
||||
|
||||
// These have annoying edge cases that would require me to implement a whole new compareTo parser
|
||||
g.Settings.ReplaceWithTodoNames = []string{"packet_scoreboard_score", "packet_advancements"}
|
||||
|
||||
g.File = NewFile("v" + strings.ReplaceAll(version, ".", "_"))
|
||||
AppendDecl(g.File, Import("encoding/binary", "io", "github.com/admin-else/queser", "github.com/admin-else/queser/nbt", "github.com/google/uuid"))
|
||||
g.RegisterNatives()
|
||||
g.RegisterDecoderNatives()
|
||||
g.RegisterEncoderNatives()
|
||||
g.RegisterCompareToNatives()
|
||||
err = g.GenerateProtocol(protocol)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return PrintToFile(g.File, w)
|
||||
}
|
||||
|
||||
func generateVersion(v string) (err error) {
|
||||
vUnderscore := strings.ReplaceAll(v, ".", "_")
|
||||
err = os.MkdirAll("generated/v"+vUnderscore, 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f, err := os.Create("generated/v" + vUnderscore + "/proto.go")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
err = Generate(v, f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
helperF, err := os.Create("generated/v" + vUnderscore + "/helpers.go")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
err = GenerateHelpers(v, helperF)
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateVersions(versions []string) (err error) {
|
||||
err = os.RemoveAll("generated/")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = os.MkdirAll("generated/", 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f, err := os.Create("generated/version_switcher.go")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
err = GenerateVersionSwitcher(versions, f)
|
||||
for _, version := range versions {
|
||||
fmt.Println("generating", version)
|
||||
err = generateVersion(version)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := GenerateVersions([]string{"1.21.8"})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
func ParseCompareTo(g *Generator, compareTo string) (e ast.Expr, err error) {
|
||||
parts := strings.Split(compareTo, "/")
|
||||
downPrefixCount := 0
|
||||
for _, part := range parts {
|
||||
if part == ".." {
|
||||
downPrefixCount++
|
||||
}
|
||||
}
|
||||
parts = parts[downPrefixCount:]
|
||||
startingPoint := g.ContainerStack[len(g.ContainerStack)-downPrefixCount-1]
|
||||
e, err = g.VisitCompareTo(parts, startingPoint.VarToSet, CombineNamAndData("container", startingPoint.Data))
|
||||
// return ParseCompareToLegacy(compareTo, varToSet)
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) VisitCompareTo(parts []string, inExpr ast.Expr, data any) (e ast.Expr, err error) {
|
||||
tName, tData, err := ParseType(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
d, found := g.CompareToNatives[tName]
|
||||
if found {
|
||||
return d(g, parts, inExpr, tData)
|
||||
}
|
||||
err = fmt.Errorf("native compare to not implemented for %v", tName)
|
||||
return
|
||||
}
|
||||
|
||||
func ContainerCompareTo(g *Generator, parts []string, inExpr ast.Expr, dataRaw any) (e ast.Expr, err error) {
|
||||
var data []struct {
|
||||
Name string
|
||||
Type any
|
||||
Anon bool
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
name, parts, ok := PopFront(parts)
|
||||
if !ok {
|
||||
err = errors.New("expected name part")
|
||||
return
|
||||
}
|
||||
for _, field := range data {
|
||||
if field.Name == name {
|
||||
return g.VisitCompareTo(parts, SelectorExprAndStr(inExpr, CamelCase(field.Name)), field.Type)
|
||||
}
|
||||
}
|
||||
err = errors.New("field not found")
|
||||
return
|
||||
}
|
||||
|
||||
func ReturnInputCompareTo(_ *Generator, _ []string, inExpr ast.Expr, _ any) (e ast.Expr, err error) {
|
||||
return inExpr, nil
|
||||
}
|
||||
|
||||
func BitflagsCompareTo(_ *Generator, parts []string, inExpr ast.Expr, dataRaw any) (e ast.Expr, err error) {
|
||||
var data struct {
|
||||
Flags []string
|
||||
Type any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
name, parts, ok := PopFront(parts)
|
||||
if !ok {
|
||||
err = errors.New("expected name part")
|
||||
return
|
||||
}
|
||||
|
||||
for i, flag := range data.Flags {
|
||||
if flag != name {
|
||||
continue
|
||||
}
|
||||
e = NotEquals(BinAnd(inExpr, NumLitHex(1<<i)), NumLit(0))
|
||||
return
|
||||
}
|
||||
err = errors.New("flag not found")
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) RegisterCompareToNatives() {
|
||||
g.CompareToNatives = map[string]CompareToGeneratorFunc{
|
||||
"container": ContainerCompareTo,
|
||||
"bool": ReturnInputCompareTo,
|
||||
"mapper": ReturnInputCompareTo,
|
||||
"varint": ReturnInputCompareTo,
|
||||
"bitfield": ReturnInputCompareTo,
|
||||
"u8": ReturnInputCompareTo,
|
||||
"i8": ReturnInputCompareTo,
|
||||
"bitflags": BitflagsCompareTo,
|
||||
"switch": ReturnInputCompareTo,
|
||||
"varlong": ReturnInputCompareTo,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,523 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
func DefaultDecoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign(
|
||||
Exprs(varToSet, Ident("err")),
|
||||
Exprs(Call(SelectorExprAndStr(varToSet, "Decode"), Ident("r"))),
|
||||
),
|
||||
IfErrNil(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func UUIDDecoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
//_, err = io.ReadFull(r, U[:])
|
||||
s = Stmts(
|
||||
Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("io", "ReadFull"), Ident("r"), MultiIndex(varToSet, nil, nil, nil)))),
|
||||
IfErrNil(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func ContainerDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data []struct {
|
||||
Name string
|
||||
Type any
|
||||
Anon bool
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g.ContainerStack = append(g.ContainerStack, ContainerStackEntry{
|
||||
Data: data,
|
||||
VarToSet: varToSet,
|
||||
})
|
||||
defer func() { g.ContainerStack = g.ContainerStack[:len(g.ContainerStack)-1] }()
|
||||
|
||||
for _, field := range data {
|
||||
var statements []ast.Stmt
|
||||
cName := CamelCase(field.Name)
|
||||
if field.Anon {
|
||||
cName = "Anon"
|
||||
}
|
||||
statements, err = g.VisitDecoder(SelectorExprAndStr(varToSet, cName), field.Type, name+cName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s, statements...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SimpleTypeDecoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign(
|
||||
Exprs(Ident("err")),
|
||||
Exprs(Call(Selector("binary", "Read"), Ident("r"), Selector("binary", "BigEndian"), AddrOf(varToSet))),
|
||||
),
|
||||
IfErrNil(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func StringDecoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign(Exprs(varToSet, Ident("err")), Exprs(Call(Selector("queser", "DecodeString"), Ident("r")))),
|
||||
IfErrNil(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func OptionDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
pVarName := name + "Present"
|
||||
tVarName := name + "PresentValue"
|
||||
boolStmts, err := g.VisitDecoder(Ident(pVarName), "bool", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t, err := g.VisitType(dataRaw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tStmts1 := Stmts(VarStmt(tVarName, t))
|
||||
tStmts2, err := g.VisitDecoder(Ident(tVarName), dataRaw, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// This looks so shit
|
||||
tStmts := append(tStmts1, tStmts2...)
|
||||
tStmts = append(tStmts, Assign121(varToSet, AddrOf(Ident(tVarName))))
|
||||
s = append(s, VarStmt(pVarName, Ident("bool")))
|
||||
s = append(s, boolStmts...)
|
||||
s = append(s, If(Ident(pVarName), NewBlock(tStmts)))
|
||||
return
|
||||
}
|
||||
|
||||
func ArrayDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
CountType any
|
||||
Type any
|
||||
}
|
||||
lName := "l" + CamelCase(name)
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
countType, err := g.VisitType(data.CountType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s2, err := g.VisitDecoder(Ident(lName), data.CountType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
normalType, err := g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
arrayType, err := g.VisitNameAndData("array", dataRaw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tName := name + "Element"
|
||||
rangeStatements := Stmts(VarStmt(tName, normalType))
|
||||
s5, err := g.VisitDecoder(Ident(tName), data.Type, tName)
|
||||
rangeStatements = append(rangeStatements, s5...)
|
||||
rangeStatements = append(rangeStatements, Assign121(varToSet, Call(Ident("append"), varToSet, Ident(tName))))
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s1 := VarStmt(lName, countType)
|
||||
s3 := Assign121(varToSet, CompLit(arrayType, Exprs()))
|
||||
s4 := ForRange(nil, Ident(lName), NewBlock(rangeStatements))
|
||||
s = append(s, s1)
|
||||
s = append(s, s2...)
|
||||
s = append(s, s3)
|
||||
s = append(s, s4)
|
||||
return
|
||||
}
|
||||
|
||||
func TypeForwardDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
Type string
|
||||
}
|
||||
if err = mapstructure.Decode(dataRaw, &data); err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitDecoder(varToSet, data.Type, name)
|
||||
}
|
||||
|
||||
var DontStringify = []string{"true", "false"}
|
||||
|
||||
// CaseExprType This exists to fix this
|
||||
// "switch",
|
||||
//
|
||||
// {
|
||||
// "compareTo": "itemCount",
|
||||
// "fields": {
|
||||
// "0": "void",
|
||||
// "false": "void"
|
||||
// },
|
||||
// "default": [
|
||||
//
|
||||
// Protodef is best!!!
|
||||
type CaseExprType int
|
||||
|
||||
const (
|
||||
CaseExprTypeUnset CaseExprType = iota
|
||||
CaseExprTypeNumber
|
||||
CaseExprTypeSpecial
|
||||
CaseExprTypeString
|
||||
)
|
||||
|
||||
func MultiTypeFix(s string, t *CaseExprType) (e ast.Expr) {
|
||||
n, err := strconv.Atoi(s)
|
||||
ct := CaseExprTypeUnset
|
||||
if err == nil {
|
||||
err = nil
|
||||
e = NumLit(n)
|
||||
ct = CaseExprTypeNumber
|
||||
} else if slices.Contains(DontStringify, s) {
|
||||
e = Ident(s)
|
||||
ct = CaseExprTypeSpecial
|
||||
} else {
|
||||
e = StrLit(s)
|
||||
ct = CaseExprTypeString
|
||||
}
|
||||
|
||||
if *t == CaseExprTypeUnset {
|
||||
*t = ct
|
||||
return
|
||||
}
|
||||
if *t != ct {
|
||||
e = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SwitchDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
CompareTo string
|
||||
Fields map[string]any
|
||||
Default any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
compareToExpr, err := ParseCompareTo(g, data.CompareTo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cet := CaseExprTypeUnset
|
||||
for _, fName := range OrderedKeys(data.Fields) {
|
||||
fType := data.Fields[fName]
|
||||
e := MultiTypeFix(fName, &cet)
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
var tType ast.Expr
|
||||
tType, err = g.VisitType(fType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tName := name + "Tmp"
|
||||
s1 := VarStmt(tName, tType)
|
||||
s3 := Assign121(varToSet, Ident(tName))
|
||||
var caseDecodeValueStmts []ast.Stmt
|
||||
caseDecodeValueStmts, err = g.VisitDecoder(Ident(tName), fType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var caseStmts []ast.Stmt
|
||||
caseStmts = append(caseStmts, s1)
|
||||
caseStmts = append(caseStmts, caseDecodeValueStmts...)
|
||||
caseStmts = append(caseStmts, s3)
|
||||
s = append(s, Case(Exprs(e), caseStmts))
|
||||
}
|
||||
if data.Default != nil {
|
||||
var tType ast.Expr
|
||||
tType, err = g.VisitType(data.Default)
|
||||
tName := name + "Tmp"
|
||||
s1 := VarStmt(tName, tType)
|
||||
s3 := Assign121(varToSet, Ident(tName))
|
||||
var caseDecodeValueStmts []ast.Stmt
|
||||
caseDecodeValueStmts, err = g.VisitDecoder(Ident(tName), data.Default, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var caseStmts []ast.Stmt
|
||||
caseStmts = append(caseStmts, s1)
|
||||
caseStmts = append(caseStmts, caseDecodeValueStmts...)
|
||||
caseStmts = append(caseStmts, s3)
|
||||
s = append(s, Case(nil, caseStmts))
|
||||
}
|
||||
if len(s) != 0 {
|
||||
s = Stmts(SwitchStmt(compareToExpr, NewBlock(s)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var ToDoStmts = Stmts(Assign121(Ident("err"), Selector("queser", "ToDoError")))
|
||||
|
||||
func ToDoDecoder(_ *Generator, _ ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
err = ToDoError
|
||||
return
|
||||
}
|
||||
|
||||
func MapperDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
Mappings map[string]string
|
||||
Type any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
kName := CamelCase(name) + "Key"
|
||||
kType, err := g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s1 := VarStmt(kName, kType)
|
||||
s2, err := g.VisitDecoder(Ident(kName), data.Type, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mName := name + "Map"
|
||||
|
||||
expressions := Exprs()
|
||||
for _, k := range OrderedKeys(data.Mappings) {
|
||||
v := data.Mappings[k]
|
||||
expressions = append(expressions, KeyValueExpr(NumLitStr(k), StrLit(v)))
|
||||
}
|
||||
g.Decl(mName, token.VAR, CompLit(MapType(kType, Ident("string")), expressions))
|
||||
s3 := Assign(Exprs(varToSet, Ident("err")), Exprs(Call(Selector("queser", "ErroringIndex"), Ident(mName), Ident(kName))))
|
||||
s = append(s, s1)
|
||||
s = append(s, s2...)
|
||||
s = append(s, s3)
|
||||
s = append(s, IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func BufferDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
Count int
|
||||
CountType any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// rawString, err := io.ReadAll(io.LimitReader(r, int64(l)))
|
||||
|
||||
if data.CountType != nil {
|
||||
lName := "l" + CamelCase(name)
|
||||
var countType ast.Expr
|
||||
countType, err = g.VisitType(data.CountType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var s2 []ast.Stmt
|
||||
s2, err = g.VisitDecoder(Ident(lName), data.CountType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s1 := VarStmt(lName, countType)
|
||||
s3 := Assign(Exprs(varToSet, Ident("err")), Exprs(Call(Selector("io", "ReadAll"), Call(Selector("io", "LimitReader"), Ident("r"), Call(Ident("int64"), Ident(lName))))))
|
||||
|
||||
s = append(s, s1)
|
||||
s = append(s, s2...)
|
||||
s = append(s, s3)
|
||||
} else {
|
||||
s = Stmts(Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("r", "Read"), MultiIndex(varToSet, nil, nil, nil)))))
|
||||
}
|
||||
|
||||
s = append(s, IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func BitFieldDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data []struct {
|
||||
Name string
|
||||
Singed bool
|
||||
Size int
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
totalSize := 0
|
||||
for _, field := range data {
|
||||
totalSize += field.Size
|
||||
}
|
||||
p, err := TotalBitfieldSizeToProtodefName(totalSize)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitDecoder(varToSet, p, name)
|
||||
}
|
||||
|
||||
func RegistryEntryHolderSetDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
Base struct {
|
||||
Name string
|
||||
Type any
|
||||
}
|
||||
Otherwise struct {
|
||||
Name string
|
||||
Type any
|
||||
}
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var countType ast.Expr
|
||||
countType, err = g.VisitType("varint")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
baseType, err := g.VisitType(data.Base.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
otherwiseArrayType, err := g.VisitNameAndData("array", map[string]any{"type": data.Otherwise.Type})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
otherwiseType, err := g.VisitType(data.Otherwise.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lName := "l" + CamelCase(name)
|
||||
s1 := VarStmt(lName, countType)
|
||||
s2, err := g.VisitDecoder(Ident(lName), "varint", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s3 := Assign121(Ident(lName), Sub(Ident(lName), NumLit(1)))
|
||||
resultName := name + "Result"
|
||||
b1s1 := VarStmt(resultName, baseType)
|
||||
b1s2, err := g.VisitDecoder(Ident(resultName), data.Base.Type, resultName)
|
||||
b1s3 := Assign121(varToSet, Ident(resultName))
|
||||
b1s4 := Return()
|
||||
var b1 []ast.Stmt
|
||||
b1 = append(b1, b1s1)
|
||||
b1 = append(b1, b1s2...)
|
||||
b1 = append(b1, b1s3)
|
||||
b1 = append(b1, b1s4)
|
||||
|
||||
tName := name + "Element"
|
||||
rangeStatements := Stmts(VarStmt(tName, otherwiseType))
|
||||
rangeStatementsDecode, err := g.VisitDecoder(Ident(tName), data.Otherwise.Type, tName)
|
||||
rangeStatements = append(rangeStatements, rangeStatementsDecode...)
|
||||
rangeStatements = append(rangeStatements, Assign121(Ident(resultName), Call(Ident("append"), Ident(resultName), Ident(tName))))
|
||||
|
||||
s5 := If(Equals(Ident(lName), NumLit(-1)), NewBlock(b1))
|
||||
s6 := VarStmt(resultName, otherwiseArrayType)
|
||||
s7 := ForRange(nil, Ident(lName), NewBlock(rangeStatements))
|
||||
s = append(s, s1)
|
||||
s = append(s, s2...)
|
||||
s = append(s, s3)
|
||||
s = append(s, s5)
|
||||
s = append(s, s6)
|
||||
s = append(s, s7)
|
||||
return
|
||||
}
|
||||
|
||||
func RegistryEntryHolderDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data EntryHolderSet
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
idName := name + "Id"
|
||||
IdVar := VarStmt(idName, Selector("queser", "VarInt"))
|
||||
idDecodeStatements, err := g.VisitDecoder(Ident(idName), "varint", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
idPath := If(NotEquals(Ident(idName), NumLit(0)), NewBlockEllipsis(Assign121(varToSet, Ident(idName)), Return()))
|
||||
|
||||
otherwiseType, err := g.VisitType(data.Otherwise.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resName := name + "Result"
|
||||
resNameIdent := Ident(resName)
|
||||
resultVar := VarStmt(resName, otherwiseType)
|
||||
|
||||
otherwiseDecodeStatements, err := g.VisitDecoder(resNameIdent, data.Otherwise.Type, name+"Otherwise")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resultAssign := Assign121(varToSet, resNameIdent)
|
||||
|
||||
s = append(s, IdVar)
|
||||
s = append(s, idDecodeStatements...)
|
||||
s = append(s, idPath)
|
||||
s = append(s, resultVar)
|
||||
s = append(s, otherwiseDecodeStatements...)
|
||||
s = append(s, resultAssign)
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) RegisterDecoderNatives() {
|
||||
g.DecoderNatives = map[string]FunctionGeneratorFunc{
|
||||
"container": ContainerDecoder,
|
||||
"array": ArrayDecoder,
|
||||
"option": OptionDecoder,
|
||||
"bitflags": TypeForwardDecoder,
|
||||
"switch": SwitchDecoder,
|
||||
"mapper": MapperDecoder,
|
||||
"buffer": BufferDecoder,
|
||||
"bitfield": BitFieldDecoder,
|
||||
"string": StringDecoder,
|
||||
|
||||
"void": DefaultDecoder,
|
||||
"varint": DefaultDecoder,
|
||||
"anonymousNbt": DefaultDecoder,
|
||||
"anonOptionalNbt": DefaultDecoder, // I have no idea what the difference is between these two
|
||||
"UUID": UUIDDecoder,
|
||||
"restBuffer": DefaultDecoder,
|
||||
|
||||
"f32": SimpleTypeDecoder,
|
||||
"f64": SimpleTypeDecoder,
|
||||
"i8": SimpleTypeDecoder,
|
||||
"i16": SimpleTypeDecoder,
|
||||
"i32": SimpleTypeDecoder,
|
||||
"i64": SimpleTypeDecoder,
|
||||
"u8": SimpleTypeDecoder,
|
||||
"u16": SimpleTypeDecoder,
|
||||
"u32": SimpleTypeDecoder,
|
||||
"u64": SimpleTypeDecoder,
|
||||
"bool": SimpleTypeDecoder,
|
||||
|
||||
"registryEntryHolder": RegistryEntryHolderDecoder,
|
||||
"registryEntryHolderSet": RegistryEntryHolderSetDecoder,
|
||||
"entityMetadataLoop": ToDoDecoder,
|
||||
"topBitSetTerminatedArray": ToDoDecoder,
|
||||
"todo": ToDoDecoder,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
func DefaultEncoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign121(
|
||||
Ident("err"),
|
||||
Call(SelectorExprAndStr(varToSet, "Encode"), Ident("w")),
|
||||
),
|
||||
IfErrNil(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ContainerEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data []struct {
|
||||
Name string
|
||||
Type any
|
||||
Anon bool
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g.ContainerStack = append(g.ContainerStack, ContainerStackEntry{
|
||||
Data: data,
|
||||
VarToSet: varToSet,
|
||||
})
|
||||
defer func() { g.ContainerStack = g.ContainerStack[:len(g.ContainerStack)-1] }()
|
||||
|
||||
for _, field := range data {
|
||||
var statements []ast.Stmt
|
||||
cName := CamelCase(field.Name)
|
||||
if field.Anon {
|
||||
cName = "Anon"
|
||||
}
|
||||
statements, err = g.VisitEncoder(SelectorExprAndStr(varToSet, cName), field.Type, name+CamelCase(cName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s, statements...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SimpleTypeEncoder(_ *Generator, data ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign121(
|
||||
Ident("err"),
|
||||
Call(Selector("binary", "Write"), Ident("w"), Selector("binary", "BigEndian"), data),
|
||||
),
|
||||
IfErrNil(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func ArrayEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
CountType any
|
||||
Type any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ct, err := g.VisitType(data.CountType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, ok := ct.(*ast.Ident)
|
||||
if !ok {
|
||||
err = errors.New("array count type must be simple type otherwise wtf")
|
||||
}
|
||||
iName := "i" + name
|
||||
s, err = g.VisitEncoder(varToSet, data.Type, name)
|
||||
s1, err := g.VisitEncoder(Call(ct, Call(Ident("len"), varToSet)), data.CountType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s2, err := g.VisitEncoder(Index(varToSet, Ident(iName)), data.Type, name+"Inner")
|
||||
if err != nil {
|
||||
}
|
||||
s2 = Stmts(ForRange(Ident(iName), Call(Ident("len"), varToSet), NewBlock(s2)))
|
||||
s = append(s1, s2...)
|
||||
return
|
||||
}
|
||||
|
||||
func ToDoEncoder(_ *Generator, _ ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
err = ToDoError
|
||||
return
|
||||
}
|
||||
|
||||
func OptionEncoder(g *Generator, data ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
cond := NotEquals(data, Nil())
|
||||
s1, err := g.VisitEncoder(cond, "bool", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s2, err := g.VisitEncoder(DeRef(data), dataRaw, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s1, If(cond, NewBlock(s2)))
|
||||
return
|
||||
}
|
||||
|
||||
func BufferEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
Count int
|
||||
CountType any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if data.CountType != nil {
|
||||
var countType ast.Expr
|
||||
countType, err = g.VisitType(data.CountType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var s2 []ast.Stmt
|
||||
s2, err = g.VisitEncoder(Call(countType, Call(Ident("len"), varToSet)), data.CountType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s, s2...)
|
||||
} else {
|
||||
s = append(s, Define121(Ident("arr"), varToSet))
|
||||
varToSet = MultiIndex(Ident("arr"), nil, nil, nil)
|
||||
}
|
||||
s = append(s, Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("w", "Write"), varToSet))))
|
||||
s = append(s, IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func BitFieldEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data []struct {
|
||||
Name string
|
||||
Singed bool
|
||||
Size int
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
totalSize := 0
|
||||
for _, field := range data {
|
||||
totalSize += field.Size
|
||||
}
|
||||
p, err := TotalBitfieldSizeToProtodefName(totalSize)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitEncoder(varToSet, p, name)
|
||||
}
|
||||
|
||||
func SwitchEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
CompareTo string
|
||||
Fields map[string]any
|
||||
Default any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
compareToExpr, err := ParseCompareTo(g, data.CompareTo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cet := CaseExprTypeUnset
|
||||
for _, fName := range OrderedKeys(data.Fields) {
|
||||
fType := data.Fields[fName]
|
||||
e := MultiTypeFix(fName, &cet)
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
var tType ast.Expr
|
||||
tType, err = g.VisitType(fType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s1 := Define(Exprs(Ident(name), Ident("ok")), Exprs(TypeAssert(varToSet, tType)))
|
||||
s2 := If(Not(Ident("ok")), NewBlockEllipsis(Assign121(Ident("err"), Selector("queser", "BadTypeError")), Return()))
|
||||
var caseDecodeValueStmts []ast.Stmt
|
||||
caseDecodeValueStmts, err = g.VisitEncoder(Ident(name), fType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var caseStmts []ast.Stmt
|
||||
caseStmts = append(caseStmts, s1)
|
||||
caseStmts = append(caseStmts, s2)
|
||||
caseStmts = append(caseStmts, caseDecodeValueStmts...)
|
||||
s = append(s, Case(Exprs(e), caseStmts))
|
||||
}
|
||||
if data.Default != nil {
|
||||
var tType ast.Expr
|
||||
tType, err = g.VisitType(data.Default)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s1 := Define(Exprs(Ident("_"), Ident("ok")), Exprs(TypeAssert(varToSet, tType)))
|
||||
s2 := If(Not(Ident("ok")), NewBlockEllipsis(Assign121(Ident("err"), Selector("queser", "BadTypeError")), Return()))
|
||||
defaultStmts := []ast.Stmt{s1, s2}
|
||||
var defaultEncodingStmts []ast.Stmt
|
||||
defaultEncodingStmts, err = g.VisitEncoder(TypeAssert(varToSet, tType), data.Default, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defaultStmts = append(defaultStmts, defaultEncodingStmts...)
|
||||
s = append(s, Case(nil, defaultStmts))
|
||||
}
|
||||
if len(s) != 0 {
|
||||
s = Stmts(SwitchStmt(compareToExpr, NewBlock(s)))
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func TypeForwardEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
Type string
|
||||
}
|
||||
if err = mapstructure.Decode(dataRaw, &data); err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitEncoder(varToSet, data.Type, name)
|
||||
}
|
||||
|
||||
func MapperEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data struct {
|
||||
Mappings map[string]string
|
||||
Type any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
kType, err := g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mName := name + "ReverseMap"
|
||||
|
||||
exprs := Exprs()
|
||||
for _, k := range OrderedKeys(data.Mappings) {
|
||||
v := data.Mappings[k]
|
||||
exprs = append(exprs, KeyValueExpr(StrLit(v), NumLitStr(k)))
|
||||
}
|
||||
g.Decl(mName, token.VAR, CompLit(MapType(Ident("string"), kType), exprs))
|
||||
|
||||
vName := "v" + name
|
||||
s1 := VarStmt(vName, kType)
|
||||
s2 := Assign(Exprs(Ident(vName), Ident("err")), Exprs(Call(Selector("queser", "ErroringIndex"), Ident(mName), varToSet)))
|
||||
s3 := IfErrNil()
|
||||
s4, err := g.VisitEncoder(Ident(vName), data.Type, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s = append(s, s1)
|
||||
s = append(s, s2)
|
||||
s = append(s, s3)
|
||||
s = append(s, s4...)
|
||||
return
|
||||
}
|
||||
|
||||
func StringEncoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign121(Ident("err"), Call(Selector("queser", "EncodeString"), Ident("w"), varToSet)), IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func UUIDEncoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("w", "Write"), MultiIndex(varToSet, nil, nil, nil)))), IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func RegistryEntryHolderEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data EntryHolderSet
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
otherwiseType, err := g.VisitType(data.Otherwise.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
knownTypeName := name + "KnownType"
|
||||
otherwiseEncodeStatements, err := g.VisitEncoder(Ident(knownTypeName), data.Otherwise.Type, name+"Otherwise")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
baseType, err := g.VisitType("varint")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
baseEncodeStatements, err := g.VisitEncoder(Ident(knownTypeName), "varint", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
statements := Stmts()
|
||||
statements = append(statements, Case(Exprs(baseType), baseEncodeStatements))
|
||||
statements = append(statements, Case(Exprs(otherwiseType), otherwiseEncodeStatements))
|
||||
statements = append(statements, Case(nil, Stmts(Assign121(Ident("err"), Selector("queser", "BadTypeError")))))
|
||||
|
||||
s = Stmts(TypeSwitch(Define121(Ident(knownTypeName), TypeAssert(varToSet, Ident("type"))), NewBlock(statements)))
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) RegisterEncoderNatives() {
|
||||
g.EncoderNatives = map[string]FunctionGeneratorFunc{
|
||||
"container": ContainerEncoder,
|
||||
"array": ArrayEncoder,
|
||||
"option": OptionEncoder,
|
||||
"bitflags": TypeForwardEncoder,
|
||||
"switch": SwitchEncoder,
|
||||
"mapper": MapperEncoder,
|
||||
"buffer": BufferEncoder,
|
||||
"bitfield": BitFieldEncoder,
|
||||
|
||||
"void": DefaultEncoder,
|
||||
"varint": DefaultEncoder,
|
||||
"anonymousNbt": DefaultEncoder,
|
||||
"anonOptionalNbt": DefaultEncoder, // I have no idea what the difference is between these two
|
||||
"UUID": UUIDEncoder,
|
||||
"restBuffer": DefaultEncoder,
|
||||
"string": StringEncoder,
|
||||
|
||||
"f32": SimpleTypeEncoder,
|
||||
"f64": SimpleTypeEncoder,
|
||||
"i8": SimpleTypeEncoder,
|
||||
"i16": SimpleTypeEncoder,
|
||||
"i32": SimpleTypeEncoder,
|
||||
"i64": SimpleTypeEncoder,
|
||||
"u8": SimpleTypeEncoder,
|
||||
"u16": SimpleTypeEncoder,
|
||||
"u32": SimpleTypeEncoder,
|
||||
"u64": SimpleTypeEncoder,
|
||||
"bool": SimpleTypeEncoder,
|
||||
|
||||
"registryEntryHolder": RegistryEntryHolderEncoder,
|
||||
"registryEntryHolderSet": ToDoEncoder,
|
||||
"entityMetadataLoop": ToDoEncoder,
|
||||
"topBitSetTerminatedArray": ToDoEncoder,
|
||||
"todo": ToDoEncoder,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
func VisitContainerType(g *Generator, dataRaw any) (ast.Expr, error) {
|
||||
var data []struct {
|
||||
Name string
|
||||
Type any
|
||||
Anon bool
|
||||
}
|
||||
err := mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.ContainerStack = append(g.ContainerStack, ContainerStackEntry{
|
||||
Data: data,
|
||||
VarToSet: nil,
|
||||
})
|
||||
defer func() { g.ContainerStack = g.ContainerStack[:len(g.ContainerStack)-1] }()
|
||||
|
||||
s := NewStruct()
|
||||
for _, field := range data {
|
||||
t, err := g.VisitType(field.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field.Anon {
|
||||
field.Name = "Anon" // lol
|
||||
}
|
||||
AddFieldToStruct(s, CamelCase(field.Name), t)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func VisitBufferType(_ *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
var data struct {
|
||||
CountType any
|
||||
Count int
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if data.CountType != nil {
|
||||
if data.CountType == "varint" {
|
||||
e = Slice(Ident("byte"))
|
||||
return
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported count type: %v", data.CountType) // TODO: old versions use uint16 as count type
|
||||
}
|
||||
e = Array(Ident("byte"), data.Count)
|
||||
return
|
||||
}
|
||||
|
||||
func VisitArrayTypeVisitorType(g *Generator, dataRaw any) (ast.Expr, error) {
|
||||
var data struct {
|
||||
CountType any
|
||||
Type any
|
||||
}
|
||||
must(mapstructure.Decode(dataRaw, &data))
|
||||
e, err := g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Slice(e), nil
|
||||
}
|
||||
|
||||
func VisitDontGenerateType(_ *Generator, _ any) (ast.Expr, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func VisitToDoType(_ *Generator, _ any) (e ast.Expr, err error) {
|
||||
err = ToDoError
|
||||
return
|
||||
}
|
||||
|
||||
func VisitOptionType(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
e, err = g.VisitType(dataRaw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e = Pointer(e)
|
||||
return
|
||||
}
|
||||
|
||||
func TotalBitfieldSizeToProtodefName(totalSize int) (e string, err error) {
|
||||
if totalSize <= 8 {
|
||||
e = "u8"
|
||||
} else if totalSize <= 16 {
|
||||
e = "u16"
|
||||
} else if totalSize <= 32 {
|
||||
e = "u32"
|
||||
} else if totalSize <= 64 {
|
||||
e = "u64"
|
||||
} else {
|
||||
err = errors.New("bitfield size too large")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func VisitBitFieldType(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
var data []struct {
|
||||
Name string
|
||||
Singed bool
|
||||
Size int
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
totalSize := 0
|
||||
for _, field := range data {
|
||||
totalSize += field.Size
|
||||
}
|
||||
// TODO: we will want to generate Methods and other stuff
|
||||
p, err := TotalBitfieldSizeToProtodefName(totalSize)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitType(p)
|
||||
}
|
||||
|
||||
func VisitBitFlagsType(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
var data struct {
|
||||
Flags []string
|
||||
Type any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitType(data.Type)
|
||||
}
|
||||
|
||||
func MakeIdentVisitor(name string) func(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
return func(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
e = Ident(name)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func MakeSelectorVisitor(x, sel string) func(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
return func(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
e = Selector(x, sel)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Generator) RegisterNatives() {
|
||||
g.Natives = map[string]ExprGeneratorFunc{
|
||||
"container": VisitContainerType,
|
||||
"buffer": VisitBufferType,
|
||||
"varint": MakeSelectorVisitor("queser", "VarInt"),
|
||||
"array": VisitArrayTypeVisitorType,
|
||||
"mapper": MakeIdentVisitor("string"),
|
||||
"native": VisitDontGenerateType,
|
||||
"anonymousNbt": MakeSelectorVisitor("nbt", "Anon"),
|
||||
"anonOptionalNbt": MakeSelectorVisitor("nbt", "Anon"), // I have no idea what the difference is between these two
|
||||
"void": MakeSelectorVisitor("queser", "Void"),
|
||||
"bool": MakeIdentVisitor("bool"),
|
||||
"u8": MakeIdentVisitor("uint8"),
|
||||
"u16": MakeIdentVisitor("uint16"),
|
||||
"u32": MakeIdentVisitor("uint32"),
|
||||
"u64": MakeIdentVisitor("uint64"),
|
||||
"i8": MakeIdentVisitor("int8"),
|
||||
"i16": MakeIdentVisitor("int16"),
|
||||
"i32": MakeIdentVisitor("int32"),
|
||||
"i64": MakeIdentVisitor("int64"),
|
||||
"f32": MakeIdentVisitor("float32"),
|
||||
"f64": MakeIdentVisitor("float64"),
|
||||
"registryEntryHolder": MakeIdentVisitor("any"), // Go really fucking needs good tagged unions
|
||||
"registryEntryHolderSet": MakeIdentVisitor("any"),
|
||||
"entityMetadataLoop": VisitToDoType,
|
||||
"option": VisitOptionType,
|
||||
"switch": MakeIdentVisitor("any"),
|
||||
"UUID": MakeSelectorVisitor("uuid", "UUID"),
|
||||
"bitfield": VisitBitFieldType,
|
||||
"pstring": VisitDontGenerateType,
|
||||
"string": MakeIdentVisitor("string"),
|
||||
"restBuffer": MakeSelectorVisitor("queser", "RestBuffer"),
|
||||
"bitflags": VisitBitFlagsType,
|
||||
"topBitSetTerminatedArray": VisitToDoType,
|
||||
"todo": VisitToDoType,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/token"
|
||||
"io"
|
||||
)
|
||||
|
||||
func NewStruct() *ast.StructType {
|
||||
return &ast.StructType{
|
||||
Fields: &ast.FieldList{
|
||||
List: []*ast.Field{},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func AddFieldToStruct(s *ast.StructType, fieldName string, t ast.Expr) {
|
||||
s.Fields.List = append(s.Fields.List, &ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
Name: fieldName,
|
||||
},
|
||||
},
|
||||
Type: t,
|
||||
})
|
||||
}
|
||||
|
||||
//func AddAnonFieldToStruct(s *ast.StructType, t ast.Expr) {
|
||||
// s.Fields.List = append(s.Fields.List, &ast.Field{
|
||||
// Names: []*ast.Ident{},
|
||||
// Type: t,
|
||||
// })
|
||||
//}
|
||||
|
||||
func Import(path ...string) ast.Decl {
|
||||
specs := make([]ast.Spec, len(path))
|
||||
for i, p := range path {
|
||||
specs[i] = &ast.ImportSpec{
|
||||
Path: StrLit(p),
|
||||
}
|
||||
}
|
||||
return &ast.GenDecl{
|
||||
Tok: token.IMPORT,
|
||||
Specs: specs,
|
||||
}
|
||||
}
|
||||
|
||||
func Ident(name string) *ast.Ident {
|
||||
return ast.NewIdent(name)
|
||||
}
|
||||
|
||||
func VarStmt(name string, t ast.Expr) *ast.DeclStmt {
|
||||
return &ast.DeclStmt{Decl: Var(name, t)}
|
||||
}
|
||||
|
||||
func Var(name string, t ast.Expr) ast.Decl {
|
||||
return &ast.GenDecl{
|
||||
Tok: token.VAR,
|
||||
Specs: []ast.Spec{
|
||||
&ast.ValueSpec{
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
Type: t,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Slice(elementType ast.Expr) ast.Expr {
|
||||
return &ast.ArrayType{
|
||||
Len: nil, // nil Length means it's a slice, not an array
|
||||
Elt: elementType,
|
||||
}
|
||||
}
|
||||
|
||||
func NumLit(n int) *ast.BasicLit {
|
||||
return NumLitStr(fmt.Sprint(n))
|
||||
}
|
||||
|
||||
func NumLitHex(n int) *ast.BasicLit {
|
||||
return NumLitStr(fmt.Sprintf("%X", n))
|
||||
}
|
||||
|
||||
func NumLitStr(n string) *ast.BasicLit {
|
||||
return &ast.BasicLit{
|
||||
ValuePos: 0,
|
||||
Kind: token.INT,
|
||||
Value: n,
|
||||
}
|
||||
}
|
||||
|
||||
func Array(elementType ast.Expr, len int) ast.Expr {
|
||||
return &ast.ArrayType{
|
||||
Len: NumLit(len),
|
||||
Elt: elementType,
|
||||
}
|
||||
}
|
||||
|
||||
func TypeDecl(name string, t ast.Expr) ast.Decl {
|
||||
return &ast.GenDecl{
|
||||
Tok: token.TYPE,
|
||||
Specs: []ast.Spec{
|
||||
&ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
Name: name,
|
||||
},
|
||||
Type: t,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Selector(x, sel string) ast.Expr {
|
||||
return &ast.SelectorExpr{
|
||||
X: Ident(x),
|
||||
Sel: Ident(sel),
|
||||
}
|
||||
}
|
||||
|
||||
func SelectorExprAndStr(x ast.Expr, sel string) ast.Expr {
|
||||
return &ast.SelectorExpr{
|
||||
X: x,
|
||||
Sel: Ident(sel),
|
||||
}
|
||||
}
|
||||
|
||||
func Pointer(elementType ast.Expr) ast.Expr {
|
||||
return &ast.StarExpr{
|
||||
X: elementType,
|
||||
}
|
||||
}
|
||||
|
||||
func Call(fn ast.Expr, args ...ast.Expr) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Fun: fn,
|
||||
Args: args,
|
||||
}
|
||||
}
|
||||
|
||||
func Assign121(lhs, rhs ast.Expr) *ast.AssignStmt {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{lhs},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{rhs},
|
||||
}
|
||||
}
|
||||
|
||||
func Assign(lhs, rhs []ast.Expr) *ast.AssignStmt {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: lhs,
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func Define(lhs, rhs []ast.Expr) *ast.AssignStmt {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: lhs,
|
||||
Tok: token.DEFINE,
|
||||
Rhs: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func Define121(lhs, rhs ast.Expr) *ast.AssignStmt {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{lhs},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{rhs},
|
||||
}
|
||||
}
|
||||
|
||||
func Exprs(exprs ...ast.Expr) []ast.Expr {
|
||||
return exprs
|
||||
}
|
||||
|
||||
func Stmts(stmts ...ast.Stmt) []ast.Stmt {
|
||||
return stmts
|
||||
}
|
||||
|
||||
func ExprStmt(expr ast.Expr) *ast.ExprStmt {
|
||||
return &ast.ExprStmt{
|
||||
X: expr,
|
||||
}
|
||||
}
|
||||
|
||||
func If(cond ast.Expr, body *ast.BlockStmt) *ast.IfStmt {
|
||||
return &ast.IfStmt{
|
||||
Cond: cond,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
func NotEquals(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.NEQ,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func Equals(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.EQL,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func Nil() ast.Expr {
|
||||
return Ident("nil")
|
||||
}
|
||||
|
||||
func NewFile(name string) *ast.File {
|
||||
return &ast.File{
|
||||
Name: &ast.Ident{
|
||||
Name: name,
|
||||
},
|
||||
Decls: []ast.Decl{},
|
||||
}
|
||||
}
|
||||
|
||||
type NameAndType struct {
|
||||
Name string
|
||||
Type ast.Expr
|
||||
}
|
||||
|
||||
func NewFunc(name string, arguments []NameAndType, returns []NameAndType) *ast.FuncDecl {
|
||||
params := &ast.FieldList{
|
||||
List: make([]*ast.Field, len(arguments)),
|
||||
}
|
||||
for i, arg := range arguments {
|
||||
params.List[i] = &ast.Field{
|
||||
Names: []*ast.Ident{Ident(arg.Name)},
|
||||
Type: arg.Type,
|
||||
}
|
||||
}
|
||||
|
||||
results := &ast.FieldList{
|
||||
List: make([]*ast.Field, len(returns)),
|
||||
}
|
||||
for i, ret := range returns {
|
||||
results.List[i] = &ast.Field{
|
||||
Names: []*ast.Ident{{Name: ret.Name}},
|
||||
Type: ret.Type,
|
||||
}
|
||||
}
|
||||
|
||||
return &ast.FuncDecl{
|
||||
Name: Ident(name),
|
||||
Type: &ast.FuncType{
|
||||
Params: params,
|
||||
Results: results,
|
||||
},
|
||||
Body: &ast.BlockStmt{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewFuncWithReceiver(name, receiverName, receiver string, arguments []NameAndType, returns []NameAndType) *ast.FuncDecl {
|
||||
f := NewFunc(name, arguments, returns)
|
||||
f.Recv = &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
{
|
||||
Names: []*ast.Ident{{Name: receiverName}},
|
||||
Type: &ast.Ident{Name: receiver},
|
||||
},
|
||||
},
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func NewBlock(statements []ast.Stmt) *ast.BlockStmt {
|
||||
return &ast.BlockStmt{
|
||||
List: statements,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBlockEllipsis(statements ...ast.Stmt) *ast.BlockStmt {
|
||||
return &ast.BlockStmt{
|
||||
List: statements,
|
||||
}
|
||||
}
|
||||
|
||||
//func MergeBlocks(blocks ...*ast.BlockStmt) *ast.BlockStmt {
|
||||
// list := make([]ast.Stmt, 0)
|
||||
// for _, block := range blocks {
|
||||
// if block != nil {
|
||||
// list = append(list, block.List...)
|
||||
// }
|
||||
// }
|
||||
// return &ast.BlockStmt{
|
||||
// List: list,
|
||||
// }
|
||||
//}
|
||||
|
||||
func Return(exprs ...ast.Expr) *ast.ReturnStmt {
|
||||
return &ast.ReturnStmt{
|
||||
Results: exprs,
|
||||
}
|
||||
}
|
||||
|
||||
func AppendDecl(file *ast.File, decl ast.Decl) {
|
||||
file.Decls = append(file.Decls, decl)
|
||||
}
|
||||
|
||||
func PrintToFile(f *ast.File, w io.Writer) error {
|
||||
return format.Node(w, token.NewFileSet(), f)
|
||||
}
|
||||
|
||||
func IfErrNil() *ast.IfStmt {
|
||||
return If(
|
||||
NotEquals(Ident("err"), Nil()), NewBlockEllipsis(Return()),
|
||||
)
|
||||
}
|
||||
|
||||
func AddrOf(e ast.Expr) *ast.UnaryExpr {
|
||||
return &ast.UnaryExpr{
|
||||
Op: token.AND,
|
||||
X: e,
|
||||
}
|
||||
}
|
||||
|
||||
func DeRef(e ast.Expr) *ast.StarExpr {
|
||||
return &ast.StarExpr{
|
||||
X: e,
|
||||
}
|
||||
}
|
||||
|
||||
func ForRange(K, X ast.Expr, block *ast.BlockStmt) *ast.RangeStmt {
|
||||
return &ast.RangeStmt{
|
||||
Key: K, // The loop variable "i"
|
||||
Value: nil, // No second variable (common for index-only range loops)
|
||||
X: X, // The collection being ranged over "l"
|
||||
Tok: token.DEFINE, // ":=" token
|
||||
Body: block,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Index(X, I ast.Expr) *ast.IndexExpr {
|
||||
return &ast.IndexExpr{
|
||||
X: X,
|
||||
Index: I,
|
||||
}
|
||||
}
|
||||
|
||||
func MultiIndex(X ast.Expr, Low, High, Max ast.Expr) *ast.SliceExpr {
|
||||
return &ast.SliceExpr{
|
||||
X: X,
|
||||
Low: Low,
|
||||
High: High,
|
||||
Max: Max,
|
||||
Slice3: Max != nil,
|
||||
}
|
||||
}
|
||||
|
||||
func SwitchStmt(Tag ast.Expr, block *ast.BlockStmt) *ast.SwitchStmt {
|
||||
return &ast.SwitchStmt{
|
||||
Tag: Tag,
|
||||
Body: block,
|
||||
}
|
||||
}
|
||||
|
||||
//func SwitchStmtInit(init ast.Stmt, block *ast.BlockStmt) *ast.SwitchStmt {
|
||||
// return &ast.SwitchStmt{
|
||||
// Init: init,
|
||||
// Body: block,
|
||||
// }
|
||||
//}
|
||||
|
||||
func StrLit(s string) *ast.BasicLit {
|
||||
return &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: `"` + s + `"`,
|
||||
}
|
||||
}
|
||||
|
||||
func Case(exprs []ast.Expr, stmts []ast.Stmt) *ast.CaseClause {
|
||||
return &ast.CaseClause{
|
||||
List: exprs,
|
||||
Body: stmts,
|
||||
}
|
||||
}
|
||||
|
||||
func MapType(key, value ast.Expr) ast.Expr {
|
||||
return &ast.MapType{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func CompLit(t ast.Expr, elts []ast.Expr) *ast.CompositeLit {
|
||||
return &ast.CompositeLit{
|
||||
Type: t,
|
||||
Elts: elts,
|
||||
}
|
||||
}
|
||||
|
||||
//func VarAssign(name string, value ast.Expr) *ast.GenDecl {
|
||||
// return &ast.GenDecl{
|
||||
// Tok: token.VAR,
|
||||
// Specs: []ast.Spec{
|
||||
// &ast.ValueSpec{
|
||||
// Names: []*ast.Ident{
|
||||
// {
|
||||
// Name: name,
|
||||
// },
|
||||
// },
|
||||
// Values: []ast.Expr{value},
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
|
||||
func KeyValueExpr(key, value ast.Expr) *ast.KeyValueExpr {
|
||||
return &ast.KeyValueExpr{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func TypeAssert(x, t ast.Expr) *ast.TypeAssertExpr {
|
||||
return &ast.TypeAssertExpr{
|
||||
X: x,
|
||||
Type: t,
|
||||
}
|
||||
}
|
||||
|
||||
func TypeSwitch(x ast.Stmt, block *ast.BlockStmt) *ast.TypeSwitchStmt {
|
||||
return &ast.TypeSwitchStmt{
|
||||
Assign: x,
|
||||
Body: block,
|
||||
}
|
||||
}
|
||||
|
||||
func Not(x ast.Expr) *ast.UnaryExpr {
|
||||
return &ast.UnaryExpr{
|
||||
Op: token.NOT,
|
||||
X: x,
|
||||
}
|
||||
}
|
||||
|
||||
func Sub(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.SUB,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func BinAnd(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.AND,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
type EntryHolderSet struct {
|
||||
BaseName string
|
||||
Otherwise struct {
|
||||
Name string
|
||||
Type any
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"maps"
|
||||
"slices"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var boundaries = []rune{' ', '_', '-', '.', ',', ';', ':', '(', ')', '[', ']', '{', '}'}
|
||||
|
||||
func CamelCase(s string) (ret string) {
|
||||
atWordBoundary := true
|
||||
for _, c := range s {
|
||||
if slices.Contains(boundaries, c) {
|
||||
atWordBoundary = true
|
||||
continue
|
||||
}
|
||||
if atWordBoundary && unicode.IsLower(c) {
|
||||
c = unicode.ToUpper(c)
|
||||
}
|
||||
atWordBoundary = false
|
||||
ret += string(c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func OrderedKeys[Map map[K]V, K cmp.Ordered, V any](m Map) []K {
|
||||
keysIter := maps.Keys(m)
|
||||
keys := slices.Collect(keysIter)
|
||||
slices.Sort(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func ReverseMap[M map[K]V, K cmp.Ordered, V cmp.Ordered](m M) (ret map[V]K) {
|
||||
ret = map[V]K{}
|
||||
for k, v := range m {
|
||||
ret[v] = k
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func PopFront[T any](s []T) (front T, rest []T, ok bool) {
|
||||
if len(s) == 0 {
|
||||
return front, s, false
|
||||
}
|
||||
return s[0], s[1:], true
|
||||
}
|
||||
|
||||
func CombineNamAndData(name string, data any) []any {
|
||||
return []any{name, data}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCamelCase(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"hello world", "HelloWorld"},
|
||||
{"test_case", "TestCase"},
|
||||
{"simple-test", "SimpleTest"},
|
||||
{"already.Camel", "AlreadyCamel"},
|
||||
{"UPPER_CASE", "UPPERCASE"},
|
||||
{"mixed_Case_string", "MixedCaseString"},
|
||||
{"", ""},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := CamelCase(test.input)
|
||||
if result != test.expected {
|
||||
t.Errorf("CamelCase(%q) = %q; expected %q", test.input, result, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GeneratePacketIdentifierToType(versions []string) (decl ast.Decl) {
|
||||
var s []ast.Stmt
|
||||
for _, version := range versions {
|
||||
s = append(s, Case(Exprs(StrLit(version)), Stmts(
|
||||
Assign121(Ident("t"), Call(SelectorExprAndStr(Ident("v"+strings.ReplaceAll(version, ".", "_")), "PacketIdentifierToType"), Ident("d"), Ident("s"), Ident("i"))),
|
||||
)))
|
||||
}
|
||||
s = append(s, Case(nil, Stmts()))
|
||||
fn := NewFunc("PacketIdentifierToType", []NameAndType{
|
||||
{"v", Ident("string")},
|
||||
{"d", Ident("queser.Direction")},
|
||||
{"s", Ident("queser.State")},
|
||||
{"i", Ident("string")},
|
||||
}, []NameAndType{
|
||||
{"t", Ident("any")},
|
||||
})
|
||||
fn.Body = NewBlockEllipsis(SwitchStmt(Ident("v"), NewBlock(s)), Return())
|
||||
decl = fn
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
func PacketIdentifierToType(v string, d queser.Direction, s queser.State, i string) (t any) {
|
||||
switch v {
|
||||
case "1.21.8":
|
||||
t = v1_21_8.PacketIdentifierToType(d, s, i)
|
||||
default:
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TypeToPacketIdentifier(v string, d queser.Direction, s queser.State, t any) (i string) {
|
||||
switch v {
|
||||
case "1.21.8":
|
||||
i = v1_21_8.TypeToPacketIdentifier(d, s, t)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DecodePacket(v string, d queser.Direction, s queser.State, r io.Reader) (packet any, err error) {
|
||||
switch v {
|
||||
case "1.21.8":
|
||||
packet, err = v1_21_8.DecodePacket(d, s, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func EncodePacket(v string, d queser.Direction, s queser.State, i string, p any, w io.Writer) (err error) {
|
||||
switch v {
|
||||
case "1.21.8":
|
||||
err = v1_21_8.EncodePacket(d, s, i, p, w)
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func GenerateTypeToPacketIdentifier(versions []string) (decl ast.Decl) {
|
||||
var s []ast.Stmt
|
||||
for _, version := range versions {
|
||||
s = append(s, Case(Exprs(StrLit(version)), Stmts(
|
||||
Assign121(Ident("i"), Call(SelectorExprAndStr(Ident("v"+strings.ReplaceAll(version, ".", "_")), "TypeToPacketIdentifier"), Ident("d"), Ident("s"), Ident("t"))),
|
||||
)))
|
||||
}
|
||||
fn := NewFunc("TypeToPacketIdentifier", []NameAndType{
|
||||
{"v", Ident("string")},
|
||||
{"d", Ident("queser.Direction")},
|
||||
{"s", Ident("queser.State")},
|
||||
{"t", Ident("any")},
|
||||
}, []NameAndType{
|
||||
{"i", Ident("string")},
|
||||
})
|
||||
fn.Body = NewBlockEllipsis(SwitchStmt(Ident("v"), NewBlock(s)), Return())
|
||||
decl = fn
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateDecodePacket(versions []string) (decl ast.Decl) {
|
||||
var s []ast.Stmt
|
||||
for _, version := range versions {
|
||||
s = append(s, Case(Exprs(StrLit(version)), Stmts(
|
||||
Assign([]ast.Expr{Ident("packet"), Ident("err")}, []ast.Expr{Call(SelectorExprAndStr(Ident("v"+strings.ReplaceAll(version, ".", "_")), "DecodePacket"), Ident("d"), Ident("s"), Ident("r"))}),
|
||||
)))
|
||||
}
|
||||
fn := NewFunc("DecodePacket", []NameAndType{
|
||||
{"v", Ident("string")},
|
||||
{"d", Ident("queser.Direction")},
|
||||
{"s", Ident("queser.State")},
|
||||
{"r", Ident("io.Reader")},
|
||||
}, []NameAndType{
|
||||
{"packet", Ident("any")},
|
||||
{"err", Ident("error")},
|
||||
})
|
||||
fn.Body = NewBlockEllipsis(SwitchStmt(Ident("v"), NewBlock(s)), Return())
|
||||
decl = fn
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateEncodePacket(versions []string) (decl ast.Decl) {
|
||||
var s []ast.Stmt
|
||||
for _, version := range versions {
|
||||
s = append(s, Case(Exprs(StrLit(version)), Stmts(
|
||||
Assign121(Ident("err"), Call(SelectorExprAndStr(Ident("v"+strings.ReplaceAll(version, ".", "_")), "EncodePacket"), Ident("d"), Ident("s"), Ident("i"), Ident("p"), Ident("w"))),
|
||||
)))
|
||||
}
|
||||
fn := NewFunc("EncodePacket", []NameAndType{
|
||||
{"v", Ident("string")},
|
||||
{"d", Ident("queser.Direction")},
|
||||
{"s", Ident("queser.State")},
|
||||
{"i", Ident("string")},
|
||||
{"p", Ident("any")},
|
||||
{"w", Ident("io.Writer")},
|
||||
}, []NameAndType{
|
||||
{"err", Ident("error")},
|
||||
})
|
||||
fn.Body = NewBlockEllipsis(SwitchStmt(Ident("v"), NewBlock(s)), Return())
|
||||
decl = fn
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateVersionSwitcher(versions []string, printTo io.Writer) (err error) {
|
||||
f := NewFile("generated")
|
||||
AppendDecl(f, Import(
|
||||
"io",
|
||||
"github.com/admin-else/queser",
|
||||
"github.com/admin-else/queser/generated/v1_21_8"))
|
||||
AppendDecl(f, GeneratePacketIdentifierToType(versions))
|
||||
AppendDecl(f, GenerateTypeToPacketIdentifier(versions))
|
||||
AppendDecl(f, GenerateDecodePacket(versions))
|
||||
AppendDecl(f, GenerateEncodePacket(versions))
|
||||
return PrintToFile(f, printTo)
|
||||
}
|
||||
Reference in New Issue
Block a user