Chromium Code Reviews| 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 "flag" | |
| 9 "fmt" | |
| 10 "io" | |
| 11 "os" | |
| 12 "sort" | |
| 13 "strings" | |
| 14 "text/template" | |
| 15 | |
| 16 "github.com/luci/luci-go/common/errors" | |
| 17 "github.com/luci/luci-go/common/flag/stringsetflag" | |
| 18 ) | |
| 19 | |
| 20 type app struct { | |
| 21 out io.Writer | |
| 22 | |
| 23 packageName string | |
| 24 typeNames stringsetflag.Flag | |
| 25 outFile string | |
| 26 } | |
| 27 | |
| 28 const help = `Usage of %s: | |
| 29 | |
| 30 %s is a go-generator program that generates PropertyConverter implementations | |
| 31 for types produced by protoc. It can be used in a go generation file like: | |
| 32 | |
| 33 //go:generate <protoc command> | |
| 34 //go:generate proto-gae -type MessageType -type OtherMessageType | |
| 35 | |
| 36 This will produce a new file which implements the ToProperty and FromProperty | |
| 37 methods for the named types. | |
| 38 | |
| 39 Options: | |
| 40 ` | |
| 41 | |
| 42 func (a *app) parseArgs(fs *flag.FlagSet, args []string) error { | |
| 43 fs.SetOutput(a.out) | |
| 44 fs.Usage = func() { | |
| 45 fmt.Fprintf(a.out, help, args[0], args[0]) | |
| 46 fs.PrintDefaults() | |
| 47 } | |
| 48 | |
| 49 fs.Var(&a.typeNames, "type", | |
| 50 "A generated proto.Message type to generate stubs for (required, repeatable)") | |
| 51 fs.StringVar(&a.outFile, "out", "proto_gae.gen.go", | |
| 52 "The name of the output file") | |
| 53 | |
| 54 if err := fs.Parse(args[1:]); err != nil { | |
| 55 return err | |
| 56 } | |
| 57 fail := errors.MultiError(nil) | |
| 58 if a.typeNames.Data == nil || a.typeNames.Data.Len() == 0 { | |
| 59 fail = append(fail, errors.New("must specify one or more -type") ) | |
| 60 } | |
| 61 if !strings.HasSuffix(a.outFile, ".go") { | |
| 62 fail = append(fail, errors.New("-output must end with '.go'")) | |
| 63 } | |
| 64 if len(fail) > 0 { | |
| 65 for _, e := range fail { | |
| 66 fmt.Fprintln(a.out, "error:", e) | |
| 67 } | |
| 68 fmt.Fprintln(a.out) | |
| 69 fs.Usage() | |
| 70 return fail | |
| 71 } | |
| 72 return nil | |
| 73 } | |
| 74 | |
| 75 var tmpl = template.Must( | |
| 76 template.New("main").Parse(`// AUTOGENERATED: Do not edit | |
| 77 | |
| 78 package {{index . "package"}} | |
| 79 | |
| 80 import ( | |
| 81 "github.com/golang/protobuf/proto" | |
| 82 | |
| 83 "github.com/luci/gae/service/datastore" | |
| 84 ){{range index . "types"}} | |
| 85 | |
| 86 var _ datastore.PropertyConverter = (*{{.}})(nil) | |
| 87 | |
| 88 // ToProperty implements datastore.PropertyConverter. It causes an embedded | |
| 89 // '{{.}}' to serialize to an unindexed '[]byte' when used with the | |
| 90 // "github.com/luci/gae" library. | |
| 91 func (p *{{.}}) ToProperty() (prop datastore.Property, err error) { | |
| 92 data, err := proto.Marshal(p) | |
| 93 if err == nil { | |
| 94 prop.SetValue(data, datastore.NoIndex) | |
| 95 } | |
| 96 return | |
| 97 } | |
| 98 | |
| 99 // FromProperty implements datastore.PropertyConverter. It parses a '[]byte' | |
| 100 // into an embedded '{{.}}' when used with the "github.com/luci/gae" library. | |
| 101 func (p *{{.}}) FromProperty(prop datastore.Property) error { | |
| 102 data, err := prop.Project(datastore.PTBytes) | |
| 103 if err != nil { | |
| 104 return err | |
| 105 } | |
| 106 return proto.Unmarshal(data.([]byte), p) | |
| 107 }{{end}} | |
| 108 `)) | |
| 109 | |
| 110 func (a *app) writeTo(w io.Writer) error { | |
| 111 typeNames := a.typeNames.Data.ToSlice() | |
| 112 sort.Strings(typeNames) | |
| 113 | |
| 114 return tmpl.Execute(w, map[string]interface{}{ | |
| 115 "package": a.packageName, | |
| 116 "types": typeNames, | |
| 117 }) | |
| 118 } | |
| 119 | |
| 120 func (a *app) main() { | |
| 121 if err := a.parseArgs(flag.NewFlagSet(os.Args[0], flag.ContinueOnError), os.Args); err != nil { | |
|
dnj
2016/01/23 06:15:36
nit: Why ContinueOnError instead of ExitOnError?
iannucci
2016/01/23 10:01:07
So that I could theoretically test this, except I
| |
| 122 os.Exit(1) | |
| 123 } | |
| 124 ofile, err := os.Create(a.outFile) | |
| 125 if err != nil { | |
| 126 fmt.Fprintf(a.out, "error: %s", err) | |
| 127 os.Exit(2) | |
| 128 } | |
| 129 defer ofile.Close() | |
|
dnj
2016/01/23 06:15:36
I suppose in theory if Close fails this could fail
iannucci
2016/01/23 10:01:07
done, though it's completely overkill.
| |
| 130 if err := a.writeTo(ofile); err != nil { | |
| 131 os.Exit(3) | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 func main() { | |
| 136 (&app{out: os.Stderr, packageName: os.Getenv("GOPACKAGE")}).main() | |
| 137 } | |
| OLD | NEW |