plc4go/tools/plc4xGenerator/main.go (791 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package main
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/format"
"go/token"
"go/types"
"log"
"os"
"path/filepath"
"strings"
"golang.org/x/tools/go/packages"
"github.com/apache/plc4x/plc4go/tools/common"
)
var (
typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
output = flag.String("output", "", "output file name; default srcdir/<type>_plc4xgen.go")
prefix = flag.String("prefix", "", "prefix for all generated files")
suffix = flag.String("suffix", "", "suffix for all generated files")
buildTags = flag.String("tags", "", "comma-separated list of build tags to apply")
licenseFile = flag.String("licenseFile", ".plc4xLicencer.header", "file containing the license (will be searched upwards)")
tests = flag.Bool("tests", false, "look at test files")
pkgIndex = flag.Int("pkgIndex", -1, "package index (can enforce a different index if multiple packages are found)")
verbose = flag.Bool("verbose", false, "verbosity")
)
// Usage is a replacement usage function for the flags package.
func Usage() {
_, _ = fmt.Fprintf(os.Stderr, "Usage of plc4xGenerator:\n")
_, _ = fmt.Fprintf(os.Stderr, "\tplc4xGenerator [flags] -type T [directory]\n")
_, _ = fmt.Fprintf(os.Stderr, "\tplc4xGenerator [flags] -type T files... # Must be a single package\n")
_, _ = fmt.Fprintf(os.Stderr, "Flags:\n")
flag.PrintDefaults()
}
var outputFile string // only used for test
func main() {
log.SetFlags(0)
log.SetPrefix("plc4xGenerator: ")
flag.Usage = Usage
flag.Parse()
if len(*typeNames) == 0 {
flag.Usage()
os.Exit(2)
}
typeList := strings.Split(*typeNames, ",")
var tags []string
if len(*buildTags) > 0 {
tags = strings.Split(*buildTags, ",")
}
args := flag.Args()
if len(args) == 0 {
args = []string{"."}
}
// Parse the package once.
var dir string
generator := Generator{}
if len(args) == 1 && isDirectory(args[0]) {
dir = args[0]
} else {
if len(tags) != 0 {
log.Fatal("-tags option applies only to directories, not when files are specified")
}
dir = filepath.Dir(args[0])
}
generator.parsePackage(args, tags)
// Print the header and package clause.
licenseFileName := *licenseFile
licenceContent := common.GetLicenseFileContent(licenseFileName, *verbose)
generator.Printf("%s", licenceContent)
generator.Printf("\n")
generator.Printf("// Code generated by \"plc4xGenerator %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
generator.Printf("\n")
generator.Printf("package %s", generator.pkg.name)
generator.Printf("\n")
generator.Printf("import (\n")
generator.Printf("\t\"context\"\n")
generator.Printf("\t\"encoding/binary\"\n")
generator.Printf("")
generator.Printf("\t\"github.com/apache/plc4x/plc4go/spi/utils\"\n")
generator.Printf("\t\"fmt\"\n")
generator.Printf(")\n")
generator.Printf("\n")
generator.Printf("var _ = fmt.Printf\n")
// Run generate for each type.
for _, typeName := range typeList {
generator.generate(typeName)
}
// Format the output.
src := generator.format()
// Write to file.
outputName := *output
if outputName == "" {
baseName := fmt.Sprintf("%s_plc4xgen.go", typeList[0])
outputName = filepath.Join(dir, baseName)
}
if *prefix != "" {
directory, file := filepath.Split(outputName)
outputName = filepath.Join(directory, *prefix+file)
}
if *suffix != "" {
directory, file := filepath.Split(outputName)
ext := filepath.Ext(file)
outputName = filepath.Join(directory, strings.ReplaceAll(file, ext, "")+*suffix+ext)
}
log.Printf("Writing to %s\n", outputName)
err := os.WriteFile(outputName, src, 0644)
if err != nil {
log.Fatalf("writing output: %s", err)
}
outputFile = outputName
}
// isDirectory reports whether the named file is a directory.
func isDirectory(name string) bool {
info, err := os.Stat(name)
if err != nil {
log.Fatal(err)
}
return info.IsDir()
}
// Generator holds the state of the analysis. Primarily used to buffer
// the output for format.Source.
type Generator struct {
buf bytes.Buffer // Accumulated output.
pkg *Package // Package we are scanning.
}
func (g *Generator) Printf(format string, args ...any) {
_, _ = fmt.Fprintf(&g.buf, format, args...)
}
// File holds a single parsed file and associated data.
type File struct {
pkg *Package // Package to which this file belongs.
file *ast.File // Parsed AST.
// These fields are reset for each type being generated.
typeName string // Name of the type.
fields []Field // Accumulator for fields of that type.
trimPrefix string
lineComment bool
}
type Package struct {
name string
defs map[*ast.Ident]types.Object
files []*File
}
// parsePackage analyzes the single package constructed from the patterns and tags.
// parsePackage exits if there is an error.
func (g *Generator) parsePackage(patterns []string, tags []string) {
cfg := &packages.Config{
Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
Tests: *tests,
BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))},
}
pkgs, err := packages.Load(cfg, patterns...)
if err != nil {
log.Fatal(err)
}
index := 0
if *pkgIndex > 0 {
index = *pkgIndex
}
if len(pkgs) != 1 {
if *pkgIndex < 0 {
log.Fatalf("error: %d packages found (%v)", len(pkgs), pkgs)
}
}
g.addPackage(pkgs[index])
}
// addPackage adds a type checked Package and its syntax files to the generator.
func (g *Generator) addPackage(pkg *packages.Package) {
g.pkg = &Package{
name: pkg.Name,
defs: pkg.TypesInfo.Defs,
files: make([]*File, len(pkg.Syntax)),
}
for i, file := range pkg.Syntax {
g.pkg.files[i] = &File{
file: file,
pkg: g.pkg,
}
}
}
// generate produces the String method for the named type.
func (g *Generator) generate(typeName string) {
fields := make([]Field, 0, 100)
for _, file := range g.pkg.files {
// Set the state for this run of the walker.
file.typeName = typeName
if file.file != nil {
ast.Inspect(file.file, file.genDecl)
fields = append(fields, file.fields...)
}
}
if len(fields) == 0 {
if *verbose {
log.Printf("no fields defined for type %s", typeName)
}
}
// TODO: for now we remove Default from the start (maybe move that to an option)
logicalTypeName := "\"" + strings.TrimPrefix(typeName, "Default") + "\""
// Generate code that will fail if the constants change value.
g.Printf("func (d *%s) Serialize() ([]byte, error) {\n", typeName)
g.Printf("\tif d == nil {\n")
g.Printf("\t\treturn nil, fmt.Errorf(\"(*DeviceInfoCache)(nil)\")\n")
g.Printf("\t}\n")
g.Printf("\twb := utils.NewWriteBufferByteBased(utils.WithByteOrderForByteBasedBuffer(binary.BigEndian))\n")
g.Printf("\tif err := d.SerializeWithWriteBuffer(context.Background(), wb); err != nil {\n")
g.Printf("\t\treturn nil, err\n")
g.Printf("\t}\n")
g.Printf("\treturn wb.GetBytes(), nil\n")
g.Printf("}\n\n")
g.Printf("func (d *%s) SerializeWithWriteBuffer(ctx context.Context, writeBuffer utils.WriteBuffer) error {\n", typeName)
g.Printf("\tif d == nil {\n")
g.Printf("\t\treturn fmt.Errorf(\"(*DeviceInfoCache)(nil)\")\n")
g.Printf("\t}\n")
g.Printf("\tif err := writeBuffer.PushContext(%s); err != nil {\n", logicalTypeName)
g.Printf("\t\treturn err\n")
g.Printf("\t}\n")
for _, field := range fields {
fieldType := field.fieldType
if field.isDelegate {
g.Printf("\t\t\tif err := d.%s.SerializeWithWriteBuffer(ctx, writeBuffer); err != nil {\n", fieldType.(*ast.Ident).Name)
g.Printf("\t\t\t\treturn err\n")
g.Printf("\t\t\t}\n")
continue
}
fieldName := field.name
fieldNameUntitled := "\"" + unTitle(fieldName) + "\""
if field.hasLocker != "" {
g.Printf("if err := func()error {\n")
g.Printf("\td.%s.Lock()\n", field.hasLocker)
g.Printf("\tdefer d.%s.Unlock()\n", field.hasLocker)
}
needsDereference := false
if starFieldType, ok := fieldType.(*ast.StarExpr); ok {
fieldType = starFieldType.X
needsDereference = true
}
if field.isStringer {
if needsDereference {
g.Printf("if d.%s != nil {", field.name)
}
g.Printf(stringFieldSerialize, "d."+field.name+".String()", fieldNameUntitled)
if field.hasLocker != "" {
g.Printf("\treturn nil\n")
g.Printf("}(); err != nil {\n")
g.Printf("\treturn err\n")
g.Printf("}\n")
}
if needsDereference {
g.Printf("}\n")
}
continue
}
if field.asPtr {
g.Printf(stringFieldSerialize, "fmt.Sprintf(\"%p\", d."+field.name+")", fieldNameUntitled)
continue
}
if field.directSerialize {
indentTimes := 0
if needsDereference {
indentTimes++
g.Printf("if d.%s != nil {", field.name)
}
g.Printf(indent(indentTimes, serializableDirectFieldTemplate), "d."+field.name+"", fieldNameUntitled)
if needsDereference {
g.Printf("}\n")
}
continue
}
switch fieldType := fieldType.(type) {
case *ast.SelectorExpr:
{
// TODO: bit hacky but not sure how else we catch those ones
x := fieldType.X
sel := fieldType.Sel
xIdent, xIsIdent := x.(*ast.Ident)
if xIsIdent {
if xIdent.Name == "atomic" {
if sel.Name == "Uint32" {
g.Printf(uint32FieldSerialize, "d."+field.name+".Load()", fieldNameUntitled)
if field.hasLocker != "" {
g.Printf("\treturn nil\n")
g.Printf("}(); err != nil {\n")
g.Printf("\treturn err\n")
g.Printf("}\n")
}
continue
}
if sel.Name == "Uint64" {
g.Printf(uint64FieldSerialize, "d."+field.name+".Load()", fieldNameUntitled)
if field.hasLocker != "" {
g.Printf("\treturn nil\n")
g.Printf("}(); err != nil {\n")
g.Printf("\treturn err\n")
g.Printf("}\n")
}
continue
}
if sel.Name == "Int32" {
g.Printf(int32FieldSerialize, "d."+field.name+".Load()", fieldNameUntitled)
if field.hasLocker != "" {
g.Printf("\treturn nil\n")
g.Printf("}(); err != nil {\n")
g.Printf("\treturn err\n")
g.Printf("}\n")
}
continue
}
if sel.Name == "Bool" {
g.Printf(boolFieldSerialize, "d."+field.name+".Load()", fieldNameUntitled)
if field.hasLocker != "" {
g.Printf("\treturn nil\n")
g.Printf("}(); err != nil {\n")
g.Printf("\treturn err\n")
g.Printf("}\n")
}
continue
}
if sel.Name == "Value" {
g.Printf(serializableFieldTemplate, "d."+field.name+".Load()", fieldNameUntitled)
if field.hasLocker != "" {
g.Printf("\treturn nil\n")
g.Printf("}(); err != nil {\n")
g.Printf("\treturn err\n")
g.Printf("}\n")
}
continue
}
}
if xIdent.Name == "sync" {
if *verbose {
fmt.Printf("\t skipping field %s because it is %v.%v\n", fieldName, x, sel)
}
if field.hasLocker != "" {
g.Printf("\treturn nil\n")
g.Printf("}(); err != nil {\n")
g.Printf("\treturn err\n")
g.Printf("}\n")
}
continue
}
if xIdent.Name == "zerolog" {
if *verbose {
fmt.Printf("\t skipping field %s because it is %v.%v\n", fieldName, x, sel)
}
continue
}
if xIdent.Name == "time" {
if sel.Name == "Time" || sel.Name == "Duration" {
deref := ""
indentTimes := 0
if needsDereference {
deref = "*"
indentTimes++
g.Printf("if d.%s != nil {", field.name)
}
g.Printf(indent(indentTimes, stringFieldSerialize), "fmt.Sprintf(\"%s\", "+deref+"d."+field.name+")", fieldNameUntitled)
if needsDereference {
g.Printf("}\n")
}
continue
}
}
}
}
g.Printf(serializableFieldTemplate, "d."+field.name, fieldNameUntitled)
case *ast.IndexExpr:
x := fieldType.X
if fieldType, isxFieldSelector := x.(*ast.SelectorExpr); isxFieldSelector { // TODO: we need to refactor this so we can reuse...
xIdent, xIsIdent := fieldType.X.(*ast.Ident)
sel := fieldType.Sel
if xIsIdent && xIdent.Name == "atomic" && sel.Name == "Pointer" {
g.Printf(atomicPointerFieldTemplate, "d."+field.name, field.name, fieldNameUntitled)
if field.hasLocker != "" {
g.Printf("\treturn nil\n")
g.Printf("}(); err != nil {\n")
g.Printf("\treturn err\n")
g.Printf("}\n")
}
continue
}
}
if *verbose {
fmt.Printf("Warn: no support yet for %#q\n", fieldType)
}
continue
case *ast.Ident:
deref := ""
indentTimes := 0
if needsDereference {
deref = "*"
indentTimes++
g.Printf("if d.%s != nil {", field.name)
}
switch fieldType.Name {
case "byte":
g.Printf(indent(indentTimes, byteFieldSerialize), deref+"d."+field.name, fieldNameUntitled)
case "int":
g.Printf(indent(indentTimes, int64FieldSerialize), "int64("+deref+"d."+field.name+")", fieldNameUntitled)
case "int32":
g.Printf(indent(indentTimes, int32FieldSerialize), "int32("+deref+"d."+field.name+")", fieldNameUntitled)
case "uint16":
g.Printf(indent(indentTimes, uint16FieldSerialize), deref+"d."+field.name, fieldNameUntitled)
case "uint32":
g.Printf(indent(indentTimes, uint32FieldSerialize), deref+"d."+field.name, fieldNameUntitled)
case "bool":
g.Printf(indent(indentTimes, boolFieldSerialize), deref+"d."+field.name, fieldNameUntitled)
case "string":
g.Printf(indent(indentTimes, stringFieldSerialize), deref+"d."+field.name, fieldNameUntitled)
case "error":
g.Printf(indent(indentTimes, errorFieldSerialize), deref+"d."+field.name, fieldNameUntitled)
default:
if *verbose {
fmt.Printf("\t Warn: no support implemented for Ident with type %v\n", fieldType)
}
g.Printf("{\n")
g.Printf("_value := fmt.Sprintf(\"%%v\", d.%s)\n", fieldName)
g.Printf(stringFieldSerialize, "_value", fieldNameUntitled)
g.Printf("}\n")
}
if needsDereference {
g.Printf("}\n")
}
case *ast.ArrayType:
if eltType, ok := fieldType.Elt.(*ast.Ident); ok && eltType.Name == "byte" {
g.Printf("if err := writeBuffer.WriteByteArray(%s, d.%s); err != nil {\n", fieldNameUntitled, field.name)
g.Printf("\treturn err\n")
g.Printf("}\n")
} else {
g.Printf("if err := writeBuffer.PushContext(%s, utils.WithRenderAsList(true)); err != nil {\n\t\treturn err\n\t}\n", fieldNameUntitled)
g.Printf("for _, elem := range d.%s {", field.name)
switch eltType := fieldType.Elt.(type) {
case *ast.SelectorExpr, *ast.StarExpr:
g.Printf("\n\t\tvar elem any = elem\n")
g.Printf(serializableFieldTemplate, "elem", "\"value\"")
case *ast.Ident:
switch eltType.Name {
case "int":
g.Printf(int64FieldSerialize, "int64(d."+field.name+")", fieldNameUntitled)
case "uint32":
g.Printf(uint32FieldSerialize, "d."+field.name, fieldNameUntitled)
case "bool":
g.Printf(boolFieldSerialize, "elem", "\"\"")
case "string":
g.Printf(stringFieldSerialize, "elem", "\"\"")
case "error":
g.Printf(errorFieldSerialize, "elem", "\"\"")
default:
if *verbose {
fmt.Printf("\t Warn: no support implemented for Ident within ArrayType for %v\n", fieldType)
}
g.Printf("_value := fmt.Sprintf(\"%%v\", elem)\n")
g.Printf(stringFieldSerialize, "_value", fieldNameUntitled)
}
}
g.Printf("}\n")
g.Printf("if err := writeBuffer.PopContext(%s, utils.WithRenderAsList(true)); err != nil {\n\t\treturn err\n\t}\n", fieldNameUntitled)
}
case *ast.MapType:
g.Printf("if err := writeBuffer.PushContext(%s, utils.WithRenderAsList(true)); err != nil {\n\t\treturn err\n\t}\n", fieldNameUntitled)
// TODO: we use serializable or strings as we don't want to over-complex this
g.Printf("for _name, elem := range d.%s {\n", fieldName)
switch keyType := fieldType.Key.(type) {
case *ast.Ident:
switch keyType.Name {
case "uint", "uint8", "uint16", "uint32", "uint64", "int", "int8", "int16", "int32", "int64": // TODO: add other types
g.Printf("\t\tname := fmt.Sprintf(\"%s\", _name)\n", "%v")
case "string":
g.Printf("\t\tname := _name\n")
default:
g.Printf("\t\tname := fmt.Sprintf(\"%s\", &_name)\n", "%v")
}
default:
g.Printf("\t\tname := fmt.Sprintf(\"%s\", &_name)\n", "%v")
}
switch eltType := fieldType.Value.(type) {
case *ast.StarExpr, *ast.SelectorExpr:
g.Printf("\n\t\tvar elem any = elem\n")
g.Printf("\t\tif serializable, ok := elem.(utils.Serializable); ok {\n")
g.Printf("\t\t\tif err := writeBuffer.PushContext(name); err != nil {\n")
g.Printf("\t\t\t\treturn err\n")
g.Printf("\t\t\t}\n")
g.Printf("\t\t\tif err := serializable.SerializeWithWriteBuffer(ctx, writeBuffer); err != nil {\n")
g.Printf("\t\t\t\treturn err\n")
g.Printf("\t\t\t}\n")
g.Printf("\t\t\tif err := writeBuffer.PopContext(name); err != nil {\n")
g.Printf("\t\t\t\treturn err\n")
g.Printf("\t\t\t}\n")
g.Printf("\t\t} else {\n")
g.Printf("\t\t\telemAsString := fmt.Sprintf(\"%%v\", elem)\n")
g.Printf("\t\t\tif err := writeBuffer.WriteString(name, uint32(len(elemAsString)*8), elemAsString); err != nil {\n")
g.Printf("\t\t\t\treturn err\n")
g.Printf("\t\t\t}\n")
g.Printf("\t\t}\n")
case *ast.Ident:
switch eltType.Name {
case "bool":
g.Printf(boolFieldSerialize, "elem", "name")
case "string":
g.Printf(stringFieldSerialize, "elem", "name")
case "error":
g.Printf(errorFieldSerialize, "elem", "name")
default:
if *verbose {
fmt.Printf("\t Warn: no support implemented for Ident within MapType for %v\n", fieldType)
}
g.Printf("\t\t_value := fmt.Sprintf(\"%%v\", elem)\n")
g.Printf(stringFieldSerialize, "_value", "name")
}
default:
if *verbose {
fmt.Printf("\t Warn: no support implemented within MapType %v\n", fieldType.Value)
}
g.Printf("\t\t_value := fmt.Sprintf(\"%%v\", elem)\n")
g.Printf(stringFieldSerialize, "_value", "name")
}
g.Printf("\t}\n")
g.Printf("if err := writeBuffer.PopContext(%s, utils.WithRenderAsList(true)); err != nil {\n\t\treturn err\n\t}\n", fieldNameUntitled)
case *ast.ChanType:
g.Printf(chanFieldSerialize, "d."+field.name, fieldNameUntitled, field.name)
case *ast.FuncType:
g.Printf(funcFieldSerialize, "d."+field.name, fieldNameUntitled)
default:
if *verbose {
fmt.Printf("Warn: no support implemented %#v\n", fieldType)
}
}
if field.hasLocker != "" {
g.Printf("\treturn nil\n")
g.Printf("}(); err != nil {\n")
g.Printf("\treturn err\n")
g.Printf("}\n")
}
}
g.Printf("\tif err := writeBuffer.PopContext(%s); err != nil {\n", logicalTypeName)
g.Printf("\t\treturn err\n")
g.Printf("\t}\n")
g.Printf("\treturn nil\n")
g.Printf("}\n")
g.Printf("\n")
g.Printf(stringerTemplate, typeName)
}
// format returns the gofmt-ed contents of the Generator's buffer.
func (g *Generator) format() []byte {
src, err := format.Source(g.buf.Bytes())
if err != nil {
// Should never happen, but can arise when developing this code.
// The user can compile the output to see the error.
log.Printf("warning: internal error: invalid Go generated: %s", err)
log.Printf("warning: compile the package to analyze the error")
return g.buf.Bytes()
}
return src
}
// Field represents a declared field.
type Field struct {
name string
fieldType ast.Expr
isDelegate bool
isStringer bool
asPtr bool
directSerialize bool
hasLocker string
}
func (f *Field) String() string {
return f.name
}
// genDecl processes one declaration clause.
func (f *File) genDecl(node ast.Node) bool {
decl, ok := node.(*ast.GenDecl)
if !ok || decl.Tok != token.TYPE {
// We only care about type declarations.
return true
}
for _, spec := range decl.Specs {
typeSpec := spec.(*ast.TypeSpec)
structDecl, ok := typeSpec.Type.(*ast.StructType)
if !ok {
continue
}
if typeSpec.Name.Name != f.typeName {
continue
}
if *verbose {
fmt.Printf("Handling %s\n", typeSpec.Name.Name)
}
for _, field := range structDecl.Fields.List {
if field.Tag != nil && field.Tag.Value == "`ignore:\"true\"`" {
var name string
if len(field.Names) != 0 {
name = field.Names[0].Name
} else {
name = "<delegate>"
}
if *verbose {
fmt.Printf("\t ignoring field %s %v\n", name, field.Type)
}
continue
}
isStringer := false
if field.Tag != nil && field.Tag.Value == "`stringer:\"true\"`" { // TODO: Check if we do that a bit smarter
isStringer = true
}
hasLocker := ""
if field.Tag != nil && strings.HasPrefix(field.Tag.Value, "`hasLocker:\"") { // TODO: Check if we do that a bit smarter
hasLocker = strings.TrimPrefix(field.Tag.Value, "`hasLocker:\"")
hasLocker = strings.TrimSuffix(hasLocker, "\"`")
}
asPtr := false
if field.Tag != nil && field.Tag.Value == "`asPtr:\"true\"`" { // TODO: Check if we do that a bit smarter
asPtr = true
}
directSerialize := false
if field.Tag != nil && field.Tag.Value == "`directSerialize:\"true\"`" { // TODO: Check if we do that a bit smarter
directSerialize = true
}
if len(field.Names) == 0 {
if *verbose {
fmt.Printf("\t adding delegate\n")
}
switch ft := field.Type.(type) {
case *ast.Ident:
f.fields = append(f.fields, Field{
fieldType: ft,
isDelegate: true,
isStringer: isStringer,
asPtr: asPtr,
hasLocker: hasLocker,
})
continue
case *ast.StarExpr:
switch set := ft.X.(type) {
case *ast.Ident:
f.fields = append(f.fields, Field{
fieldType: set,
isDelegate: true,
isStringer: isStringer,
asPtr: asPtr,
hasLocker: hasLocker,
})
continue
case *ast.SelectorExpr:
f.fields = append(f.fields, Field{
fieldType: set.Sel,
isDelegate: true,
isStringer: isStringer,
asPtr: asPtr,
hasLocker: hasLocker,
})
continue
default:
panic(fmt.Sprintf("Only pointer to struct delegates supported now. Type %T", field.Type))
}
case *ast.SelectorExpr:
f.fields = append(f.fields, Field{
fieldType: ft.Sel,
isDelegate: true,
isStringer: isStringer,
asPtr: asPtr,
hasLocker: hasLocker,
})
continue
default:
panic(fmt.Sprintf("Only struct delegates supported now. Type %T", field.Type))
}
}
if *verbose {
fmt.Printf("\t adding field %s %v\n", field.Names[0].Name, field.Type)
}
f.fields = append(f.fields, Field{
name: field.Names[0].Name,
fieldType: field.Type,
isStringer: isStringer,
asPtr: asPtr,
directSerialize: directSerialize,
hasLocker: hasLocker,
})
}
}
return false
}
func indent(times int, input string) string {
if times == 0 {
return input
}
lines := strings.Split(input, "\n")
indents := strings.Repeat("\t", times)
return indents + strings.Join(lines, "\n"+indents)
}
var stringerTemplate = `
func (d *%s) String() string {
if alternateStringer, ok := any(d).(utils.AlternateStringer); ok {
if alternateString, use := alternateStringer.AlternateString(); use {
return alternateString
}
}
wb := utils.NewWriteBufferBoxBased(utils.WithWriteBufferBoxBasedMergeSingleBoxes(), utils.WithWriteBufferBoxBasedOmitEmptyBoxes())
if err := wb.WriteSerializable(context.Background(), d); err != nil {
return err.Error()
}
return wb.GetBox().String()
}
`
var serializableDirectFieldTemplate = `
if err := writeBuffer.PushContext(%[2]s); err != nil {
return err
}
if err := %[1]s.SerializeWithWriteBuffer(ctx, writeBuffer); err != nil {
return err
}
if err := writeBuffer.PopContext(%[2]s); err != nil {
return err
}
`
var serializableFieldTemplate = `
if %[1]s != nil {
if serializableField, ok := any(%[1]s).(utils.Serializable); ok {
if err := writeBuffer.PushContext(%[2]s); err != nil {
return err
}
if err := serializableField.SerializeWithWriteBuffer(ctx, writeBuffer); err != nil {
return err
}
if err := writeBuffer.PopContext(%[2]s); err != nil {
return err
}
} else {
stringValue := fmt.Sprintf("%%v", %[1]s)
if err := writeBuffer.WriteString(%[2]s, uint32(len(stringValue)*8), stringValue); err != nil {
return err
}
}
}
`
var atomicPointerFieldTemplate = `
if %[2]sLoaded :=%[1]s.Load(); %[2]sLoaded != nil && *%[2]sLoaded != nil {
%[2]s := *%[2]sLoaded
if serializableField, ok := %[2]s.(utils.Serializable); ok {
if err := writeBuffer.PushContext(%[3]s); err != nil {
return err
}
if err := serializableField.SerializeWithWriteBuffer(ctx, writeBuffer); err != nil {
return err
}
if err := writeBuffer.PopContext(%[3]s); err != nil {
return err
}
} else {
stringValue := fmt.Sprintf("%%v", %[2]s)
if err := writeBuffer.WriteString(%[3]s, uint32(len(stringValue)*8), stringValue); err != nil {
return err
}
}
}
`
var byteFieldSerialize = `
if err := writeBuffer.WriteByte(%[2]s, %[1]s); err != nil {
return err
}
`
var int32FieldSerialize = `
if err := writeBuffer.WriteInt32(%[2]s, 32, %[1]s); err != nil {
return err
}
`
var int64FieldSerialize = `
if err := writeBuffer.WriteInt64(%[2]s, 64, %[1]s); err != nil {
return err
}
`
var uint16FieldSerialize = `
if err := writeBuffer.WriteUint16(%[2]s, 16, %[1]s); err != nil {
return err
}
`
var uint32FieldSerialize = `
if err := writeBuffer.WriteUint32(%[2]s, 32, %[1]s); err != nil {
return err
}
`
var uint64FieldSerialize = `
if err := writeBuffer.WriteUint64(%[2]s, 64, %[1]s); err != nil {
return err
}
`
var boolFieldSerialize = `
if err := writeBuffer.WriteBit(%[2]s, %[1]s); err != nil {
return err
}
`
var stringFieldSerialize = `
if err := writeBuffer.WriteString(%[2]s, uint32(len(%[1]s)*8), %[1]s); err != nil {
return err
}
`
var errorFieldSerialize = `
if %[1]s != nil {
_errString := %[1]s.Error()
if err := writeBuffer.WriteString(%[2]s, uint32(len(_errString)*8), _errString); err != nil {
return err
}
}
`
var chanFieldSerialize = `
_%[3]s_plx4gen_description := fmt.Sprintf("%%d element(s)", len(%[1]s))
if err := writeBuffer.WriteString(%[2]s, uint32(len(_%[3]s_plx4gen_description)*8), _%[3]s_plx4gen_description); err != nil {
return err
}
`
var funcFieldSerialize = `
if err := writeBuffer.WriteBit(%[2]s, %[1]s != nil); err != nil {
return err
}
`
func unTitle(value string) string {
return strings.ToLower(string(value[0])) + value[1:]
}