github.com/mailru/activerecord@v1.12.2/internal/pkg/generator/octopus.go (about)

     1  package generator
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	_ "embed"
     7  	"log"
     8  	"strings"
     9  	"text/template"
    10  
    11  	"github.com/mailru/activerecord/internal/pkg/arerror"
    12  	"github.com/mailru/activerecord/internal/pkg/ds"
    13  	"github.com/mailru/activerecord/pkg/octopus"
    14  	"golang.org/x/text/cases"
    15  	"golang.org/x/text/language"
    16  )
    17  
    18  //nolint:revive
    19  //go:embed tmpl/octopus/mock.tmpl
    20  var OctopusMockRepositoryTmpl string
    21  
    22  //nolint:revive
    23  //go:embed tmpl/octopus/main.tmpl
    24  var OctopusRootRepositoryTmpl string
    25  
    26  //nolint:revive
    27  //go:embed tmpl/octopus/fixture.tmpl
    28  var OctopusFixtureRepositoryTmpl string
    29  
    30  func GenerateOctopus(params PkgData) (map[string]bytes.Buffer, *arerror.ErrGeneratorPhases) {
    31  	octopusWriter := bytes.Buffer{}
    32  	mockWriter := bytes.Buffer{}
    33  	fixtureWriter := bytes.Buffer{}
    34  
    35  	octopusFile := bufio.NewWriter(&octopusWriter)
    36  
    37  	//TODO возможно имеет смысл разделить большой шаблон OctopusRootRepositoryTmpl для удобства поддержки
    38  	err := GenerateByTmpl(octopusFile, params, "octopus", OctopusRootRepositoryTmpl, OctopusTemplateFuncs)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	octopusFile.Flush()
    44  
    45  	mockFile := bufio.NewWriter(&mockWriter)
    46  
    47  	err = GenerateByTmpl(mockFile, params, "octopus", OctopusMockRepositoryTmpl, OctopusTemplateFuncs)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	mockFile.Flush()
    53  
    54  	fixtureFile := bufio.NewWriter(&fixtureWriter)
    55  
    56  	err = GenerateByTmpl(fixtureFile, params, "octopus", OctopusFixtureRepositoryTmpl, OctopusTemplateFuncs)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	fixtureFile.Flush()
    62  
    63  	ret := map[string]bytes.Buffer{
    64  		"octopus": octopusWriter,
    65  		"mock":    mockWriter,
    66  		"fixture": fixtureWriter,
    67  	}
    68  
    69  	return ret, nil
    70  }
    71  
    72  //go:embed tmpl/octopus/fixturestore.tmpl
    73  var fixtureStoreTmpl string
    74  
    75  func GenerateOctopusFixtureStore(params FixturePkgData) (map[string]bytes.Buffer, *arerror.ErrGeneratorPhases) {
    76  	fixtureWriter := bytes.Buffer{}
    77  
    78  	file := bufio.NewWriter(&fixtureWriter)
    79  
    80  	templatePackage, err := template.New(TemplateName).Funcs(funcs).Funcs(OctopusTemplateFuncs).Parse(disclaimer + fixtureStoreTmpl)
    81  	if err != nil {
    82  		tmplLines, errgetline := getTmplErrorLine(strings.SplitAfter(disclaimer+fixtureStoreTmpl, "\n"), err.Error())
    83  		if errgetline != nil {
    84  			tmplLines = errgetline.Error()
    85  		}
    86  
    87  		return nil, &arerror.ErrGeneratorPhases{Backend: "fixture", Phase: "parse", TmplLines: tmplLines, Err: err}
    88  	}
    89  
    90  	err = templatePackage.Execute(file, params)
    91  	if err != nil {
    92  		tmplLines, errgetline := getTmplErrorLine(strings.SplitAfter(disclaimer+fixtureStoreTmpl, "\n"), err.Error())
    93  		if errgetline != nil {
    94  			tmplLines = errgetline.Error()
    95  		}
    96  
    97  		return nil, &arerror.ErrGeneratorPhases{Backend: "fixture", Phase: "execute", TmplLines: tmplLines, Err: err}
    98  	}
    99  
   100  	file.Flush()
   101  
   102  	ret := map[string]bytes.Buffer{
   103  		"fixture": fixtureWriter,
   104  	}
   105  
   106  	return ret, nil
   107  }
   108  
   109  var OctopusTemplateFuncs = template.FuncMap{
   110  	"packerParam": func(format octopus.Format) OctopusFormatParam {
   111  		ret, ex := OctopusFormatMapper[format]
   112  		if !ex {
   113  			log.Fatalf("packer for type `%s` not found", format)
   114  		}
   115  
   116  		return ret
   117  	},
   118  	"mutatorParam": func(mut string, format octopus.Format) OctopusMutatorParam {
   119  		ret, ex := OctopusMutatorMapper[mut]
   120  		if !ex {
   121  			log.Fatalf("mutator packer for type `%s` not found", format)
   122  		}
   123  
   124  		for _, availFormat := range ret.AvailableType {
   125  			if availFormat == format {
   126  				return ret
   127  			}
   128  		}
   129  
   130  		log.Fatalf("Mutator `%s` not available for type `%s`", mut, format)
   131  
   132  		return OctopusMutatorParam{}
   133  	},
   134  	"addImport": func(flds []ds.FieldDeclaration) (imports []string) {
   135  		var needMath, needStrconv bool
   136  
   137  		for _, fld := range flds {
   138  			if fld.Format == octopus.Float32 || fld.Format == octopus.Float64 {
   139  				needMath = true
   140  			}
   141  
   142  			if len(fld.Mutators) > 0 && fld.Format != "uint32" && fld.Format != "uint64" && fld.Format != "uint" {
   143  				needMath = true
   144  			}
   145  
   146  			if fld.PrimaryKey && fld.Format != "string" {
   147  				needStrconv = true
   148  			}
   149  		}
   150  
   151  		if needMath {
   152  			imports = append(imports, "math")
   153  		}
   154  
   155  		if needStrconv {
   156  			imports = append(imports, "strconv")
   157  		}
   158  
   159  		return
   160  	},
   161  	"trimPrefix": strings.TrimPrefix,
   162  	"hasPrefix":  strings.HasPrefix,
   163  }
   164  
   165  var ToLower = cases.Title(language.English)
   166  
   167  type OctopusFormatParam struct {
   168  	Name           string
   169  	Default        []byte
   170  	packFunc       string
   171  	unpackFunc     string
   172  	packConvFunc   string
   173  	UnpackConvFunc string
   174  	unpackType     string
   175  	len            uint
   176  	lenFunc        func(uint32) uint32
   177  	minValue       string
   178  	maxValue       string
   179  	convstr        string
   180  }
   181  
   182  func (p OctopusFormatParam) PackConvFunc(fieldname string) string {
   183  	if p.packConvFunc != "" {
   184  		return p.packConvFunc + "(" + fieldname + ")"
   185  	}
   186  
   187  	return fieldname
   188  }
   189  
   190  func (p OctopusFormatParam) UnpackFunc() string {
   191  	if p.unpackFunc != "" {
   192  		return p.unpackFunc
   193  	}
   194  
   195  	return "iproto.Unpack" + p.Name
   196  }
   197  
   198  func (p OctopusFormatParam) PackFunc() string {
   199  	if p.packFunc != "" {
   200  		return p.packFunc
   201  	}
   202  
   203  	return "iproto.Pack" + p.Name
   204  }
   205  
   206  func (p OctopusFormatParam) DefaultValue() string {
   207  	fname := "iproto.Pack" + p.Name
   208  	if p.packFunc != "" {
   209  		fname = p.packFunc
   210  	}
   211  
   212  	if strings.HasPrefix(p.Name, "Uint") {
   213  		return fname + "([]byte{}, 0, iproto.ModeDefault)"
   214  	} else if strings.HasPrefix(p.Name, "String") {
   215  		return fname + `([]byte{}, "", iproto.ModeDefault)`
   216  	} else {
   217  		return "can't detect type"
   218  	}
   219  }
   220  
   221  func (p OctopusFormatParam) UnpackType() string {
   222  	if p.unpackType != "" {
   223  		return p.unpackType
   224  	} else if p.packConvFunc != "" {
   225  		return p.packConvFunc
   226  	}
   227  
   228  	return ""
   229  }
   230  
   231  func (p OctopusFormatParam) Len(l uint32) uint {
   232  	if p.len != 0 {
   233  		return p.len
   234  	}
   235  
   236  	return uint(p.lenFunc(l))
   237  }
   238  
   239  func (p OctopusFormatParam) MinValue() string {
   240  	if p.minValue != "" {
   241  		return p.minValue
   242  	}
   243  
   244  	return "math.Min" + p.Name
   245  }
   246  
   247  func (p OctopusFormatParam) MaxValue() string {
   248  	if p.maxValue != "" {
   249  		return p.maxValue
   250  	}
   251  
   252  	return "math.Max" + p.Name
   253  }
   254  
   255  func (p OctopusFormatParam) ToString() []string {
   256  	return strings.SplitN(p.convstr, `%%`, 2)
   257  }
   258  
   259  func (p OctopusFormatParam) MutatorTypeConv() string {
   260  	if p.UnpackConvFunc != "" {
   261  		return p.UnpackConvFunc
   262  	}
   263  
   264  	return ToLower.String(p.Name)
   265  }
   266  
   267  type OctopusMutatorParam struct {
   268  	Name          string
   269  	AvailableType []octopus.Format
   270  	ArgType       string
   271  }
   272  
   273  var OctopusFormatMapper = map[octopus.Format]OctopusFormatParam{
   274  	octopus.Bool:    {Name: "Uint8", len: 2, convstr: "strconv.FormatBool(%%)", packConvFunc: "octopus.BoolToUint", UnpackConvFunc: "octopus.UintToBool", unpackType: "uint8"},
   275  	octopus.Uint8:   {Name: "Uint8", len: 2, convstr: "strconv.FormatUint(uint64(%%), 10)"},
   276  	octopus.Uint16:  {Name: "Uint16", len: 3, convstr: "strconv.FormatUint(uint64(%%), 10)"},
   277  	octopus.Uint32:  {Name: "Uint32", len: 5, convstr: "strconv.FormatUint(uint64(%%), 10)"},
   278  	octopus.Uint64:  {Name: "Uint64", len: 9, convstr: "strconv.FormatUint(%%, 10)"},
   279  	octopus.Uint:    {Name: "Uint32", len: 5, convstr: "strconv.FormatUint(uint64(%%), 10)", packConvFunc: "uint32", UnpackConvFunc: "uint"},
   280  	octopus.Int8:    {Name: "Uint8", len: 2, convstr: "strconv.FormatInt(int64(%%), 10)", packConvFunc: "uint8", UnpackConvFunc: "int8", minValue: "math.MinInt8", maxValue: "math.MaxInt8"},
   281  	octopus.Int16:   {Name: "Uint16", len: 3, convstr: "strconv.FormatInt(int64(%%), 10)", packConvFunc: "uint16", UnpackConvFunc: "int16", minValue: "math.MinInt16", maxValue: "math.MaxInt16"},
   282  	octopus.Int32:   {Name: "Uint32", len: 5, convstr: "strconv.FormatInt(int64(%%), 10)", packConvFunc: "uint32", UnpackConvFunc: "int32", minValue: "math.MinInt32", maxValue: "math.MaxInt32"},
   283  	octopus.Int64:   {Name: "Uint64", len: 9, convstr: "strconv.FormatInt(%%, 10)", packConvFunc: "uint64", UnpackConvFunc: "int64", minValue: "math.MinInt64", maxValue: "math.MaxInt64"},
   284  	octopus.Int:     {Name: "Uint32", len: 5, convstr: "strconv.FormatInt(int64(%%), 10)", packConvFunc: "uint32", UnpackConvFunc: "int", minValue: "math.MinInt32", maxValue: "math.MaxInt32"},
   285  	octopus.Float32: {Name: "Uint32", len: 5, convstr: "strconv.FormatFloat(%%, 32)", packConvFunc: "math.Float32bits", UnpackConvFunc: "math.Float32frombits", unpackType: "uint32", minValue: "math.MinFloat32", maxValue: "math.MaxFloat32"},
   286  	octopus.Float64: {Name: "Uint64", len: 9, convstr: "strconv.FormatFloat(%%, 64)", packConvFunc: "math.Float64bits", UnpackConvFunc: "math.Float64frombits", unpackType: "uint64", minValue: "math.MinFloat64", maxValue: "math.MaxFloat64"},
   287  	octopus.String:  {Name: "String", convstr: " %% ", lenFunc: octopus.ByteLen, packFunc: "octopus.PackString", unpackFunc: "octopus.UnpackString", minValue: "0", maxValue: "4096", unpackType: "string"}}
   288  
   289  var OctopusMutatorMapper = map[string]OctopusMutatorParam{
   290  	ds.IncMutator:      {Name: "Inc", AvailableType: octopus.NumericFormat},
   291  	ds.DecMutator:      {Name: "Dec", AvailableType: octopus.NumericFormat},
   292  	ds.AndMutator:      {Name: "And", AvailableType: octopus.UnsignedFormat},
   293  	ds.OrMutator:       {Name: "Or", AvailableType: octopus.UnsignedFormat},
   294  	ds.XorMutator:      {Name: "Xor", AvailableType: octopus.UnsignedFormat},
   295  	ds.ClearBitMutator: {Name: "ClearBit", AvailableType: octopus.UnsignedFormat},
   296  	ds.SetBitMutator:   {Name: "SetBit", AvailableType: octopus.UnsignedFormat},
   297  }