package printer import ( "bytes" "fmt" "io" "io/ioutil" "strings" "github.com/tinylib/msgp/gen" "github.com/tinylib/msgp/parse" "github.com/ttacon/chalk" "golang.org/x/tools/imports" ) func infof(s string, v ...interface{}) { fmt.Printf(chalk.Magenta.Color(s), v...) } // PrintFile prints the methods for the provided list // of elements to the given file name and canonical // package path. func PrintFile(file string, f *parse.FileSet, mode gen.Method) error { out, tests, err := generate(f, mode) if err != nil { return err } // we'll run goimports on the main file // in another goroutine, and run it here // for the test file. empirically, this // takes about the same amount of time as // doing them in serial when GOMAXPROCS=1, // and faster otherwise. res := goformat(file, out.Bytes()) if tests != nil { testfile := strings.TrimSuffix(file, ".go") + "_test.go" err = format(testfile, tests.Bytes()) if err != nil { return err } infof(">>> Wrote and formatted \"%s\"\n", testfile) } err = <-res if err != nil { return err } return nil } func format(file string, data []byte) error { out, err := imports.Process(file, data, nil) if err != nil { return err } return ioutil.WriteFile(file, out, 0600) } func goformat(file string, data []byte) <-chan error { out := make(chan error, 1) go func(file string, data []byte, end chan error) { end <- format(file, data) infof(">>> Wrote and formatted \"%s\"\n", file) }(file, data, out) return out } func dedupImports(imp []string) []string { m := make(map[string]struct{}) for i := range imp { m[imp[i]] = struct{}{} } r := []string{} for k := range m { r = append(r, k) } return r } func generate(f *parse.FileSet, mode gen.Method) (*bytes.Buffer, *bytes.Buffer, error) { outbuf := bytes.NewBuffer(make([]byte, 0, 4096)) writePkgHeader(outbuf, f.Package) myImports := []string{"github.com/tinylib/msgp/msgp"} for _, imp := range f.Imports { if imp.Name != nil { // have an alias, include it. myImports = append(myImports, imp.Name.Name+` `+imp.Path.Value) } else { myImports = append(myImports, imp.Path.Value) } } dedup := dedupImports(myImports) writeImportHeader(outbuf, dedup...) var testbuf *bytes.Buffer var testwr io.Writer if mode&gen.Test == gen.Test { testbuf = bytes.NewBuffer(make([]byte, 0, 4096)) writePkgHeader(testbuf, f.Package) if mode&(gen.Encode|gen.Decode) != 0 { writeImportHeader(testbuf, "bytes", "github.com/tinylib/msgp/msgp", "testing") } else { writeImportHeader(testbuf, "github.com/tinylib/msgp/msgp", "testing") } testwr = testbuf } return outbuf, testbuf, f.PrintTo(gen.NewPrinter(mode, outbuf, testwr)) } func writePkgHeader(b *bytes.Buffer, name string) { b.WriteString("package ") b.WriteString(name) b.WriteByte('\n') b.WriteString("// NOTE: THIS FILE WAS PRODUCED BY THE\n// MSGP CODE GENERATION TOOL (github.com/tinylib/msgp)\n// DO NOT EDIT\n\n") } func writeImportHeader(b *bytes.Buffer, imports ...string) { b.WriteString("import (\n") for _, im := range imports { if im[len(im)-1] == '"' { // support aliased imports fmt.Fprintf(b, "\t%s\n", im) } else { fmt.Fprintf(b, "\t%q\n", im) } } b.WriteString(")\n\n") }