package main import ( "bytes" "flag" "fmt" "log" "os" "path" "strconv" "strings" "sync" "text/template" "quantex.com/qfixpt/quickfix/cmd/generate-fix/internal" "quantex.com/qfixpt/quickfix/datadictionary" ) var ( waitGroup sync.WaitGroup errors = make(chan error) ) func usage() { fmt.Fprintf(os.Stderr, "usage: %v [flags] ... \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) }