OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package main |
| 6 |
| 7 import ( |
| 8 "bufio" |
| 9 "flag" |
| 10 "fmt" |
| 11 "io" |
| 12 "os" |
| 13 "sort" |
| 14 "strings" |
| 15 "text/template" |
| 16 |
| 17 "github.com/luci/luci-go/common/errors" |
| 18 "github.com/luci/luci-go/common/flag/stringsetflag" |
| 19 ) |
| 20 |
| 21 type app struct { |
| 22 out io.Writer |
| 23 |
| 24 packageName string |
| 25 typeNames stringsetflag.Flag |
| 26 outFile string |
| 27 } |
| 28 |
| 29 const help = `Usage of %s: |
| 30 |
| 31 %s is a go-generator program that generates PropertyConverter implementations |
| 32 for types produced by protoc. It can be used in a go generation file like: |
| 33 |
| 34 //go:generate <protoc command> |
| 35 //go:generate proto-gae -type MessageType -type OtherMessageType |
| 36 |
| 37 This will produce a new file which implements the ToProperty and FromProperty |
| 38 methods for the named types. |
| 39 |
| 40 Options: |
| 41 ` |
| 42 |
| 43 func (a *app) parseArgs(fs *flag.FlagSet, args []string) error { |
| 44 fs.SetOutput(a.out) |
| 45 fs.Usage = func() { |
| 46 fmt.Fprintf(a.out, help, args[0], args[0]) |
| 47 fs.PrintDefaults() |
| 48 } |
| 49 |
| 50 fs.Var(&a.typeNames, "type", |
| 51 "A generated proto.Message type to generate stubs for (required,
repeatable)") |
| 52 fs.StringVar(&a.outFile, "out", "proto_gae.gen.go", |
| 53 "The name of the output file") |
| 54 |
| 55 if err := fs.Parse(args[1:]); err != nil { |
| 56 return err |
| 57 } |
| 58 fail := errors.MultiError(nil) |
| 59 if a.typeNames.Data == nil || a.typeNames.Data.Len() == 0 { |
| 60 fail = append(fail, errors.New("must specify one or more -type")
) |
| 61 } |
| 62 if !strings.HasSuffix(a.outFile, ".go") { |
| 63 fail = append(fail, errors.New("-output must end with '.go'")) |
| 64 } |
| 65 if len(fail) > 0 { |
| 66 for _, e := range fail { |
| 67 fmt.Fprintln(a.out, "error:", e) |
| 68 } |
| 69 fmt.Fprintln(a.out) |
| 70 fs.Usage() |
| 71 return fail |
| 72 } |
| 73 return nil |
| 74 } |
| 75 |
| 76 var tmpl = template.Must( |
| 77 template.New("main").Parse(`// AUTOGENERATED: Do not edit |
| 78 |
| 79 package {{index . "package"}} |
| 80 |
| 81 import ( |
| 82 "github.com/golang/protobuf/proto" |
| 83 |
| 84 "github.com/luci/gae/service/datastore" |
| 85 ){{range index . "types"}} |
| 86 |
| 87 var _ datastore.PropertyConverter = (*{{.}})(nil) |
| 88 |
| 89 // ToProperty implements datastore.PropertyConverter. It causes an embedded |
| 90 // '{{.}}' to serialize to an unindexed '[]byte' when used with the |
| 91 // "github.com/luci/gae" library. |
| 92 func (p *{{.}}) ToProperty() (prop datastore.Property, err error) { |
| 93 data, err := proto.Marshal(p) |
| 94 if err == nil { |
| 95 prop.SetValue(data, datastore.NoIndex) |
| 96 } |
| 97 return |
| 98 } |
| 99 |
| 100 // FromProperty implements datastore.PropertyConverter. It parses a '[]byte' |
| 101 // into an embedded '{{.}}' when used with the "github.com/luci/gae" library. |
| 102 func (p *{{.}}) FromProperty(prop datastore.Property) error { |
| 103 data, err := prop.Project(datastore.PTBytes) |
| 104 if err != nil { |
| 105 return err |
| 106 } |
| 107 return proto.Unmarshal(data.([]byte), p) |
| 108 }{{end}} |
| 109 `)) |
| 110 |
| 111 func (a *app) writeTo(w io.Writer) error { |
| 112 typeNames := a.typeNames.Data.ToSlice() |
| 113 sort.Strings(typeNames) |
| 114 |
| 115 return tmpl.Execute(w, map[string]interface{}{ |
| 116 "package": a.packageName, |
| 117 "types": typeNames, |
| 118 }) |
| 119 } |
| 120 |
| 121 func (a *app) main() { |
| 122 if err := a.parseArgs(flag.NewFlagSet(os.Args[0], flag.ContinueOnError),
os.Args); err != nil { |
| 123 os.Exit(1) |
| 124 } |
| 125 ofile, err := os.Create(a.outFile) |
| 126 if err != nil { |
| 127 fmt.Fprintf(a.out, "error: %s", err) |
| 128 os.Exit(2) |
| 129 } |
| 130 closeFn := func(delete bool) { |
| 131 if ofile != nil { |
| 132 if err := ofile.Close(); err != nil { |
| 133 fmt.Fprintf(a.out, "error while closing file: %s
", err) |
| 134 } |
| 135 if delete { |
| 136 if err := os.Remove(a.outFile); err != nil { |
| 137 fmt.Fprintf(a.out, "failed to remove fil
e!") |
| 138 } |
| 139 } |
| 140 } |
| 141 ofile = nil |
| 142 } |
| 143 defer closeFn(false) |
| 144 buf := bufio.NewWriter(ofile) |
| 145 err = a.writeTo(buf) |
| 146 if err != nil { |
| 147 fmt.Fprintf(a.out, "error while writing: %s", err) |
| 148 closeFn(true) |
| 149 os.Exit(3) |
| 150 } |
| 151 if err := buf.Flush(); err != nil { |
| 152 fmt.Fprintf(a.out, "error while writing: %s", err) |
| 153 closeFn(true) |
| 154 os.Exit(4) |
| 155 } |
| 156 } |
| 157 |
| 158 func main() { |
| 159 (&app{out: os.Stderr, packageName: os.Getenv("GOPACKAGE")}).main() |
| 160 } |
OLD | NEW |