代码拉取完成,页面将自动刷新
package main
import (
"go/ast"
"go/token"
"go/types"
"os"
"gitee.com/FlyingOnion/wtflog"
. "gitee.com/FlyingOnion/wtflog/types"
"golang.org/x/tools/go/packages"
)
var (
logger = wtflog.NewTextLogger()
)
type spec struct {
has bool
idents []identSpec
structs []structSpec
maps []mapSpec
arrays []arraySpec
}
type arraySpec struct {
name string
arrayType *ast.ArrayType
}
type identSpec struct {
name string
ident *ast.Ident
}
type structSpec struct {
name string
structType *ast.StructType
}
type mapSpec struct {
keyType string
isValueBasicType bool
name string
mapType *ast.MapType
}
func getPkg(path string) (*packages.Package, error) {
cfg := &packages.Config{
Mode: packages.LoadSyntax,
Dir: path,
Tests: false,
}
pkgs, err := packages.Load(cfg)
if err != nil {
return nil, err
}
if len(pkgs) > 0 {
return pkgs[0], nil
}
return nil, nil
}
func getSpecs(pkg *packages.Package) *spec {
structs := []structSpec{}
maps := []mapSpec{}
idents := []identSpec{}
arrays := []arraySpec{}
has := false
// fmt.Println(pkg.TypesInfo.Types)
for _, f := range pkg.Syntax {
for _, decl := range f.Decls {
if gdecl, ok := decl.(*ast.GenDecl); ok && gdecl.Tok == token.TYPE {
for _, spec := range gdecl.Specs {
if typeSpec, ok2 := spec.(*ast.TypeSpec); ok2 {
if typeSpec.Name.Name == "_" {
continue
}
has = true
switch t := typeSpec.Type.(type) {
case *ast.StructType:
structs = append(structs, structSpec{
name: typeSpec.Name.Name,
structType: t,
})
case *ast.Ident:
if tv, ok := pkg.TypesInfo.Types[t]; ok {
if types.IsInterface(tv.Type) {
logger.Warn(typeSpec.Name.Name, "is an interface, skipped.")
} else {
idents = append(idents, identSpec{
name: typeSpec.Name.Name,
ident: t,
})
}
}
case *ast.ArrayType:
arrays = append(arrays, arraySpec{
name: typeSpec.Name.Name,
arrayType: t,
})
case *ast.MapType:
// 一些自定义的类型,底层是int, string基本类型的,也需要支持
if keyTypeValue, ok := pkg.TypesInfo.Types[t.Key]; ok {
keyType := keyTypeValue.Type.Underlying().String()
switch keyType {
case "string", "byte",
"int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64":
isValueBasicType := false
vt := types.ExprString(t.Value)
switch vt {
case "string", "byte",
"int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64":
isValueBasicType = true
}
maps = append(maps, mapSpec{
keyType: keyType,
isValueBasicType: isValueBasicType,
name: typeSpec.Name.Name,
mapType: t,
})
default:
logger.Warn(typeSpec.Name.Name, "is a map with unsupported key type.")
logger.Warn("Supported key types (underlying): string, byte, and int/uint family.")
}
}
}
}
}
}
}
}
return &spec{
has: has,
structs: structs,
idents: idents,
arrays: arrays,
maps: maps,
}
}
func main() {
paths := os.Args[1:]
if len(paths) == 0 {
paths = []string{"."}
}
b := NewByteSlice(make([]byte, 0, 200))
showBuiltinFieldWarning := true
for _, path := range paths {
pkg, err := getPkg(path)
if err != nil {
logger.Error(err)
os.Exit(1)
}
if pkg == nil {
logger.Info("No packages found in", path)
continue
}
ss := getSpecs(pkg)
if !ss.has {
logger.Info("No custom types in", path)
continue
}
logger.Info("Got",
Int(len(ss.structs)), "type struct(s),",
Int(len(ss.arrays)), "type array(s),",
Int(len(ss.idents)), "type ident(s),",
Int(len(ss.maps)), "type map(s) in",
path,
)
b.Reset()
b.AppendString("// Code generated by gen-bsa; DO NOT EDIT.\n\n").
AppendString("package ").AppendString(pkg.Name).AppendString("\n\n"). // TODO: modify package name
AppendString("import \"fmt\"\n").
AppendString("import \"sort\"\n").
AppendString("import \"unsafe\"\n").
AppendString("import . \"gitee.com/FlyingOnion/wtflog/types\"\n\n"). // TODO: modify import pkg
AppendString("func fmtPrint(vs ...interface{}) string { return fmt.Sprintf(\"%+v\", vs...) }\n\n").
AppendString("func fmtBool(b bool) string { if b { return \"true\" }; return \"false\"}\n\n").
AppendString("func sortKeys(keys interface{}, less func(i, j int) bool) { sort.Slice(keys, less) }\n").
AppendString("var _ unsafe.Pointer\n\n")
// generate AppendTo for custom structures
for _, _struct := range ss.structs {
if len(_struct.structType.Fields.List) == 0 {
b.AppendString("func (").AppendString(_struct.name).AppendString(") AppendTo(b *ByteSlice) { b.AppendString(\"{}\") }\n\n")
continue
}
b.AppendString("func (p ").AppendString(_struct.name).AppendString(") AppendTo(b *ByteSlice) {\n").
AppendString("\tvar any interface{}\n").
AppendString("\t_ = any\n").
AppendString("\tb.AppendString(\"{")
hasLoggedField := false
for _, structField := range _struct.structType.Fields.List {
if len(structField.Names) == 0 {
if hasLoggedField {
// b.AppendString("\tb.AppendByte(' ').AppendString(\"")
b.AppendString("\tb.AppendString(\" ")
} else {
hasLoggedField = true
}
// a 0-name field means a builtin type
if showBuiltinFieldWarning {
logger.Warn("Builtin struct field is not fully supported. The generated codes may have syntax errors, or the log will be different from your expectation. We suggest you set the name of the field explicitly.")
showBuiltinFieldWarning = false
}
if t, ok := structField.Type.(*ast.Ident); ok {
name := "p." + t.Name
b.AppendString(t.Name).AppendString(":\")\n")
switch t.Name {
case "bool":
appendBool(b, name, 1)
case "byte":
appendByte(b, name, 1)
case "string":
appendString(b, name, 1)
case "error":
appendError(b, name, 1)
case "int", "int8", "int16", "int32", "int64":
appendInt(b, name, 1)
case "uint", "uint8", "uint16", "uint32", "uint64":
appendUint(b, name, 1)
default:
appendAny(b, name, 1)
}
continue
}
if t, ok := structField.Type.(*ast.StarExpr); ok {
switch x := t.X.(type) {
case *ast.Ident:
name := "p." + x.Name
b.AppendString(x.Name).AppendString(":\")\n")
b.AppendString("\tif ").AppendString(name).AppendString(" == nil {\n\t\tb.AppendString(\"<nil>\")\n\t} else {\n")
switch x.Name {
case "bool":
appendPointer(b, name, 2)
appendPointerElem(b, appendBool, name, 2)
// b.AppendString("\t\tb.AppendPointer(uintptr(unsafe.Pointer(elem))).\n").
// AppendString("\t\t\tAppendByte('(').AppendString(fmtBool(*elem)).AppendByte(')')\n")
case "byte":
appendPointer(b, name, 2)
appendPointerElem(b, appendByte, name, 2)
// b.AppendString("\t\tb.AppendPointer(uintptr(unsafe.Pointer(elem))).\n").
// AppendString("\t\t\tAppendByte('(').AppendByte(*elem)).AppendByte(')')\n")
case "string":
appendPointer(b, name, 2)
appendPointerElem(b, appendString, name, 2)
case "int", "int8", "int16", "int32", "int64":
appendPointer(b, name, 2)
appendPointerElem(b, appendInt, name, 2)
case "uint", "uint8", "uint16", "uint32", "uint64":
appendPointer(b, name, 2)
appendPointerElem(b, appendUint, name, 2)
default:
appendAny(b, name, 2)
}
b.AppendString("\t}\n")
case *ast.SelectorExpr:
name := "p." + x.Sel.Name
b.AppendString("\tif ").AppendString(name).AppendString(" == nil {\n\t\tb.AppendString(\"<nil>\")\n\t} else {\n")
appendAny(b, name, 2)
b.AppendString("\t}\n")
}
continue
}
continue
}
// for j, fieldName := range structField.Names {
// }
// if structField.Names[0].Name != "_" {
// if i > 0 {
// b.AppendString("\tb.AppendByte(' ').AppendString(\"")
// }
// } else {
// continue
// }
for _, fieldName := range structField.Names {
if fieldName.Name == "_" {
continue
}
if hasLoggedField {
// b.AppendString("\tb.AppendByte(' ').AppendString(\"")
b.AppendString("\tb.AppendString(\" ")
} else {
hasLoggedField = true
}
b.AppendString(fieldName.Name).AppendString(":\")\n") // add field name
name := "p." + fieldName.Name
switch t := structField.Type.(type) {
case *ast.Ident:
switch t.Name {
case "bool":
appendBool(b, name, 1)
case "byte":
appendByte(b, name, 1)
case "rune":
appendRune(b, name, 1)
case "string":
appendString(b, name, 1)
case "error":
appendError(b, name, 1)
case "int", "int8", "int16", "int32", "int64":
appendInt(b, name, 1)
case "uint", "uint8", "uint16", "uint32", "uint64":
appendUint(b, name, 1)
default:
appendAny(b, name, 1)
}
case *ast.ArrayType:
b.AppendString("\tb.AppendByte('[')\n").
AppendString("\tfor i, elem := range p.").AppendString(fieldName.Name).AppendString(" {\n").
AppendString("\t\tif i > 0 { b.AppendByte(' ') }\n")
switch et := t.Elt.(type) {
case *ast.Ident:
switch et.Name {
// case "byte":
// appendUint(b, "elem", 2)
// b.AppendString("\t\tb.AppendUint64(uint64(elem))\n")
case "bool":
appendBool(b, "elem", 2)
// b.AppendString("\t\tb.AppendString(fmtBool(elem))\n")
case "string":
appendString(b, "elem", 2)
// b.AppendString("\t\tb.AppendString(elem)\n")
case "error":
appendError(b, "elem", 2)
// b.AppendString("\t\tb.AppendString(elem.Error())\n")
case "int", "int8", "int16", "int32", "int64":
appendInt(b, "elem", 2)
// b.AppendString("\t\tb.AppendInt64(int64(elem))\n")
case "byte", "uint", "uint8", "uint16", "uint32", "uint64":
appendUint(b, "elem", 2)
// b.AppendString("\t\tb.AppendUint64(uint64(elem))\n")
default:
appendAny(b, "elem", 2)
// b.AppendString("\t\tany = elem\n").
// AppendString("\t\tif bsa, ok := any.(ByteSliceAppender); ok { bsa.AppendTo(b) } else if str, ok := any.(Stringer); ok { b.AppendString(str.String()) } else { b.AppendString(fmtPrint(any)) }\n")
}
b.AppendString("\t}\n\tb.AppendByte(']')\n")
if et.Name == "byte" {
b.AppendString("\tif len(p.").AppendString(fieldName.Name).AppendString(") > 0 { b.AppendByte('(').AppendBytes(p.").AppendString(fieldName.Name).AppendString(").AppendByte(')') }\n")
}
case *ast.StarExpr:
b.AppendString("\t\tif elem == nil { b.AppendString(\"<nil>\"); continue }\n")
switch x := et.X.(type) {
case *ast.Ident:
switch x.Name {
case "bool":
appendPointer(b, "elem", 2)
appendPointerElem(b, appendBool, "elem", 2)
// b.AppendString("\t\tb.AppendPointer(uintptr(unsafe.Pointer(elem))).\n").
// AppendString("\t\t\tAppendByte('(').AppendString(fmtBool(*elem)).AppendByte(')')\n")
case "byte":
appendPointer(b, "elem", 2)
appendPointerElem(b, appendByte, "elem", 2)
// b.AppendString("\t\tb.AppendPointer(uintptr(unsafe.Pointer(elem))).\n").
// AppendString("\t\t\tAppendByte('(').AppendByte(*elem)).AppendByte(')')\n")
case "string":
appendPointer(b, "elem", 2)
appendPointerElem(b, appendString, "elem", 2)
case "int", "int8", "int16", "int32", "int64":
appendPointer(b, "elem", 2)
appendPointerElem(b, appendInt, "elem", 2)
case "uint", "uint8", "uint16", "uint32", "uint64":
appendPointer(b, "elem", 2)
appendPointerElem(b, appendUint, "elem", 2)
// switch x.Name {
// case "bool", "byte", "string",
// "int", "int8", "int16", "int32", "int64",
// "uint", "uint8", "uint16", "uint32", "uint64":
// // TODO: modify each type pointer
// // b.AppendString("\tfor i, elem := range p.").AppendString(fieldName.Name).AppendString(" {\n").
// // AppendString("\t\tif i > 0 { b.AppendString(\" \") }\n").
// // b.AppendString("\t\tif elem == nil { b.AppendString(\"<nil>\"); continue }\n").
// b.AppendString("\t\tb.AppendPointer(uintptr(unsafe.Pointer(elem))).AppendByte('(')\n")
// // AppendString("\t}\n")
// // AppendString("\tb.AppendByte(']')\n")
default:
// b.AppendString("\tfor i, elem := range p.").AppendString(fieldName.Name).AppendString(" {\n").
// AppendString("\t\tif i > 0 { b.AppendString(\" \") }\n").
// b.AppendString("\t\tany = elem\n").
// AppendString("\t\tif bsa, ok := any.(ByteSliceAppender); ok { bsa.AppendTo(b) } else if str, ok := any.(Stringer); ok { b.AppendString(str.String()) } else { b.AppendString(fmtPrint(any)) }\n").
appendAny(b, "elem", 2)
// b.AppendString("\t}\n")
// AppendString("\tb.AppendByte(']')\n")
}
default:
appendAny(b, "elem", 2)
// b.AppendString("\tfor i, elem := range p.").AppendString(fieldName.Name).AppendString(" {\n").
// AppendString("\t\tif i > 0 { b.AppendString(\" \") }\n").
// AppendString("\t\tany = elem\n").
// AppendString("\t\tif bsa, ok := any.(ByteSliceAppender); ok { bsa.AppendTo(b) } else if str, ok := any.(Stringer); ok { b.AppendString(str.String()) } else { b.AppendString(fmtPrint(any)) }\n").
// AppendString("\t}\n")
}
b.AppendString("\t}\n\tb.AppendByte(']')\n")
default:
appendAny(b, "elem", 2)
b.AppendString("\t}\n\tb.AppendByte(']')\n")
}
// b.AppendString("\t}\n")
case *ast.SelectorExpr:
appendAny(b, name, 1)
case *ast.StarExpr:
switch x := t.X.(type) {
case *ast.Ident:
b.AppendString("\tif ").AppendString(name).AppendString(" == nil {\n\t\tb.AppendString(\"<nil>\")\n\t} else {\n")
switch x.Name {
case "bool":
appendPointer(b, name, 2)
appendPointerElem(b, appendBool, name, 2)
// b.AppendString("\t\tb.AppendPointer(uintptr(unsafe.Pointer(elem))).\n").
// AppendString("\t\t\tAppendByte('(').AppendString(fmtBool(*elem)).AppendByte(')')\n")
case "byte":
appendPointer(b, name, 2)
appendPointerElem(b, appendByte, name, 2)
// b.AppendString("\t\tb.AppendPointer(uintptr(unsafe.Pointer(elem))).\n").
// AppendString("\t\t\tAppendByte('(').AppendByte(*elem)).AppendByte(')')\n")
case "string":
appendPointer(b, name, 2)
appendPointerElem(b, appendString, name, 2)
case "int", "int8", "int16", "int32", "int64":
appendPointer(b, name, 2)
appendPointerElem(b, appendInt, name, 2)
case "uint", "uint8", "uint16", "uint32", "uint64":
appendPointer(b, name, 2)
appendPointerElem(b, appendUint, name, 2)
default:
appendAny(b, name, 2)
}
b.AppendString("\t}\n")
case *ast.SelectorExpr:
b.AppendString("\tif ").AppendString(name).AppendString(" == nil {\n\t\tb.AppendString(\"<nil>\")\n\t} else {\n")
appendAny(b, name, 2)
b.AppendString("\t}\n")
}
case *ast.FuncType, *ast.ChanType:
appendPointer(b, "&p."+fieldName.Name, 1)
}
}
}
if !hasLoggedField {
b.AppendString("}\")\n}\n\n")
continue
}
b.AppendString("\tb.AppendByte('}')\n}\n\n")
}
// generate AppendTo for custom idents
for _, _ident := range ss.idents {
b.AppendString("func (p ").AppendString(_ident.name).AppendString(") AppendTo(b *ByteSlice) {\n")
switch _ident.ident.Name {
case "bool":
appendBool(b, "bool(p)", 1)
// error cannot be initialized
// case "error":
// appendError(b, "error(p)", 1)
case "string":
appendString(b, "string(p)", 1)
case "rune", "int", "int8", "int16", "int32", "int64":
appendInt(b, "p", 1)
case "byte", "uint", "uint8", "uint16", "uint32", "uint64":
appendUint(b, "p", 1)
default:
b.AppendString("\tvar any interface{}\n").
AppendString("\t_ = any\n")
appendAny(b, _ident.ident.Name+"(p)", 1)
}
b.AppendString("}\n\n")
}
for _, _array := range ss.arrays {
b.AppendString("func (p ").AppendString(_array.name).AppendString(") AppendTo(b *ByteSlice) {\n").
AppendString("\tif p == nil { b.AppendString(\"<nil>\"); return }\n").
AppendString("\tvar any interface{}\n").
AppendString("\t_ = any\n").
AppendString("\tb.AppendByte('[')\n").
AppendString("\tfor i, elem := range p {\n").
AppendString("\t\tif i > 0 { b.AppendByte(' ') }\n")
switch et := _array.arrayType.Elt.(type) {
case *ast.Ident:
switch et.Name {
case "bool":
appendBool(b, "elem", 2)
case "string":
appendString(b, "elem", 2)
case "error":
appendError(b, "elem", 2)
case "int", "int8", "int16", "int32", "int64":
appendInt(b, "elem", 2)
case "byte", "uint", "uint8", "uint16", "uint32", "uint64":
appendUint(b, "elem", 2)
default:
appendAny(b, "elem", 2)
}
default:
appendAny(b, "elem", 2)
}
b.AppendString("\t}\n").
AppendString("\tb.AppendByte(']')\n").
AppendString("}\n\n")
}
for _, _map := range ss.maps {
vType := "interface{}"
if _map.isValueBasicType {
vType = types.ExprString(_map.mapType.Value)
}
kvStruct := NewByteSlice(make([]byte, 0, 80))
kvStruct.AppendString("struct{k ").AppendString(_map.keyType).AppendString("; v ").AppendString(vType).AppendByte('}')
b.AppendString("func (p ").AppendString(_map.name).AppendString(") AppendTo(b *ByteSlice) {\n").
AppendString("\tif p == nil { b.AppendString(\"<nil>\"); return }\n").
AppendString("\tif len(p) == 0 { b.AppendString(\"{}\"); return }\n").
AppendString("\tvar any interface{}\n").
AppendString("\t_ = any\n").
AppendString("\tb.AppendByte('{')\n").
AppendString("\tkvs := make([]").Append(kvStruct).AppendString(", 0, len(p))\n").
AppendString("\tfor k, v := range p { kvs = append(kvs, ").Append(kvStruct).AppendString("{k: ").AppendString(_map.keyType).AppendString("(k), v: ").AppendString(vType).AppendString("(v)}) }\n").
AppendString("\tsortKeys(kvs, func(i, j int) bool { return kvs[i].k < kvs[j].k })\n").
AppendString("\tfor i, kv := range kvs {\n").
AppendString("\t\tif i > 0 { b.AppendByte(' ') }\n")
switch _map.keyType {
case "byte":
appendByte(b, "kv.k", 2)
case "string":
appendString(b, "kv.k", 2)
case "int", "int8", "int16", "int32", "int64":
appendInt(b, "kv.k", 2)
case "uint", "uint8", "uint16", "uint32", "uint64":
appendUint(b, "kv.k", 2)
default:
b.AppendString("\t\tb.AppendString(fmtPrint(kv.k))\n")
}
b.AppendString("\t\tb.AppendByte(':')\n")
switch vType {
case "bool":
appendBool(b, "kv.v", 2)
case "byte":
appendByte(b, "kv.v", 2)
case "string":
appendString(b, "kv.v", 2)
case "error":
appendError(b, "kv.v", 2)
case "int", "int8", "int16", "int32", "int64":
appendInt(b, "kv.v", 2)
case "uint", "uint8", "uint16", "uint32", "uint64":
appendUint(b, "kv.v", 2)
case "interface{}":
fallthrough
default:
appendAny(b, "kv.v", 2)
}
b.AppendString("\t}\n").
AppendString("\tb.AppendByte('}')\n").
AppendString("}\n\n")
}
filepath := path + "/wtf__.go"
logger.Info("Write to file", filepath)
f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
logger.Error(err)
panic(err)
}
f.Write(b.Bytes())
f.Close()
}
logger.Info("Done.")
}
const indent = "\t" // a tab
// type typeAppender struct {
// basic, pointer func(b *ByteSlice, name string, level int)
// }
func appendByte(b *ByteSlice, name string, level int) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("b.AppendByte(").AppendString(name).AppendString(")\n")
}
func appendRune(b *ByteSlice, name string, level int) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("b.AppendRune(").AppendString(name).AppendString(")\n")
}
func appendInt(b *ByteSlice, name string, level int) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("b.AppendInt64(int64(").AppendString(name).AppendString("))\n")
}
func appendUint(b *ByteSlice, name string, level int) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("b.AppendUint64(uint64(").AppendString(name).AppendString("))\n")
}
func appendBool(b *ByteSlice, name string, level int) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("b.AppendString(fmtBool(").AppendString(name).AppendString("))\n")
}
func appendString(b *ByteSlice, name string, level int) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("b.AppendString(").AppendString(name).AppendString(")\n")
}
func appendAny(b *ByteSlice, name string, level int) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("any = ").AppendString(name).AppendByte('\n')
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("if bsa, ok := any.(ByteSliceAppender); ok { bsa.AppendTo(b) } else if str, ok := any.(Stringer); ok { b.AppendString(str.String()) } else { b.AppendString(fmtPrint(any)) }\n")
}
func appendError(b *ByteSlice, name string, level int) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("if ").AppendString(name).AppendString(" == nil { b.AppendString(\"<nil>\") } else { b.AppendString(\"error(\").AppendString(").AppendString(name).AppendString(".Error()).AppendString(\")\") }\n")
}
func appendPointer(b *ByteSlice, name string, level int) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("b.AppendPointer(uintptr(unsafe.Pointer(").AppendString(name).AppendString(")))\n")
}
func appendPointerElem(b *ByteSlice,
appendElem func(*ByteSlice, string, int),
ptrName string,
level int,
) {
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("b.AppendByte('(')\n")
appendElem(b, "*"+ptrName, level)
for i := 0; i < level; i++ {
b.AppendString(indent)
}
b.AppendString("b.AppendByte(')')\n")
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。