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 |