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 }