github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/internal/generator/jce_gen/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"fmt"
     7  	"go/ast"
     8  	"go/format"
     9  	"go/parser"
    10  	"go/token"
    11  	"io"
    12  	"os"
    13  	"reflect"
    14  	"strconv"
    15  )
    16  
    17  type JceField struct {
    18  	Name string
    19  	Type ast.Expr
    20  	Id   int
    21  }
    22  
    23  type JceStruct struct {
    24  	Name   string
    25  	Fields []JceField
    26  }
    27  
    28  type Generator struct {
    29  	JceStructs []JceStruct
    30  }
    31  
    32  func main() {
    33  	file := flag.String("file", "structs.txt", "file to parse")
    34  	output := flag.String("o", "", "output file")
    35  	flag.Parse()
    36  	f, err := parser.ParseFile(token.NewFileSet(), *file, nil, parser.ParseComments)
    37  	assert(err == nil, err)
    38  	g := Generator{}
    39  	ast.Inspect(f, func(n ast.Node) bool {
    40  		switch n := n.(type) {
    41  		case *ast.FuncDecl:
    42  			return false
    43  		case *ast.TypeSpec:
    44  			x, ok := n.Type.(*ast.StructType)
    45  			if !ok {
    46  				return false
    47  			}
    48  			if x.Fields != nil {
    49  				var jce JceStruct
    50  				for _, f := range x.Fields.List {
    51  					if f.Tag == nil {
    52  						continue
    53  					}
    54  					unquoted, _ := strconv.Unquote(f.Tag.Value)
    55  					tag, ok := reflect.StructTag(unquoted).Lookup("jceId")
    56  					if !ok {
    57  						continue
    58  					}
    59  					id, _ := strconv.Atoi(tag)
    60  					if len(f.Names) != 1 {
    61  						panic("unexpected name count")
    62  					}
    63  					jce.Fields = append(jce.Fields, JceField{
    64  						Name: f.Names[0].Name,
    65  						Type: f.Type,
    66  						Id:   id,
    67  					})
    68  				}
    69  				jce.Name = n.Name.Name
    70  				g.JceStructs = append(g.JceStructs, jce)
    71  			}
    72  		}
    73  		return true
    74  	})
    75  	buf := new(bytes.Buffer)
    76  	assert(err == nil, err)
    77  	g.Generate(buf)
    78  	formated, err := format.Source(buf.Bytes())
    79  	if err != nil {
    80  		fmt.Printf("%s\n", buf.Bytes())
    81  		panic(err)
    82  	}
    83  	_ = os.WriteFile(*output, formated, 0o644)
    84  }
    85  
    86  func (g Generator) Generate(w io.Writer) {
    87  	io.WriteString(w, "// Code generated by internal/generator/jce_gen; DO NOT EDIT.\n\npackage jce\n\n")
    88  	for _, jce := range g.JceStructs {
    89  		if jce.Name == "" || len(jce.Fields) == 0 {
    90  			continue
    91  		}
    92  
    93  		var generate func(typ ast.Expr, i int)
    94  		generate = func(typ ast.Expr, i int) {
    95  			field := jce.Fields[i]
    96  			switch typ := typ.(type) {
    97  			case *ast.Ident:
    98  				switch typ.Name {
    99  				case "uint8", "uint16", "uint32", "uint64":
   100  					typename := []byte(typ.Name)[1:]
   101  					casted := fmt.Sprintf("%s(pkt.%s)", typename, field.Name)
   102  					typename[0] ^= ' '
   103  					fmt.Fprintf(w, "w.Write%s(%s, %d)\n", typename, casted, field.Id)
   104  
   105  				case "int8", "int16", "int32", "int64",
   106  					"byte", "string":
   107  					typename := []byte(typ.Name)
   108  					typename[0] ^= ' '
   109  					fmt.Fprintf(w, "w.Write%s(pkt.%s, %d)\n", typename, field.Name, field.Id)
   110  
   111  				case "int":
   112  					fmt.Fprintf(w, "w.WriteInt64(int64(pkt.%s), %d)\n", field.Name, field.Id)
   113  
   114  				default:
   115  					fmt.Fprintf(w, `{ // write %s tag=%d
   116  					w.writeHead(10, %d)
   117  					w.buf.Write(pkt.%s.ToBytes())
   118  					w.writeHead(11, 0)}`+"\n", field.Name, field.Id, field.Id, field.Name)
   119  				}
   120  			case *ast.StarExpr:
   121  				_ = typ.X.(*ast.Ident) // assert
   122  				generate(typ.X, i)
   123  			case *ast.ArrayType:
   124  				assert(typ.Len == nil, "unexpected array len")
   125  				var method string
   126  				switch typeName(typ) {
   127  				case "[]byte":
   128  					method = "WriteBytes"
   129  				case "[]int64":
   130  					method = "WriteInt64Slice"
   131  				case "[][]byte":
   132  					method = "WriteBytesSlice"
   133  
   134  				default:
   135  					fmt.Fprintf(w,
   136  						"\t"+`{ // write pkt.%s tag=%d 
   137  	w.writeHead(9, %d)
   138  	if len(pkt.%s) == 0 {
   139  		w.writeHead(12, 0) // w.WriteInt32(0, 0)
   140  	} else {
   141  		w.WriteInt32(int32(len(pkt.%s)), 0)
   142  		for _, i := range pkt.%s {
   143  			w.writeHead(10, 0)
   144  			w.buf.Write(i.ToBytes())
   145  			w.writeHead(11, 0)
   146  		}
   147  	}}`+"\n", field.Name, field.Id, field.Id, field.Name, field.Name, field.Name)
   148  					return
   149  				}
   150  				assert(method != "", typeName(typ))
   151  				fmt.Fprintf(w, "w.%s(pkt.%s, %d)\n", method, field.Name, field.Id)
   152  			case *ast.MapType:
   153  				var method string
   154  				typName := typeName(typ)
   155  				switch typName {
   156  				case "map[string]string":
   157  					method = "writeMapStrStr"
   158  				case "map[string][]byte":
   159  					method = "writeMapStrBytes"
   160  				case "map[string]map[string][]byte":
   161  					method = "writeMapStrMapStrBytes"
   162  				}
   163  				assert(method != "", typName)
   164  				fmt.Fprintf(w, "w.%s(pkt.%s, %d)\n", method, field.Name, field.Id)
   165  			}
   166  		}
   167  
   168  		fmt.Fprintf(w, "func (pkt *%s) ToBytes() []byte {\nw := NewJceWriter()\n", jce.Name)
   169  		for i := range jce.Fields {
   170  			generate(jce.Fields[i].Type, i)
   171  		}
   172  		fmt.Fprintf(w, "return w.Bytes()\n}\n\n")
   173  	}
   174  }
   175  
   176  func assert(cond bool, val any) {
   177  	if !cond {
   178  		panic("assertion failed: " + fmt.Sprint(val))
   179  	}
   180  }
   181  
   182  func typeName(typ ast.Expr) string {
   183  	switch typ := typ.(type) {
   184  	case *ast.Ident:
   185  		return typ.Name
   186  	case *ast.StarExpr:
   187  		return "*" + typeName(typ.X)
   188  	case *ast.ArrayType:
   189  		if typ.Len != nil {
   190  			panic("unexpected array type")
   191  		}
   192  		return "[]" + typeName(typ.Elt)
   193  	case *ast.MapType:
   194  		return "map[" + typeName(typ.Key) + "]" + typeName(typ.Value)
   195  	}
   196  	panic("unexpected type")
   197  }