adding quickfix library
This commit is contained in:
194
quickfix/cmd/generate-fix/generate-fix.go
Normal file
194
quickfix/cmd/generate-fix/generate-fix.go
Normal file
@ -0,0 +1,194 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
|
||||
"github.com/quickfixgo/quickfix/cmd/generate-fix/internal"
|
||||
"github.com/quickfixgo/quickfix/datadictionary"
|
||||
)
|
||||
|
||||
var (
|
||||
waitGroup sync.WaitGroup
|
||||
errors = make(chan error)
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: %v [flags] <path to data dictionary> ... \n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func getPackageName(fixSpec *datadictionary.DataDictionary) string {
|
||||
pkg := strings.ToLower(fixSpec.FIXType) + strconv.Itoa(fixSpec.Major) + strconv.Itoa(fixSpec.Minor)
|
||||
|
||||
if fixSpec.ServicePack != 0 {
|
||||
pkg += "sp" + strconv.Itoa(fixSpec.ServicePack)
|
||||
}
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func getTransportPackageName(fixSpec *datadictionary.DataDictionary) string {
|
||||
if fixSpec.Major >= 5 {
|
||||
return "fixt11"
|
||||
}
|
||||
return getPackageName(fixSpec)
|
||||
}
|
||||
|
||||
type component struct {
|
||||
Package string
|
||||
FIXPackage string
|
||||
TransportPackage string
|
||||
FIXSpec *datadictionary.DataDictionary
|
||||
Name string
|
||||
*datadictionary.MessageDef
|
||||
}
|
||||
|
||||
func genHeader(pkg string, spec *datadictionary.DataDictionary) {
|
||||
c := component{
|
||||
Package: pkg,
|
||||
Name: "Header",
|
||||
MessageDef: spec.Header,
|
||||
FIXSpec: spec,
|
||||
}
|
||||
gen(internal.HeaderTemplate, path.Join(pkg, "header.generated.go"), c)
|
||||
}
|
||||
|
||||
func genTrailer(pkg string, spec *datadictionary.DataDictionary) {
|
||||
c := component{
|
||||
Package: pkg,
|
||||
Name: "Trailer",
|
||||
MessageDef: spec.Trailer,
|
||||
}
|
||||
gen(internal.TrailerTemplate, path.Join(pkg, "trailer.generated.go"), c)
|
||||
}
|
||||
|
||||
func genMessage(fixPkg string, spec *datadictionary.DataDictionary, msg *datadictionary.MessageDef) {
|
||||
pkgName := strings.ToLower(msg.Name)
|
||||
transportPkg := getTransportPackageName(spec)
|
||||
|
||||
c := component{
|
||||
Package: pkgName,
|
||||
FIXPackage: fixPkg,
|
||||
TransportPackage: transportPkg,
|
||||
FIXSpec: spec,
|
||||
Name: msg.Name,
|
||||
MessageDef: msg,
|
||||
}
|
||||
|
||||
gen(internal.MessageTemplate, path.Join(fixPkg, pkgName, msg.Name+".generated.go"), c)
|
||||
}
|
||||
|
||||
func genTags() {
|
||||
gen(internal.TagTemplate, "tag/tag_numbers.generated.go", internal.GlobalFieldTypes)
|
||||
}
|
||||
|
||||
func genFields() {
|
||||
gen(internal.FieldTemplate, "field/fields.generated.go", internal.GlobalFieldTypes)
|
||||
}
|
||||
|
||||
func genEnums() {
|
||||
gen(internal.EnumTemplate, "enum/enums.generated.go", internal.GlobalFieldTypes)
|
||||
}
|
||||
|
||||
func gen(t *template.Template, fileOut string, data interface{}) {
|
||||
defer waitGroup.Done()
|
||||
writer := new(bytes.Buffer)
|
||||
|
||||
if err := t.Execute(writer, data); err != nil {
|
||||
errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
if err := internal.WriteFile(fileOut, writer.String()); err != nil {
|
||||
errors <- err
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() < 1 {
|
||||
usage()
|
||||
}
|
||||
|
||||
args := flag.Args()
|
||||
if len(args) == 1 {
|
||||
dictpath := args[0]
|
||||
if strings.Contains(dictpath, "FIX50SP1") {
|
||||
args = append(args, strings.Replace(dictpath, "FIX50SP1", "FIXT11", -1))
|
||||
} else if strings.Contains(dictpath, "FIX50SP2") {
|
||||
args = append(args, strings.Replace(dictpath, "FIX50SP2", "FIXT11", -1))
|
||||
} else if strings.Contains(dictpath, "FIX50") {
|
||||
args = append(args, strings.Replace(dictpath, "FIX50", "FIXT11", -1))
|
||||
}
|
||||
}
|
||||
specs := []*datadictionary.DataDictionary{}
|
||||
|
||||
for _, dataDictPath := range args {
|
||||
spec, err := datadictionary.Parse(dataDictPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Error Parsing %v: %v", dataDictPath, err)
|
||||
}
|
||||
specs = append(specs, spec)
|
||||
}
|
||||
|
||||
internal.BuildGlobalFieldTypes(specs)
|
||||
|
||||
waitGroup.Add(1)
|
||||
go genTags()
|
||||
waitGroup.Add(1)
|
||||
go genFields()
|
||||
waitGroup.Add(1)
|
||||
go genEnums()
|
||||
|
||||
for _, spec := range specs {
|
||||
pkg := getPackageName(spec)
|
||||
|
||||
if fi, err := os.Stat(pkg); os.IsNotExist(err) {
|
||||
if err := os.Mkdir(pkg, os.ModePerm); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
log.Fatalf("%v/ is not a directory", pkg)
|
||||
}
|
||||
|
||||
switch pkg {
|
||||
// Uses fixt11 header/trailer.
|
||||
case "fix50", "fix50sp1", "fix50sp2":
|
||||
default:
|
||||
waitGroup.Add(1)
|
||||
go genHeader(pkg, spec)
|
||||
|
||||
waitGroup.Add(1)
|
||||
go genTrailer(pkg, spec)
|
||||
}
|
||||
|
||||
for _, m := range spec.Messages {
|
||||
waitGroup.Add(1)
|
||||
go genMessage(pkg, spec, m)
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
waitGroup.Wait()
|
||||
close(errors)
|
||||
}()
|
||||
|
||||
var h internal.ErrorHandler
|
||||
for err := range errors {
|
||||
h.Handle(err)
|
||||
}
|
||||
|
||||
os.Exit(h.ReturnCode)
|
||||
}
|
||||
88
quickfix/cmd/generate-fix/internal/generate.go
Normal file
88
quickfix/cmd/generate-fix/internal/generate.go
Normal file
@ -0,0 +1,88 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var (
|
||||
useFloat = flag.Bool("use-float", false, "By default, FIX float fields are represented as arbitrary-precision fixed-point decimal numbers. Set to 'true' to instead generate FIX float fields as float64 values.")
|
||||
useUDecimal = flag.Bool("use-udecimal", false, "By default, FIX uses the shopspring/decimal library for fixed-point decimal numbers. Set to 'true' to instead use the quagmt/udecimal library.")
|
||||
pkgRoot = flag.String("pkg-root", "github.com/quickfixgo", "Set a string here to provide a custom import path for generated packages.")
|
||||
tabWidth = 8
|
||||
printerMode = printer.UseSpaces | printer.TabIndent
|
||||
)
|
||||
|
||||
// ParseError indicates generated go source is invalid
|
||||
type ParseError struct {
|
||||
path string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e ParseError) Error() string {
|
||||
return fmt.Sprintf("Error parsing %v: %v", e.path, e.err)
|
||||
}
|
||||
|
||||
// ErrorHandler is a convenience struct for interpretting generation Errors
|
||||
type ErrorHandler struct {
|
||||
ReturnCode int
|
||||
}
|
||||
|
||||
// Handle interprets the generation error. Proceeds with setting returnCode, or panics depending on error type
|
||||
func (h *ErrorHandler) Handle(err error) {
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
//do nothing
|
||||
case ParseError:
|
||||
fmt.Println(err)
|
||||
h.ReturnCode = 1
|
||||
default:
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func write(filePath string, fset *token.FileSet, f *ast.File) error {
|
||||
if parentdir := path.Dir(filePath); parentdir != "." {
|
||||
if err := os.MkdirAll(parentdir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ast.SortImports(fset, f)
|
||||
err = (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(file, fset, f)
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteFile parses the generated code in fileOut and writes the code out to filePath.
|
||||
// Function performs some import clean up and gofmts the code before writing
|
||||
// Returns ParseError if the generated source is invalid but is written to filePath
|
||||
func WriteFile(filePath, fileOut string) error {
|
||||
fset := token.NewFileSet()
|
||||
f, pErr := parser.ParseFile(fset, "", fileOut, parser.ParseComments)
|
||||
if f == nil {
|
||||
return pErr
|
||||
}
|
||||
|
||||
//write out the file regardless of parseFile errors
|
||||
if err := write(filePath, fset, f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pErr != nil {
|
||||
return ParseError{path: filePath, err: pErr}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
74
quickfix/cmd/generate-fix/internal/globals.go
Normal file
74
quickfix/cmd/generate-fix/internal/globals.go
Normal file
@ -0,0 +1,74 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/quickfixgo/quickfix/datadictionary"
|
||||
)
|
||||
|
||||
type fieldTypeMap map[string]*datadictionary.FieldType
|
||||
|
||||
var (
|
||||
globalFieldTypesLookup fieldTypeMap
|
||||
GlobalFieldTypes []*datadictionary.FieldType
|
||||
)
|
||||
|
||||
// Sort fieldtypes by name.
|
||||
type byFieldName []*datadictionary.FieldType
|
||||
|
||||
func (n byFieldName) Len() int { return len(n) }
|
||||
func (n byFieldName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||
func (n byFieldName) Less(i, j int) bool { return n[i].Name() < n[j].Name() }
|
||||
|
||||
func getGlobalFieldType(f *datadictionary.FieldDef) (t *datadictionary.FieldType, err error) {
|
||||
var ok bool
|
||||
t, ok = globalFieldTypesLookup[f.Name()]
|
||||
if !ok {
|
||||
err = fmt.Errorf("Unknown global type for %v", f.Name())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func BuildGlobalFieldTypes(specs []*datadictionary.DataDictionary) {
|
||||
globalFieldTypesLookup = make(fieldTypeMap)
|
||||
for _, spec := range specs {
|
||||
for _, field := range spec.FieldTypeByTag {
|
||||
if oldField, ok := globalFieldTypesLookup[field.Name()]; ok {
|
||||
// Merge old enums with new.
|
||||
if len(oldField.Enums) > 0 && field.Enums == nil {
|
||||
field.Enums = make(map[string]datadictionary.Enum)
|
||||
}
|
||||
|
||||
for enumVal, enum := range oldField.Enums {
|
||||
if _, ok := field.Enums[enumVal]; !ok {
|
||||
// Verify an existing enum doesn't have the same description. Keep newer enum.
|
||||
okToKeepEnum := true
|
||||
for _, newEnum := range field.Enums {
|
||||
if newEnum.Description == enum.Description {
|
||||
okToKeepEnum = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if okToKeepEnum {
|
||||
field.Enums[enumVal] = enum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
globalFieldTypesLookup[field.Name()] = field
|
||||
}
|
||||
}
|
||||
|
||||
GlobalFieldTypes = make([]*datadictionary.FieldType, len(globalFieldTypesLookup))
|
||||
i := 0
|
||||
for _, fieldType := range globalFieldTypesLookup {
|
||||
GlobalFieldTypes[i] = fieldType
|
||||
i++
|
||||
}
|
||||
|
||||
sort.Sort(byFieldName(GlobalFieldTypes))
|
||||
}
|
||||
6
quickfix/cmd/generate-fix/internal/helpers.go
Normal file
6
quickfix/cmd/generate-fix/internal/helpers.go
Normal file
@ -0,0 +1,6 @@
|
||||
package internal
|
||||
|
||||
// getImportPathRoot returns the root path to use in import statements.
|
||||
func getImportPathRoot() string {
|
||||
return *pkgRoot
|
||||
}
|
||||
365
quickfix/cmd/generate-fix/internal/template_helpers.go
Normal file
365
quickfix/cmd/generate-fix/internal/template_helpers.go
Normal file
@ -0,0 +1,365 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/quickfixgo/quickfix/datadictionary"
|
||||
)
|
||||
|
||||
func isDecimalType(quickfixType string) bool {
|
||||
switch quickfixType {
|
||||
case "FIXDecimal", "FIXUDecimal":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func checkIfDecimalImportRequiredForFields(fTypes []*datadictionary.FieldType) (ok bool, err error) {
|
||||
var t string
|
||||
for _, fType := range fTypes {
|
||||
t, err = quickfixType(fType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if isDecimalType(t) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkIfTimeImportRequiredForFields(fTypes []*datadictionary.FieldType) (ok bool, err error) {
|
||||
var t string
|
||||
for _, fType := range fTypes {
|
||||
t, err = quickfixType(fType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var vt string
|
||||
if vt, err = quickfixValueType(t); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if vt == "time.Time" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkFieldDecimalRequired(f *datadictionary.FieldDef) (required bool, err error) {
|
||||
var globalType *datadictionary.FieldType
|
||||
if globalType, err = getGlobalFieldType(f); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var t string
|
||||
if t, err = quickfixType(globalType); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if isDecimalType(t) {
|
||||
required = true
|
||||
return
|
||||
}
|
||||
|
||||
for _, groupField := range f.Fields {
|
||||
if required, err = checkFieldDecimalRequired(groupField); required || err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkFieldTimeRequired(f *datadictionary.FieldDef) (required bool, err error) {
|
||||
var globalType *datadictionary.FieldType
|
||||
if globalType, err = getGlobalFieldType(f); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var t string
|
||||
if t, err = quickfixType(globalType); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var vt string
|
||||
if vt, err = quickfixValueType(t); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if vt == "time.Time" {
|
||||
required = true
|
||||
return
|
||||
}
|
||||
|
||||
for _, groupField := range f.Fields {
|
||||
if required, err = checkFieldTimeRequired(groupField); required || err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func collectStandardImports(m *datadictionary.MessageDef) (imports []string, err error) {
|
||||
var timeRequired bool
|
||||
for _, f := range m.Fields {
|
||||
if !timeRequired {
|
||||
if timeRequired, err = checkFieldTimeRequired(f); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if timeRequired {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if timeRequired {
|
||||
imports = append(imports, "time")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func collectExtraImports(m *datadictionary.MessageDef) (imports []string, err error) {
|
||||
var decimalRequired bool
|
||||
importPath := "github.com/shopspring/decimal"
|
||||
if *useUDecimal {
|
||||
importPath = "github.com/quagmt/udecimal"
|
||||
}
|
||||
for _, f := range m.Fields {
|
||||
if !decimalRequired {
|
||||
if decimalRequired, err = checkFieldDecimalRequired(f); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if decimalRequired {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if decimalRequired {
|
||||
imports = append(imports, importPath)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkIfEnumImportRequired(m *datadictionary.MessageDef) (required bool, err error) {
|
||||
for _, f := range m.Fields {
|
||||
required, err = checkFieldEnumRequired(f)
|
||||
if err != nil || required {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkFieldEnumRequired(f *datadictionary.FieldDef) (required bool, err error) {
|
||||
var globalType *datadictionary.FieldType
|
||||
if globalType, err = getGlobalFieldType(f); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if globalType.Enums != nil && 0 < len(globalType.Enums) {
|
||||
var t string
|
||||
if t, err = quickfixType(globalType); err != nil {
|
||||
return
|
||||
}
|
||||
if t != "FIXBoolean" {
|
||||
required = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, groupField := range f.Fields {
|
||||
if required, err = checkFieldEnumRequired(groupField); required || err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func quickfixValueType(quickfixType string) (goType string, err error) {
|
||||
switch quickfixType {
|
||||
case "FIXString":
|
||||
goType = "string"
|
||||
case "FIXBoolean":
|
||||
goType = "bool"
|
||||
case "FIXInt":
|
||||
goType = "int"
|
||||
case "FIXUTCTimestamp":
|
||||
goType = "time.Time"
|
||||
case "FIXFloat":
|
||||
goType = "float64"
|
||||
case "FIXDecimal":
|
||||
goType = "decimal.Decimal"
|
||||
case "FIXUDecimal":
|
||||
goType = "udecimal.Decimal"
|
||||
default:
|
||||
err = fmt.Errorf("Unknown QuickFIX Type: %v", quickfixType)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func quickfixType(field *datadictionary.FieldType) (quickfixType string, err error) {
|
||||
switch field.Type {
|
||||
case "MULTIPLESTRINGVALUE", "MULTIPLEVALUESTRING":
|
||||
fallthrough
|
||||
case "MULTIPLECHARVALUE":
|
||||
fallthrough
|
||||
case "CHAR":
|
||||
fallthrough
|
||||
case "CURRENCY":
|
||||
fallthrough
|
||||
case "DATA":
|
||||
fallthrough
|
||||
case "MONTHYEAR":
|
||||
fallthrough
|
||||
case "LOCALMKTTIME", "LOCALMKTDATE":
|
||||
fallthrough
|
||||
case "TIME":
|
||||
fallthrough
|
||||
case "DATE":
|
||||
fallthrough
|
||||
case "EXCHANGE":
|
||||
fallthrough
|
||||
case "LANGUAGE":
|
||||
fallthrough
|
||||
case "XMLDATA":
|
||||
fallthrough
|
||||
case "COUNTRY":
|
||||
fallthrough
|
||||
case "UTCTIMEONLY":
|
||||
fallthrough
|
||||
case "UTCDATE":
|
||||
fallthrough
|
||||
case "UTCDATEONLY":
|
||||
fallthrough
|
||||
case "TZTIMEONLY":
|
||||
fallthrough
|
||||
case "TZTIMESTAMP":
|
||||
fallthrough
|
||||
case "XID", "XIDREF":
|
||||
fallthrough
|
||||
case "STRING":
|
||||
quickfixType = "FIXString"
|
||||
|
||||
case "BOOLEAN":
|
||||
quickfixType = "FIXBoolean"
|
||||
|
||||
case "LENGTH":
|
||||
fallthrough
|
||||
case "DAYOFMONTH":
|
||||
fallthrough
|
||||
case "NUMINGROUP":
|
||||
fallthrough
|
||||
case "SEQNUM":
|
||||
fallthrough
|
||||
case "TAGNUM":
|
||||
fallthrough
|
||||
case "INT":
|
||||
quickfixType = "FIXInt"
|
||||
|
||||
case "UTCTIMESTAMP":
|
||||
quickfixType = "FIXUTCTimestamp"
|
||||
|
||||
case "QTY":
|
||||
fallthrough
|
||||
case "QUANTITY":
|
||||
fallthrough
|
||||
case "AMT":
|
||||
fallthrough
|
||||
case "PRICE":
|
||||
fallthrough
|
||||
case "PRICEOFFSET":
|
||||
fallthrough
|
||||
case "PERCENTAGE":
|
||||
fallthrough
|
||||
case "FLOAT":
|
||||
if *useFloat {
|
||||
quickfixType = "FIXFloat"
|
||||
} else if *useUDecimal {
|
||||
quickfixType = "FIXUDecimal"
|
||||
} else {
|
||||
quickfixType = "FIXDecimal"
|
||||
}
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("Unknown type '%v' for tag '%v'\n", field.Type, field.Tag())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func requiredFields(m *datadictionary.MessageDef) (required []*datadictionary.FieldDef) {
|
||||
for _, part := range m.RequiredParts() {
|
||||
if part.Required() {
|
||||
switch pType := part.(type) {
|
||||
case *datadictionary.FieldDef:
|
||||
if !pType.IsGroup() {
|
||||
required = append(required, pType)
|
||||
}
|
||||
case *datadictionary.Component:
|
||||
for _, f := range pType.RequiredFields() {
|
||||
if !f.IsGroup() {
|
||||
required = append(required, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func beginString(spec *datadictionary.DataDictionary) string {
|
||||
if spec.FIXType == "FIXT" || spec.Major == 5 {
|
||||
return "FIXT.1.1"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("FIX.%v.%v", spec.Major, spec.Minor)
|
||||
}
|
||||
|
||||
func routerBeginString(spec *datadictionary.DataDictionary) (routerBeginString string) {
|
||||
switch {
|
||||
case spec.FIXType == "FIXT":
|
||||
routerBeginString = "FIXT.1.1"
|
||||
case spec.Major != 5 && spec.ServicePack == 0:
|
||||
routerBeginString = fmt.Sprintf("FIX.%v.%v", spec.Major, spec.Minor)
|
||||
// ApplVerID enums.
|
||||
case spec.Major == 2:
|
||||
routerBeginString = "0"
|
||||
case spec.Major == 3:
|
||||
routerBeginString = "1"
|
||||
case spec.Major == 4 && spec.Minor == 0:
|
||||
routerBeginString = "2"
|
||||
case spec.Major == 4 && spec.Minor == 1:
|
||||
routerBeginString = "3"
|
||||
case spec.Major == 4 && spec.Minor == 2:
|
||||
routerBeginString = "4"
|
||||
case spec.Major == 4 && spec.Minor == 3:
|
||||
routerBeginString = "5"
|
||||
case spec.Major == 4 && spec.Minor == 4:
|
||||
routerBeginString = "6"
|
||||
case spec.Major == 5 && spec.Minor == 0 && spec.ServicePack == 0:
|
||||
routerBeginString = "7"
|
||||
case spec.Major == 5 && spec.Minor == 0 && spec.ServicePack == 1:
|
||||
routerBeginString = "8"
|
||||
case spec.Major == 5 && spec.Minor == 0 && spec.ServicePack == 2:
|
||||
routerBeginString = "9"
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
455
quickfix/cmd/generate-fix/internal/templates.go
Normal file
455
quickfix/cmd/generate-fix/internal/templates.go
Normal file
@ -0,0 +1,455 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var (
|
||||
HeaderTemplate *template.Template
|
||||
TrailerTemplate *template.Template
|
||||
MessageTemplate *template.Template
|
||||
TagTemplate *template.Template
|
||||
FieldTemplate *template.Template
|
||||
EnumTemplate *template.Template
|
||||
)
|
||||
|
||||
func init() {
|
||||
tmplFuncs := template.FuncMap{
|
||||
"toLower": strings.ToLower,
|
||||
"requiredFields": requiredFields,
|
||||
"beginString": beginString,
|
||||
"routerBeginString": routerBeginString,
|
||||
"importRootPath": getImportPathRoot,
|
||||
"quickfixType": quickfixType,
|
||||
"quickfixValueType": quickfixValueType,
|
||||
"getGlobalFieldType": getGlobalFieldType,
|
||||
"collectStandardImports": collectStandardImports,
|
||||
"collectExtraImports": collectExtraImports,
|
||||
"checkIfDecimalImportRequiredForFields": checkIfDecimalImportRequiredForFields,
|
||||
"decimalImport": func() string {
|
||||
if *useUDecimal {
|
||||
return "github.com/quagmt/udecimal"
|
||||
}
|
||||
return "github.com/shopspring/decimal"
|
||||
},
|
||||
"checkIfTimeImportRequiredForFields": checkIfTimeImportRequiredForFields,
|
||||
"checkIfEnumImportRequired": checkIfEnumImportRequired,
|
||||
}
|
||||
|
||||
baseTemplate := template.Must(template.New("Base").Funcs(tmplFuncs).Parse(`
|
||||
{{ define "receiver" }}RECEIVER{{ end }}
|
||||
|
||||
{{ define "fieldsetter" -}}
|
||||
{{- $field_type := getGlobalFieldType . -}}
|
||||
{{- $qfix_type := quickfixType $field_type -}}
|
||||
{{- if and ($field_type.Enums) (ne $qfix_type "FIXBoolean") -}}
|
||||
Set{{ .Name }}(v enum.{{ .Name }}) {
|
||||
{{ template "receiver" }}.Set(field.New{{ .Name }}(v))
|
||||
}
|
||||
{{- else if eq $qfix_type "FIXDecimal" -}}
|
||||
Set{{ .Name }}(value decimal.Decimal, scale int32) {
|
||||
{{ template "receiver" }}.Set(field.New{{ .Name }}(value, scale))
|
||||
}
|
||||
{{- else if eq $qfix_type "FIXUDecimal" -}}
|
||||
Set{{ .Name }}(value udecimal.Decimal, scale uint8) {
|
||||
{{ template "receiver" }}.Set(field.New{{ .Name }}(value, scale))
|
||||
}
|
||||
{{- else -}}
|
||||
Set{{ .Name }}(v {{ quickfixValueType $qfix_type }}) {
|
||||
{{ template "receiver" }}.Set(field.New{{ .Name }}(v))
|
||||
}
|
||||
{{- end }}{{ end }}
|
||||
|
||||
{{ define "groupsetter" -}}
|
||||
Set{{ .Name }}(f {{ .Name }}RepeatingGroup){
|
||||
{{ template "receiver" }}.SetGroup(f)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "setters" }}
|
||||
{{ range .Fields }}
|
||||
// Set{{ .Name }} sets {{ .Name }}, Tag {{ .Tag }}.
|
||||
func ({{ template "receiver" }} {{ $.Name }}) {{ if .IsGroup }}{{ template "groupsetter" . }}{{ else }}{{ template "fieldsetter" . }}{{ end }}
|
||||
{{ end }}{{ end }}
|
||||
|
||||
{{ define "fieldgetter" -}}
|
||||
Get{{ .Name }}() (f field.{{ .Name }}Field, err quickfix.MessageRejectError) {
|
||||
err = {{ template "receiver" }}.Get(&f)
|
||||
return
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "fieldvaluegetter" -}}
|
||||
{{- $ft := getGlobalFieldType . -}}
|
||||
{{- $bt := quickfixType $ft -}}
|
||||
{{- if and $ft.Enums (ne $bt "FIXBoolean") -}}
|
||||
Get{{ .Name }}() (v enum.{{ .Name }}, err quickfix.MessageRejectError) {
|
||||
{{- else if eq $bt "FIXDecimal" -}}
|
||||
Get{{ .Name }}() (v decimal.Decimal, err quickfix.MessageRejectError) {
|
||||
{{- else if eq $bt "FIXUDecimal" -}}
|
||||
Get{{ .Name }}() (v udecimal.Decimal, err quickfix.MessageRejectError) {
|
||||
{{- else -}}
|
||||
Get{{ .Name }}() (v {{ quickfixValueType $bt }}, err quickfix.MessageRejectError) {
|
||||
{{- end }}
|
||||
var f field.{{ .Name }}Field
|
||||
if err = {{ template "receiver" }}.Get(&f); err == nil {
|
||||
v = f.Value()
|
||||
}
|
||||
return
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "groupgetter" -}}
|
||||
Get{{ .Name }}() ({{ .Name }}RepeatingGroup, quickfix.MessageRejectError) {
|
||||
f := New{{ .Name }}RepeatingGroup()
|
||||
err := {{ template "receiver" }}.GetGroup(f)
|
||||
return f, err
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{ define "getters" }}
|
||||
{{ range .Fields }}
|
||||
// Get{{ .Name }} gets {{ .Name }}, Tag {{ .Tag }}.
|
||||
func ({{ template "receiver" }} {{ $.Name }}) {{if .IsGroup}}{{ template "groupgetter" . }}{{ else }}{{ template "fieldvaluegetter" .}}{{ end }}
|
||||
{{ end }}{{ end }}
|
||||
|
||||
{{ define "hasers" }}
|
||||
{{range .Fields}}
|
||||
// Has{{ .Name}} returns true if {{ .Name}} is present, Tag {{ .Tag}}.
|
||||
func ({{ template "receiver" }} {{ $.Name }}) Has{{ .Name}}() bool {
|
||||
return {{ template "receiver" }}.Has(tag.{{ .Name}})
|
||||
}
|
||||
{{end}}{{ end }}
|
||||
|
||||
{{ define "group_template" }}
|
||||
quickfix.GroupTemplate{
|
||||
{{- range $index, $field := . }}
|
||||
{{- if $field.IsGroup }}
|
||||
New{{ $field.Name }}RepeatingGroup(),
|
||||
{{- else}}
|
||||
quickfix.GroupElement(tag.{{$field.Name}}),
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "field_args" }}
|
||||
{{- range $index, $field := . }}{{if $index}},{{end}}{{toLower $field.Name}} field.{{ .Name }}Field{{ end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "groups" }}
|
||||
{{ range .Fields }}
|
||||
{{ if .IsGroup }}
|
||||
// {{ .Name }} is a repeating group element, Tag {{ .Tag }}.
|
||||
type {{ .Name }} struct {
|
||||
*quickfix.Group
|
||||
}
|
||||
|
||||
{{ template "setters" .}}
|
||||
{{ template "getters" . }}
|
||||
{{ template "hasers" . }}
|
||||
{{ template "groups" . }}
|
||||
|
||||
// {{ .Name }}RepeatingGroup is a repeating group, Tag {{ .Tag }}.
|
||||
type {{ .Name }}RepeatingGroup struct {
|
||||
*quickfix.RepeatingGroup
|
||||
}
|
||||
|
||||
// New{{ .Name }}RepeatingGroup returns an initialized, {{ .Name }}RepeatingGroup.
|
||||
func New{{ .Name }}RepeatingGroup() {{ .Name }}RepeatingGroup {
|
||||
return {{ .Name }}RepeatingGroup{
|
||||
quickfix.NewRepeatingGroup(
|
||||
tag.{{ .Name }},
|
||||
{{- template "group_template" .Fields }},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// Add create and append a new {{ .Name }} to this group.
|
||||
func ({{ template "receiver" }} {{ .Name }}RepeatingGroup) Add() {{ .Name }} {
|
||||
g := {{ template "receiver" }}.RepeatingGroup.Add()
|
||||
return {{ .Name }}{g}
|
||||
}
|
||||
|
||||
// Get returns the ith {{ .Name }} in the {{ .Name }}RepeatinGroup.
|
||||
func ({{ template "receiver" }} {{ .Name}}RepeatingGroup) Get(i int) {{ .Name }} {
|
||||
return {{ .Name }}{ {{ template "receiver" }}.RepeatingGroup.Get(i) }
|
||||
}
|
||||
|
||||
{{ end }}{{ end }}{{ end }}
|
||||
`))
|
||||
|
||||
HeaderTemplate = template.Must(template.Must(baseTemplate.Clone()).Parse(`
|
||||
{{ define "receiver" }}h{{ end }}
|
||||
// Code generated by quickfix. DO NOT EDIT.
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
{{- if collectStandardImports .MessageDef }}
|
||||
{{- range collectStandardImports .MessageDef }}
|
||||
"{{ . }}"
|
||||
{{- end }}{{ "\n" }}
|
||||
{{- end }}
|
||||
{{- if collectExtraImports .MessageDef }}
|
||||
{{- range collectExtraImports .MessageDef }}
|
||||
"{{ . }}"
|
||||
{{- end }}{{ "\n" }}
|
||||
{{- end }}
|
||||
"github.com/quickfixgo/quickfix"
|
||||
{{- if checkIfEnumImportRequired .MessageDef}}
|
||||
"{{ importRootPath }}/enum"
|
||||
{{- end }}
|
||||
"{{ importRootPath }}/field"
|
||||
"{{ importRootPath }}/tag"
|
||||
)
|
||||
|
||||
// Header is the {{ .Package }} Header type.
|
||||
type Header struct {
|
||||
*quickfix.Header
|
||||
}
|
||||
|
||||
// NewHeader returns a new, initialized Header instance.
|
||||
func NewHeader(header *quickfix.Header) (h Header) {
|
||||
h.Header = header
|
||||
h.SetBeginString("{{ beginString .FIXSpec }}")
|
||||
return
|
||||
}
|
||||
|
||||
{{ template "setters" .}}
|
||||
{{ template "getters" . }}
|
||||
{{ template "hasers" . }}
|
||||
{{ template "groups" . }}
|
||||
`))
|
||||
|
||||
TrailerTemplate = template.Must(template.Must(baseTemplate.Clone()).Parse(`
|
||||
{{ define "receiver" }}t{{ end }}
|
||||
// Code generated by quickfix. DO NOT EDIT.
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
{{- if collectStandardImports .MessageDef }}
|
||||
{{- range collectStandardImports .MessageDef }}
|
||||
"{{ . }}"
|
||||
{{- end }}{{ "\n" }}
|
||||
{{- end }}
|
||||
{{- if collectExtraImports .MessageDef }}
|
||||
{{- range collectExtraImports .MessageDef }}
|
||||
"{{ . }}"
|
||||
{{- end }}{{ "\n" }}
|
||||
{{- end }}
|
||||
"github.com/quickfixgo/quickfix"
|
||||
{{- if checkIfEnumImportRequired .MessageDef}}
|
||||
"{{ importRootPath }}/enum"
|
||||
{{- end }}
|
||||
"{{ importRootPath }}/field"
|
||||
"{{ importRootPath }}/tag"
|
||||
)
|
||||
|
||||
// Trailer is the {{ .Package }} Trailer type.
|
||||
type Trailer struct {
|
||||
*quickfix.Trailer
|
||||
}
|
||||
|
||||
{{ template "setters" .}}
|
||||
{{ template "getters" . }}
|
||||
{{ template "hasers" . }}
|
||||
{{ template "groups" . }}
|
||||
`))
|
||||
|
||||
MessageTemplate = template.Must(baseTemplate.Parse(`
|
||||
{{ define "receiver" }}m{{ end }}
|
||||
// Code generated by quickfix. DO NOT EDIT.
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
{{- if collectStandardImports .MessageDef }}
|
||||
{{- range collectStandardImports .MessageDef }}
|
||||
"{{ . }}"
|
||||
{{- end }}{{ "\n" }}
|
||||
{{- end }}
|
||||
{{- if collectExtraImports .MessageDef }}
|
||||
{{- range collectExtraImports .MessageDef }}
|
||||
"{{ . }}"
|
||||
{{- end }}{{ "\n" }}
|
||||
{{- end }}
|
||||
"github.com/quickfixgo/quickfix"
|
||||
{{- if checkIfEnumImportRequired .MessageDef}}
|
||||
"{{ importRootPath }}/enum"
|
||||
{{- end }}
|
||||
"{{ importRootPath }}/field"
|
||||
"{{ importRootPath }}/{{ .TransportPackage }}"
|
||||
"{{ importRootPath }}/tag"
|
||||
)
|
||||
|
||||
// {{ .Name }} is the {{ .FIXPackage }} {{ .Name }} type, MsgType = {{ .MsgType }}.
|
||||
type {{ .Name }} struct {
|
||||
{{ .TransportPackage }}.Header
|
||||
*quickfix.Body
|
||||
{{ .TransportPackage }}.Trailer
|
||||
Message *quickfix.Message
|
||||
}
|
||||
|
||||
// FromMessage creates a {{ .Name }} from a quickfix.Message instance.
|
||||
func FromMessage(m *quickfix.Message) {{ .Name }} {
|
||||
return {{ .Name }}{
|
||||
Header: {{ .TransportPackage}}.Header{Header: &m.Header},
|
||||
Body: &m.Body,
|
||||
Trailer: {{ .TransportPackage}}.Trailer{Trailer: &m.Trailer},
|
||||
Message: m,
|
||||
}
|
||||
}
|
||||
|
||||
// ToMessage returns a quickfix.Message instance.
|
||||
func (m {{ .Name }}) ToMessage() *quickfix.Message {
|
||||
return m.Message
|
||||
}
|
||||
|
||||
{{ $required_fields := requiredFields .MessageDef -}}
|
||||
// New returns a {{ .Name }} initialized with the required fields for {{ .Name }}.
|
||||
func New({{template "field_args" $required_fields }}) (m {{ .Name }}) {
|
||||
m.Message = quickfix.NewMessage()
|
||||
m.Header = {{ .TransportPackage }}.NewHeader(&m.Message.Header)
|
||||
m.Body = &m.Message.Body
|
||||
m.Trailer.Trailer = &m.Message.Trailer
|
||||
|
||||
m.Header.Set(field.NewMsgType("{{ .MessageDef.MsgType }}"))
|
||||
{{- range $required_fields }}
|
||||
m.Set({{ toLower .Name }})
|
||||
{{- end }}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// A RouteOut is the callback type that should be implemented for routing Message.
|
||||
type RouteOut func(msg {{ .Name }}, sessionID quickfix.SessionID) quickfix.MessageRejectError
|
||||
|
||||
// Route returns the beginstring, message type, and MessageRoute for this Message type.
|
||||
func Route(router RouteOut) (string, string, quickfix.MessageRoute) {
|
||||
r:=func(msg *quickfix.Message, sessionID quickfix.SessionID) quickfix.MessageRejectError {
|
||||
return router(FromMessage(msg), sessionID)
|
||||
}
|
||||
return "{{ routerBeginString .FIXSpec }}", "{{ .MessageDef.MsgType }}", r
|
||||
}
|
||||
|
||||
{{ template "setters" . }}
|
||||
{{ template "getters" . }}
|
||||
{{ template "hasers" . }}
|
||||
{{ template "groups" . }}
|
||||
`))
|
||||
|
||||
TagTemplate = template.Must(template.New("Tag").Parse(`
|
||||
// Code generated by quickfix. DO NOT EDIT.
|
||||
package tag
|
||||
import "github.com/quickfixgo/quickfix"
|
||||
|
||||
const (
|
||||
{{- range .}}
|
||||
{{ .Name }} quickfix.Tag = {{ .Tag }}
|
||||
{{- end }}
|
||||
)
|
||||
`))
|
||||
|
||||
FieldTemplate = template.Must(template.New("Field").Funcs(tmplFuncs).Parse(`
|
||||
// Code generated by quickfix. DO NOT EDIT.
|
||||
package field
|
||||
import (
|
||||
{{ if checkIfTimeImportRequiredForFields . }}"time"{{ end }}
|
||||
|
||||
{{ if checkIfDecimalImportRequiredForFields . }}"{{ decimalImport }}"{{ end }}
|
||||
|
||||
"github.com/quickfixgo/quickfix"
|
||||
"{{ importRootPath }}/enum"
|
||||
"{{ importRootPath }}/tag"
|
||||
)
|
||||
|
||||
{{ range . }}
|
||||
{{- $base_type := quickfixType . -}}
|
||||
|
||||
{{ if and .Enums (ne $base_type "FIXBoolean") }}
|
||||
// {{ .Name }}Field is a enum.{{ .Name }} field.
|
||||
type {{ .Name }}Field struct { quickfix.FIXString }
|
||||
{{ else }}
|
||||
// {{ .Name }}Field is a {{ .Type }} field.
|
||||
type {{ .Name }}Field struct { quickfix.{{ $base_type }} }
|
||||
{{ end }}
|
||||
|
||||
// Tag returns tag.{{ .Name }} ({{ .Tag }}).
|
||||
func (f {{ .Name }}Field) Tag() quickfix.Tag { return tag.{{ .Name }} }
|
||||
|
||||
{{ if eq $base_type "FIXUTCTimestamp" }}
|
||||
// New{{ .Name }} returns a new {{ .Name }}Field initialized with val.
|
||||
func New{{ .Name }}(val time.Time) {{ .Name }}Field {
|
||||
return New{{ .Name }}WithPrecision(val, quickfix.Millis)
|
||||
}
|
||||
|
||||
// New{{ .Name }}NoMillis returns a new {{ .Name }}Field initialized with val without millisecs.
|
||||
func New{{ .Name }}NoMillis(val time.Time) {{ .Name }}Field {
|
||||
return New{{ .Name }}WithPrecision(val, quickfix.Seconds)
|
||||
}
|
||||
|
||||
// New{{ .Name }}WithPrecision returns a new {{ .Name }}Field initialized with val of specified precision.
|
||||
func New{{ .Name }}WithPrecision(val time.Time, precision quickfix.TimestampPrecision) {{ .Name }}Field {
|
||||
return {{ .Name }}Field{ quickfix.FIXUTCTimestamp{ Time: val, Precision: precision } }
|
||||
}
|
||||
|
||||
{{ else if and .Enums (ne $base_type "FIXBoolean") }}
|
||||
func New{{ .Name }}(val enum.{{ .Name }}) {{ .Name }}Field {
|
||||
return {{ .Name }}Field{ quickfix.FIXString(val) }
|
||||
}
|
||||
{{ else if eq $base_type "FIXDecimal" }}
|
||||
// New{{ .Name }} returns a new {{ .Name }}Field initialized with val and scale.
|
||||
func New{{ .Name }}(val decimal.Decimal, scale int32) {{ .Name }}Field {
|
||||
return {{ .Name }}Field{ quickfix.FIXDecimal{ Decimal: val, Scale: scale} }
|
||||
}
|
||||
{{ else if eq $base_type "FIXUDecimal" }}
|
||||
// New{{ .Name }} returns a new {{ .Name }}Field initialized with val and scale.
|
||||
func New{{ .Name }}(val udecimal.Decimal, scale uint8) {{ .Name }}Field {
|
||||
return {{ .Name }}Field{ quickfix.FIXUDecimal{ Decimal: val, Scale: scale} }
|
||||
}
|
||||
{{ else }}
|
||||
// New{{ .Name }} returns a new {{ .Name }}Field initialized with val.
|
||||
func New{{ .Name }}(val {{ quickfixValueType $base_type }}) {{ .Name }}Field {
|
||||
return {{ .Name }}Field{ quickfix.{{ $base_type }}(val) }
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if and .Enums (ne $base_type "FIXBoolean") }}
|
||||
func (f {{ .Name }}Field) Value() enum.{{ .Name }} { return enum.{{ .Name }}(f.String()) }
|
||||
{{ else if eq $base_type "FIXDecimal" }}
|
||||
func (f {{ .Name }}Field) Value() (val decimal.Decimal) { return f.Decimal }
|
||||
{{ else if eq $base_type "FIXUDecimal" }}
|
||||
func (f {{ .Name }}Field) Value() (val udecimal.Decimal) { return f.Decimal }
|
||||
{{ else }}
|
||||
func (f {{ .Name }}Field) Value() ({{ quickfixValueType $base_type }}) {
|
||||
{{- if eq $base_type "FIXString" -}}
|
||||
return f.String() }
|
||||
{{- else if eq $base_type "FIXBoolean" -}}
|
||||
return f.Bool() }
|
||||
{{- else if eq $base_type "FIXInt" -}}
|
||||
return f.Int() }
|
||||
{{- else if eq $base_type "FIXUTCTimestamp" -}}
|
||||
return f.Time }
|
||||
{{- else if eq $base_type "FIXFloat" -}}
|
||||
return f.Float() }
|
||||
{{- else -}}
|
||||
TEMPLATE ERROR: Value() for {{ $base_type }}
|
||||
{{ end }}{{ end }}{{ end }}
|
||||
`))
|
||||
|
||||
EnumTemplate = template.Must(template.New("Enum").Parse(`
|
||||
// Code generated by quickfix. DO NOT EDIT.
|
||||
package enum
|
||||
{{ range $ft := . }}
|
||||
{{ if $ft.Enums }}
|
||||
// {{ $ft.Name }} field enumeration values.
|
||||
type {{ $ft.Name }} string
|
||||
const(
|
||||
{{ range $ft.Enums }}
|
||||
{{ $ft.Name }}_{{ .Description }} {{ $ft.Name }} = "{{ .Value }}"
|
||||
{{- end }}
|
||||
)
|
||||
{{ end }}{{ end }}
|
||||
`))
|
||||
}
|
||||
Reference in New Issue
Block a user