blob: 1d92ac0ebf7c9e5fffec2545857061c105860de0 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Todd Wang232d6492015-02-25 18:04:54 -08005// Package golang implements Go code generation from compiled VDL packages.
6package golang
7
8import (
9 "bytes"
10 "fmt"
11 "go/format"
12 "path"
13 "strings"
14 "text/template"
15
16 "v.io/v23/vdl"
Todd Wang232d6492015-02-25 18:04:54 -080017 "v.io/v23/vdlroot/vdltool"
Jiri Simsaffceefa2015-02-28 11:03:34 -080018 "v.io/x/ref/lib/vdl/compile"
19 "v.io/x/ref/lib/vdl/parse"
20 "v.io/x/ref/lib/vdl/vdlutil"
Todd Wang232d6492015-02-25 18:04:54 -080021)
22
23type goData struct {
24 File *compile.File
25 Env *compile.Env
26 Imports *goImports
27}
28
29// testingMode is only set to true in tests, to make testing simpler.
30var testingMode = false
31
32func (data goData) Pkg(pkgPath string) string {
33 if testingMode {
34 return path.Base(pkgPath) + "."
35 }
36 // Special-case to avoid adding package qualifiers if we're generating code
37 // for that package.
38 if data.File.Package.GenPath == pkgPath {
39 return ""
40 }
41 if local := data.Imports.LookupLocal(pkgPath); local != "" {
42 return local + "."
43 }
44 data.Env.Errorf(data.File, parse.Pos{}, "missing package %q", pkgPath)
45 return ""
46}
47
48// Generate takes a populated compile.File and returns a byte slice containing
49// the generated Go source code.
50func Generate(file *compile.File, env *compile.Env) []byte {
51 validateGoConfig(file, env)
52 data := goData{
53 File: file,
54 Env: env,
55 Imports: newImports(file, env),
56 }
57 // The implementation uses the template mechanism from text/template and
58 // executes the template against the goData instance.
59 var buf bytes.Buffer
60 if err := goTemplate.Execute(&buf, data); err != nil {
61 // We shouldn't see an error; it means our template is buggy.
62 panic(fmt.Errorf("vdl: couldn't execute template: %v", err))
63 }
64 // Use gofmt to format the generated source.
65 pretty, err := format.Source(buf.Bytes())
66 if err != nil {
67 // We shouldn't see an error; it means we generated invalid code.
68 fmt.Printf("%s", buf.Bytes())
69 panic(fmt.Errorf("vdl: generated invalid Go code: %v", err))
70 }
71 return pretty
72}
73
74// The native types feature is hard to use correctly. E.g. the package
75// containing the wire type must be imported into your Go binary in order for
76// the wire<->native registration to work, which is hard to ensure. E.g.
77//
78// package base // VDL package
79// type Wire int // has native type native.Int
80//
81// package dep // VDL package
82// import "base"
83// type Foo struct {
84// X base.Wire
85// }
86//
87// The Go code for package "dep" imports "native", rather than "base":
88//
89// package dep // Go package generated from VDL package
90// import "native"
91// type Foo struct {
92// X native.Int
93// }
94//
95// Note that when you import the "dep" package in your own code, you always use
96// native.Int, rather than base.Wire; the base.Wire representation is only used
97// as the wire format, but doesn't appear in generated code. But in order for
98// this to work correctly, the "base" package must imported. This is tricky.
99//
100// Restrict the feature to these whitelisted VDL packages for now.
101var nativeTypePackageWhitelist = map[string]bool{
102 "time": true,
Jiri Simsaffceefa2015-02-28 11:03:34 -0800103 "v.io/x/ref/lib/vdl/testdata/nativetest": true,
104 "v.io/v23/security": true,
Todd Wang232d6492015-02-25 18:04:54 -0800105}
106
107func validateGoConfig(file *compile.File, env *compile.Env) {
108 pkg := file.Package
109 vdlconfig := path.Join(pkg.GenPath, "vdl.config")
110 // Validate native type configuration. Since native types are hard to use, we
111 // restrict them to a built-in whitelist of packages for now.
112 if len(pkg.Config.Go.WireToNativeTypes) > 0 && !nativeTypePackageWhitelist[pkg.Path] {
113 env.Errors.Errorf("%s: Go.WireToNativeTypes is restricted to whitelisted VDL packages", vdlconfig)
114 }
115 // Make sure each wire type is actually defined in the package, and required
116 // fields are all filled in.
117 for wire, native := range pkg.Config.Go.WireToNativeTypes {
118 if def := pkg.ResolveType(wire); def == nil {
119 env.Errors.Errorf("%s: type %s specified in Go.WireToNativeTypes undefined", vdlconfig, wire)
120 }
121 if native.Type == "" {
122 env.Errors.Errorf("%s: type %s specified in Go.WireToNativeTypes invalid (empty GoType.Type)", vdlconfig, wire)
123 }
124 for _, imp := range native.Imports {
125 if imp.Path == "" || imp.Name == "" {
126 env.Errors.Errorf("%s: type %s specified in Go.WireToNativeTypes invalid (empty GoImport.Path or Name)", vdlconfig, wire)
127 continue
128 }
129 importPrefix := imp.Name + "."
130 if !strings.Contains(native.Type, importPrefix) {
131 env.Errors.Errorf("%s: type %s specified in Go.WireToNativeTypes invalid (native type %q doesn't contain import prefix %q)", vdlconfig, wire, native.Type, importPrefix)
132 }
133 }
134 }
135}
136
137var goTemplate *template.Template
138
139// The template mechanism is great at high-level formatting and simple
140// substitution, but is bad at more complicated logic. We define some functions
141// that we can use in the template so that when things get complicated we back
142// off to a regular function.
143func init() {
144 funcMap := template.FuncMap{
145 "firstRuneToExport": vdlutil.FirstRuneToExportCase,
146 "firstRuneToUpper": vdlutil.FirstRuneToUpper,
147 "firstRuneToLower": vdlutil.FirstRuneToLower,
148 "errorName": errorName,
149 "nativeIdent": nativeIdent,
150 "typeGo": typeGo,
151 "typeDefGo": typeDefGo,
152 "constDefGo": constDefGo,
153 "tagValue": tagValue,
154 "embedGo": embedGo,
155 "isStreamingMethod": isStreamingMethod,
156 "hasStreamingMethods": hasStreamingMethods,
157 "docBreak": docBreak,
158 "quoteStripDoc": parse.QuoteStripDoc,
159 "argNames": argNames,
160 "argTypes": argTypes,
161 "argNameTypes": argNameTypes,
162 "argParens": argParens,
163 "uniqueName": uniqueName,
164 "uniqueNameImpl": uniqueNameImpl,
Todd Wang54feabe2015-04-15 23:38:26 -0700165 "serverCallVar": serverCallVar,
166 "serverCallStubVar": serverCallStubVar,
Todd Wang232d6492015-02-25 18:04:54 -0800167 "outArgsClient": outArgsClient,
168 "clientStubImpl": clientStubImpl,
169 "clientFinishImpl": clientFinishImpl,
170 "serverStubImpl": serverStubImpl,
171 "reInitStreamValue": reInitStreamValue,
172 "nativeConversionsInFile": nativeConversionsInFile,
173 }
174 goTemplate = template.Must(template.New("genGo").Funcs(funcMap).Parse(genGo))
175}
176
177func errorName(def *compile.ErrorDef, file *compile.File) string {
178 switch {
179 case def.Exported:
180 return "Err" + def.Name
181 default:
182 return "err" + vdlutil.FirstRuneToUpper(def.Name)
183 }
184}
185
186func isStreamingMethod(method *compile.Method) bool {
187 return method.InStream != nil || method.OutStream != nil
188}
189
190func hasStreamingMethods(methods []*compile.Method) bool {
191 for _, method := range methods {
192 if isStreamingMethod(method) {
193 return true
194 }
195 }
196 return false
197}
198
199// docBreak adds a "//\n" break to separate previous comment lines and doc. If
200// doc is empty it returns the empty string.
201func docBreak(doc string) string {
202 if doc == "" {
203 return ""
204 }
205 return "//\n" + doc
206}
207
208// argTypes returns a comma-separated list of each type from args.
209func argTypes(first, last string, data goData, args []*compile.Field) string {
210 var result []string
211 if first != "" {
212 result = append(result, first)
213 }
214 for _, arg := range args {
215 result = append(result, typeGo(data, arg.Type))
216 }
217 if last != "" {
218 result = append(result, last)
219 }
220 return strings.Join(result, ", ")
221}
222
223// argNames returns a comma-separated list of each name from args. If argPrefix
224// is empty, the name specified in args is used; otherwise the name is prefixD,
225// where D is the position of the argument.
Todd Wang54feabe2015-04-15 23:38:26 -0700226func argNames(boxPrefix, argPrefix, first, second, last string, args []*compile.Field) string {
Todd Wang232d6492015-02-25 18:04:54 -0800227 var result []string
228 if first != "" {
229 result = append(result, first)
230 }
Todd Wang54feabe2015-04-15 23:38:26 -0700231 if second != "" {
232 result = append(result, second)
233 }
Todd Wang232d6492015-02-25 18:04:54 -0800234 for ix, arg := range args {
235 name := arg.Name
236 if argPrefix != "" {
237 name = fmt.Sprintf("%s%d", argPrefix, ix)
238 }
239 if arg.Type == vdl.ErrorType {
240 // TODO(toddw): Also need to box user-defined external interfaces. Or can
241 // we remove this special-case now?
242 name = boxPrefix + name
243 }
244 result = append(result, name)
245 }
246 if last != "" {
247 result = append(result, last)
248 }
249 return strings.Join(result, ", ")
250}
251
252// argNameTypes returns a comma-separated list of "name type" from args. If
253// argPrefix is empty, the name specified in args is used; otherwise the name is
254// prefixD, where D is the position of the argument. If argPrefix is empty and
255// no names are specified in args, no names will be output.
Todd Wang54feabe2015-04-15 23:38:26 -0700256func argNameTypes(argPrefix, first, second, last string, data goData, args []*compile.Field) string {
Todd Wang232d6492015-02-25 18:04:54 -0800257 noNames := argPrefix == "" && !hasArgNames(args)
258 var result []string
259 if first != "" {
260 result = append(result, maybeStripArgName(first, noNames))
261 }
Todd Wang54feabe2015-04-15 23:38:26 -0700262 if second != "" {
263 result = append(result, maybeStripArgName(second, noNames))
264 }
Todd Wang232d6492015-02-25 18:04:54 -0800265 for ax, arg := range args {
266 var name string
267 switch {
268 case noNames:
269 break
270 case argPrefix == "":
271 name = arg.Name + " "
272 default:
273 name = fmt.Sprintf("%s%d ", argPrefix, ax)
274 }
275 result = append(result, name+typeGo(data, arg.Type))
276 }
277 if last != "" {
278 result = append(result, maybeStripArgName(last, noNames))
279 }
280 return strings.Join(result, ", ")
281}
282
283func hasArgNames(args []*compile.Field) bool {
284 // VDL guarantees that either all args are named, or none of them are.
285 return len(args) > 0 && args[0].Name != ""
286}
287
288// maybeStripArgName strips away the first space-terminated token from arg, only
289// if strip is true.
290func maybeStripArgName(arg string, strip bool) string {
291 if index := strings.Index(arg, " "); index != -1 && strip {
292 return arg[index+1:]
293 }
294 return arg
295}
296
297// argParens takes a list of 0 or more arguments, and adds parens only when
298// necessary; if args contains any commas or spaces, we must add parens.
299func argParens(argList string) string {
300 if strings.IndexAny(argList, ", ") > -1 {
301 return "(" + argList + ")"
302 }
303 return argList
304}
305
306// uniqueName returns a unique name based on the interface, method and suffix.
307func uniqueName(iface *compile.Interface, method *compile.Method, suffix string) string {
308 return iface.Name + method.Name + suffix
309}
310
311// uniqueNameImpl returns uniqueName with an "impl" prefix.
312func uniqueNameImpl(iface *compile.Interface, method *compile.Method, suffix string) string {
313 return "impl" + uniqueName(iface, method, suffix)
314}
315
316// The first arg of every server method is a context; the type is either a typed
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700317// context for streams, or rpc.ServerCall for non-streams.
Todd Wang54feabe2015-04-15 23:38:26 -0700318func serverCallVar(data goData, iface *compile.Interface, method *compile.Method) string {
Todd Wang232d6492015-02-25 18:04:54 -0800319 if isStreamingMethod(method) {
Todd Wang54feabe2015-04-15 23:38:26 -0700320 return "call " + uniqueName(iface, method, "ServerCall")
Todd Wang232d6492015-02-25 18:04:54 -0800321 }
Todd Wang54feabe2015-04-15 23:38:26 -0700322 return "call " + data.Pkg("v.io/v23/rpc") + "ServerCall"
Todd Wang232d6492015-02-25 18:04:54 -0800323}
324
325// The first arg of every server stub method is a context; the type is either a
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700326// typed context stub for streams, or rpc.ServerCall for non-streams.
Todd Wang54feabe2015-04-15 23:38:26 -0700327func serverCallStubVar(data goData, iface *compile.Interface, method *compile.Method) string {
Todd Wang232d6492015-02-25 18:04:54 -0800328 if isStreamingMethod(method) {
Todd Wang54feabe2015-04-15 23:38:26 -0700329 return "call *" + uniqueName(iface, method, "ServerCallStub")
Todd Wang232d6492015-02-25 18:04:54 -0800330 }
Todd Wang54feabe2015-04-15 23:38:26 -0700331 return "call " + data.Pkg("v.io/v23/rpc") + "ServerCall"
Todd Wang232d6492015-02-25 18:04:54 -0800332}
333
334// outArgsClient returns the out args of an interface method on the client,
335// wrapped in parens if necessary. The client side always returns a final
336// error, in addition to the regular out-args.
337func outArgsClient(argPrefix string, data goData, iface *compile.Interface, method *compile.Method) string {
338 first, args := "", method.OutArgs
339 if isStreamingMethod(method) {
Matt Rosencrantz5c7ed212015-02-27 22:42:35 -0800340 first, args = "ocall "+uniqueName(iface, method, "ClientCall"), nil
Todd Wang232d6492015-02-25 18:04:54 -0800341 }
Todd Wang54feabe2015-04-15 23:38:26 -0700342 return argParens(argNameTypes(argPrefix, first, "", "err error", data, args))
Todd Wang232d6492015-02-25 18:04:54 -0800343}
344
345// clientStubImpl returns the interface method client stub implementation.
346func clientStubImpl(data goData, iface *compile.Interface, method *compile.Method) string {
347 var buf bytes.Buffer
348 inargs := "nil"
349 if len(method.InArgs) > 0 {
Todd Wang54feabe2015-04-15 23:38:26 -0700350 inargs = "[]interface{}{" + argNames("&", "i", "", "", "", method.InArgs) + "}"
Todd Wang232d6492015-02-25 18:04:54 -0800351 }
Todd Wang232d6492015-02-25 18:04:54 -0800352 switch {
353 case isStreamingMethod(method):
Suharsh Sivakumardf2672a2015-04-09 19:26:43 -0700354 fmt.Fprint(&buf, "\tvar call "+data.Pkg("v.io/v23/rpc")+"ClientCall\n")
355 fmt.Fprintf(&buf, "\tif call, err = "+data.Pkg("v.io/v23")+"GetClient(ctx).StartCall(ctx, c.name, %q, %s, opts...); err != nil {\n\t\treturn\n\t}\n", method.Name, inargs)
Matt Rosencrantz5c7ed212015-02-27 22:42:35 -0800356 fmt.Fprintf(&buf, "ocall = &%s{ClientCall: call}\n", uniqueNameImpl(iface, method, "ClientCall"))
Todd Wang232d6492015-02-25 18:04:54 -0800357 default:
Suharsh Sivakumardf2672a2015-04-09 19:26:43 -0700358 outargs := "nil"
359 if len(method.OutArgs) > 0 {
Todd Wang54feabe2015-04-15 23:38:26 -0700360 outargs = "[]interface{}{" + argNames("", "&o", "", "", "", method.OutArgs) + "}"
Suharsh Sivakumardf2672a2015-04-09 19:26:43 -0700361 }
362 fmt.Fprintf(&buf, "\terr = "+data.Pkg("v.io/v23")+"GetClient(ctx).Call(ctx, c.name, %q, %s, %s, opts...)\n", method.Name, inargs, outargs)
Todd Wang232d6492015-02-25 18:04:54 -0800363 }
364 fmt.Fprint(&buf, "\treturn")
365 return buf.String() // the caller writes the trailing newline
366}
367
368// clientFinishImpl returns the client finish implementation for method.
369func clientFinishImpl(varname string, method *compile.Method) string {
Todd Wang54feabe2015-04-15 23:38:26 -0700370 outargs := argNames("", "&o", "", "", "", method.OutArgs)
Todd Wang232d6492015-02-25 18:04:54 -0800371 return fmt.Sprintf("\terr = %s.Finish(%s)", varname, outargs)
372}
373
374// serverStubImpl returns the interface method server stub implementation.
375func serverStubImpl(data goData, iface *compile.Interface, method *compile.Method) string {
376 var buf bytes.Buffer
Todd Wang54feabe2015-04-15 23:38:26 -0700377 inargs := argNames("", "i", "ctx", "call", "", method.InArgs)
Todd Wang232d6492015-02-25 18:04:54 -0800378 fmt.Fprintf(&buf, "\treturn s.impl.%s(%s)", method.Name, inargs)
379 return buf.String() // the caller writes the trailing newline
380}
381
382func reInitStreamValue(data goData, t *vdl.Type, name string) string {
383 switch t.Kind() {
384 case vdl.Struct:
385 return name + " = " + typeGo(data, t) + "{}\n"
386 case vdl.Any:
387 return name + " = nil\n"
388 }
389 return ""
390}
391
392// nativeConversionsInFile returns the map between wire and native types for
393// wire types defined in file.
394func nativeConversionsInFile(file *compile.File) map[string]vdltool.GoType {
395 all := file.Package.Config.Go.WireToNativeTypes
396 infile := make(map[string]vdltool.GoType)
397 for wire, gotype := range all {
398 for _, tdef := range file.TypeDefs {
399 if tdef.Name == wire {
400 infile[wire] = gotype
401 break
402 }
403 }
404 }
405 return infile
406}
407
408// The template that we execute against a goData instance to generate our
409// code. Most of this is fairly straightforward substitution and ranges; more
410// complicated logic is delegated to the helper functions above.
411//
412// We try to generate code that has somewhat reasonable formatting, and leave
413// the fine-tuning to the go/format package. Note that go/format won't fix
414// some instances of spurious newlines, so we try to keep it reasonable.
415const genGo = `
416{{$data := .}}
417{{$file := $data.File}}
Todd Wangb90b8de2015-03-31 20:04:13 -0700418{{$file.Package.FileDoc}}
Jiri Simsa67b8a262015-03-24 21:14:07 -0700419
Suharsh Sivakumara4ac50a2015-03-13 16:13:50 -0700420// This file was auto-generated by the vanadium vdl tool.
Todd Wang232d6492015-02-25 18:04:54 -0800421// Source: {{$file.BaseName}}
422
423{{$file.PackageDef.Doc}}package {{$file.PackageDef.Name}}{{$file.PackageDef.DocSuffix}}
424
425{{if or $data.Imports.System $data.Imports.User}}
426import ( {{if $data.Imports.System}}
427 // VDL system imports{{range $imp := $data.Imports.System}}
428 {{if $imp.Name}}{{$imp.Name}} {{end}}"{{$imp.Path}}"{{end}}{{end}}
429{{if $data.Imports.User}}
430 // VDL user imports{{range $imp := $data.Imports.User}}
431 {{if $imp.Name}}{{$imp.Name}} {{end}}"{{$imp.Path}}"{{end}}{{end}}
432){{end}}
433
434{{if $file.TypeDefs}}
435{{range $tdef := $file.TypeDefs}}
436{{typeDefGo $data $tdef}}
437{{end}}
438{{$nativeConversions := nativeConversionsInFile $file}}
439func init() { {{range $wire, $native := $nativeConversions}}{{$lwire := firstRuneToLower $wire}}
440 {{$data.Pkg "v.io/v23/vdl"}}RegisterNative({{$lwire}}ToNative, {{$lwire}}FromNative){{end}}{{range $tdef := $file.TypeDefs}}
441 {{$data.Pkg "v.io/v23/vdl"}}Register((*{{$tdef.Name}})(nil)){{end}}
442}
443{{range $wire, $native := $nativeConversions}}{{$lwire := firstRuneToLower $wire}}{{$nat := nativeIdent $data $native}}
444// Type-check {{$wire}} conversion functions.
445var _ func({{$wire}}, *{{$nat}}) error = {{$lwire}}ToNative
446var _ func(*{{$wire}}, {{$nat}}) error = {{$lwire}}FromNative
447{{end}}
448{{end}}
449
450{{range $cdef := $file.ConstDefs}}
451{{constDefGo $data $cdef}}
452{{end}}
453
454{{if $file.ErrorDefs}}var ( {{range $edef := $file.ErrorDefs}}
455 {{$edef.Doc}}{{errorName $edef $file}} = {{$data.Pkg "v.io/v23/verror"}}Register("{{$edef.ID}}", {{$data.Pkg "v.io/v23/verror"}}{{$edef.RetryCode}}, "{{$edef.English}}"){{end}}
456)
457
458{{/* TODO(toddw): Don't set "en-US" or "en" again, since it's already set by Register */}}
459func init() { {{range $edef := $file.ErrorDefs}}{{range $lf := $edef.Formats}}
460 {{$data.Pkg "v.io/v23/i18n"}}Cat().SetWithBase({{$data.Pkg "v.io/v23/i18n"}}LangID("{{$lf.Lang}}"), {{$data.Pkg "v.io/v23/i18n"}}MsgID({{errorName $edef $file}}.ID), "{{$lf.Fmt}}"){{end}}{{end}}
461}
462{{range $edef := $file.ErrorDefs}}
463{{$errName := errorName $edef $file}}
464{{$newErr := print (firstRuneToExport "New" $edef.Exported) (firstRuneToUpper $errName)}}
465// {{$newErr}} returns an error with the {{$errName}} ID.
Todd Wang54feabe2015-04-15 23:38:26 -0700466func {{$newErr}}(ctx {{argNameTypes "" (print "*" ($data.Pkg "v.io/v23/context") "T") "" "" $data $edef.Params}}) error {
467 return {{$data.Pkg "v.io/v23/verror"}}New({{$errName}}, {{argNames "" "" "ctx" "" "" $edef.Params}})
Todd Wang232d6492015-02-25 18:04:54 -0800468}
469{{end}}{{end}}
470
471{{range $iface := $file.Interfaces}}
472{{$ifaceStreaming := hasStreamingMethods $iface.AllMethods}}
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700473{{$rpc_ := $data.Pkg "v.io/v23/rpc"}}
Todd Wang54feabe2015-04-15 23:38:26 -0700474{{$optsVar := print "opts ..." $rpc_ "CallOpt"}}
475{{$ctxVar := print "ctx *" ($data.Pkg "v.io/v23/context") "T"}}
Todd Wang232d6492015-02-25 18:04:54 -0800476// {{$iface.Name}}ClientMethods is the client interface
477// containing {{$iface.Name}} methods.
478{{docBreak $iface.Doc}}type {{$iface.Name}}ClientMethods interface { {{range $embed := $iface.Embeds}}
479 {{$embed.Doc}}{{embedGo $data $embed}}ClientMethods{{$embed.DocSuffix}}{{end}}{{range $method := $iface.Methods}}
Todd Wang54feabe2015-04-15 23:38:26 -0700480 {{$method.Doc}}{{$method.Name}}({{argNameTypes "" $ctxVar "" $optsVar $data $method.InArgs}}) {{outArgsClient "" $data $iface $method}}{{$method.DocSuffix}}{{end}}
Todd Wang232d6492015-02-25 18:04:54 -0800481}
482
483// {{$iface.Name}}ClientStub adds universal methods to {{$iface.Name}}ClientMethods.
484type {{$iface.Name}}ClientStub interface {
485 {{$iface.Name}}ClientMethods
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700486 {{$rpc_}}UniversalServiceMethods
Todd Wang232d6492015-02-25 18:04:54 -0800487}
488
489// {{$iface.Name}}Client returns a client stub for {{$iface.Name}}.
Asim Shankar69fa69f2015-04-01 11:34:32 -0700490func {{$iface.Name}}Client(name string) {{$iface.Name}}ClientStub {
491 return impl{{$iface.Name}}ClientStub{name{{range $embed := $iface.Embeds}}, {{embedGo $data $embed}}Client(name){{end}} }
Todd Wang232d6492015-02-25 18:04:54 -0800492}
493
494type impl{{$iface.Name}}ClientStub struct {
495 name string
Todd Wang232d6492015-02-25 18:04:54 -0800496{{range $embed := $iface.Embeds}}
497 {{embedGo $data $embed}}ClientStub{{end}}
498}
499
Todd Wang232d6492015-02-25 18:04:54 -0800500{{range $method := $iface.Methods}}
Todd Wang54feabe2015-04-15 23:38:26 -0700501func (c impl{{$iface.Name}}ClientStub) {{$method.Name}}({{argNameTypes "i" $ctxVar "" $optsVar $data $method.InArgs}}) {{outArgsClient "o" $data $iface $method}} {
Todd Wang232d6492015-02-25 18:04:54 -0800502{{clientStubImpl $data $iface $method}}
503}
504{{end}}
505
506{{range $method := $iface.Methods}}{{if isStreamingMethod $method}}
507{{$clientStream := uniqueName $iface $method "ClientStream"}}
Matt Rosencrantz5c7ed212015-02-27 22:42:35 -0800508{{$clientCall := uniqueName $iface $method "ClientCall"}}
509{{$clientCallImpl := uniqueNameImpl $iface $method "ClientCall"}}
Suharsh Sivakumar31f49852015-03-03 16:13:20 -0800510{{$clientRecvImpl := uniqueNameImpl $iface $method "ClientCallRecv"}}
511{{$clientSendImpl := uniqueNameImpl $iface $method "ClientCallSend"}}
Todd Wang232d6492015-02-25 18:04:54 -0800512
513// {{$clientStream}} is the client stream for {{$iface.Name}}.{{$method.Name}}.
514type {{$clientStream}} interface { {{if $method.OutStream}}
515 // RecvStream returns the receiver side of the {{$iface.Name}}.{{$method.Name}} client stream.
516 RecvStream() interface {
517 // Advance stages an item so that it may be retrieved via Value. Returns
518 // true iff there is an item to retrieve. Advance must be called before
519 // Value is called. May block if an item is not available.
520 Advance() bool
521 // Value returns the item that was staged by Advance. May panic if Advance
522 // returned false or was not called. Never blocks.
523 Value() {{typeGo $data $method.OutStream}}
524 // Err returns any error encountered by Advance. Never blocks.
525 Err() error
526 } {{end}}{{if $method.InStream}}
527 // SendStream returns the send side of the {{$iface.Name}}.{{$method.Name}} client stream.
528 SendStream() interface {
529 // Send places the item onto the output stream. Returns errors
530 // encountered while sending, or if Send is called after Close or
531 // the stream has been canceled. Blocks if there is no buffer
532 // space; will unblock when buffer space is available or after
533 // the stream has been canceled.
534 Send(item {{typeGo $data $method.InStream}}) error
535 // Close indicates to the server that no more items will be sent;
536 // server Recv calls will receive io.EOF after all sent items.
537 // This is an optional call - e.g. a client might call Close if it
538 // needs to continue receiving items from the server after it's
539 // done sending. Returns errors encountered while closing, or if
540 // Close is called after the stream has been canceled. Like Send,
541 // blocks if there is no buffer space available.
542 Close() error
543 } {{end}}
544}
545
546// {{$clientCall}} represents the call returned from {{$iface.Name}}.{{$method.Name}}.
547type {{$clientCall}} interface {
548 {{$clientStream}} {{if $method.InStream}}
549 // Finish performs the equivalent of SendStream().Close, then blocks until
550 // the server is done, and returns the positional return values for the call.{{else}}
551 // Finish blocks until the server is done, and returns the positional return
552 // values for call.{{end}}
553 //
554 // Finish returns immediately if the call has been canceled; depending on the
555 // timing the output could either be an error signaling cancelation, or the
556 // valid positional return values from the server.
557 //
558 // Calling Finish is mandatory for releasing stream resources, unless the call
559 // has been canceled or any of the other methods return an error. Finish should
560 // be called at most once.
Todd Wang54feabe2015-04-15 23:38:26 -0700561 Finish() {{argParens (argNameTypes "" "" "" "err error" $data $method.OutArgs)}}
Todd Wang232d6492015-02-25 18:04:54 -0800562}
563
564type {{$clientCallImpl}} struct {
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700565 {{$rpc_}}ClientCall{{if $method.OutStream}}
Todd Wang232d6492015-02-25 18:04:54 -0800566 valRecv {{typeGo $data $method.OutStream}}
567 errRecv error{{end}}
568}
569
570{{if $method.OutStream}}func (c *{{$clientCallImpl}}) RecvStream() interface {
571 Advance() bool
572 Value() {{typeGo $data $method.OutStream}}
573 Err() error
574} {
575 return {{$clientRecvImpl}}{c}
576}
577
578type {{$clientRecvImpl}} struct {
579 c *{{$clientCallImpl}}
580}
581
582func (c {{$clientRecvImpl}}) Advance() bool {
583 {{reInitStreamValue $data $method.OutStream "c.c.valRecv"}}c.c.errRecv = c.c.Recv(&c.c.valRecv)
584 return c.c.errRecv == nil
585}
586func (c {{$clientRecvImpl}}) Value() {{typeGo $data $method.OutStream}} {
587 return c.c.valRecv
588}
589func (c {{$clientRecvImpl}}) Err() error {
590 if c.c.errRecv == {{$data.Pkg "io"}}EOF {
591 return nil
592 }
593 return c.c.errRecv
594}
595{{end}}{{if $method.InStream}}func (c *{{$clientCallImpl}}) SendStream() interface {
596 Send(item {{typeGo $data $method.InStream}}) error
597 Close() error
598} {
599 return {{$clientSendImpl}}{c}
600}
601
602type {{$clientSendImpl}} struct {
603 c *{{$clientCallImpl}}
604}
605
606func (c {{$clientSendImpl}}) Send(item {{typeGo $data $method.InStream}}) error {
607 return c.c.Send(item)
608}
609func (c {{$clientSendImpl}}) Close() error {
610 return c.c.CloseSend()
611}
Todd Wang54feabe2015-04-15 23:38:26 -0700612{{end}}func (c *{{$clientCallImpl}}) Finish() {{argParens (argNameTypes "o" "" "" "err error" $data $method.OutArgs)}} {
Matt Rosencrantz5c7ed212015-02-27 22:42:35 -0800613{{clientFinishImpl "c.ClientCall" $method}}
Todd Wang232d6492015-02-25 18:04:54 -0800614 return
615}
616{{end}}{{end}}
617
618// {{$iface.Name}}ServerMethods is the interface a server writer
619// implements for {{$iface.Name}}.
620{{docBreak $iface.Doc}}type {{$iface.Name}}ServerMethods interface { {{range $embed := $iface.Embeds}}
621 {{$embed.Doc}}{{embedGo $data $embed}}ServerMethods{{$embed.DocSuffix}}{{end}}{{range $method := $iface.Methods}}
Todd Wang54feabe2015-04-15 23:38:26 -0700622 {{$method.Doc}}{{$method.Name}}({{argNameTypes "" $ctxVar (serverCallVar $data $iface $method) "" $data $method.InArgs}}) {{argParens (argNameTypes "" "" "" "err error" $data $method.OutArgs)}}{{$method.DocSuffix}}{{end}}
Todd Wang232d6492015-02-25 18:04:54 -0800623}
624
625// {{$iface.Name}}ServerStubMethods is the server interface containing
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700626// {{$iface.Name}} methods, as expected by rpc.Server.{{if $ifaceStreaming}}
Todd Wang232d6492015-02-25 18:04:54 -0800627// The only difference between this interface and {{$iface.Name}}ServerMethods
628// is the streaming methods.{{else}}
629// There is no difference between this interface and {{$iface.Name}}ServerMethods
630// since there are no streaming methods.{{end}}
631type {{$iface.Name}}ServerStubMethods {{if $ifaceStreaming}}interface { {{range $embed := $iface.Embeds}}
632 {{$embed.Doc}}{{embedGo $data $embed}}ServerStubMethods{{$embed.DocSuffix}}{{end}}{{range $method := $iface.Methods}}
Todd Wang54feabe2015-04-15 23:38:26 -0700633 {{$method.Doc}}{{$method.Name}}({{argNameTypes "" $ctxVar (serverCallStubVar $data $iface $method) "" $data $method.InArgs}}) {{argParens (argNameTypes "" "" "" "err error" $data $method.OutArgs)}}{{$method.DocSuffix}}{{end}}
Todd Wang232d6492015-02-25 18:04:54 -0800634}
635{{else}}{{$iface.Name}}ServerMethods
636{{end}}
637
638// {{$iface.Name}}ServerStub adds universal methods to {{$iface.Name}}ServerStubMethods.
639type {{$iface.Name}}ServerStub interface {
640 {{$iface.Name}}ServerStubMethods
641 // Describe the {{$iface.Name}} interfaces.
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700642 Describe__() []{{$rpc_}}InterfaceDesc
Todd Wang232d6492015-02-25 18:04:54 -0800643}
644
645// {{$iface.Name}}Server returns a server stub for {{$iface.Name}}.
646// It converts an implementation of {{$iface.Name}}ServerMethods into
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700647// an object that may be used by rpc.Server.
Todd Wang232d6492015-02-25 18:04:54 -0800648func {{$iface.Name}}Server(impl {{$iface.Name}}ServerMethods) {{$iface.Name}}ServerStub {
649 stub := impl{{$iface.Name}}ServerStub{
650 impl: impl,{{range $embed := $iface.Embeds}}
651 {{$embed.Name}}ServerStub: {{embedGo $data $embed}}Server(impl),{{end}}
652 }
653 // Initialize GlobState; always check the stub itself first, to handle the
654 // case where the user has the Glob method defined in their VDL source.
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700655 if gs := {{$rpc_}}NewGlobState(stub); gs != nil {
Todd Wang232d6492015-02-25 18:04:54 -0800656 stub.gs = gs
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700657 } else if gs := {{$rpc_}}NewGlobState(impl); gs != nil {
Todd Wang232d6492015-02-25 18:04:54 -0800658 stub.gs = gs
659 }
660 return stub
661}
662
663type impl{{$iface.Name}}ServerStub struct {
664 impl {{$iface.Name}}ServerMethods{{range $embed := $iface.Embeds}}
665 {{embedGo $data $embed}}ServerStub{{end}}
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700666 gs *{{$rpc_}}GlobState
Todd Wang232d6492015-02-25 18:04:54 -0800667}
668
669{{range $method := $iface.Methods}}
Todd Wang54feabe2015-04-15 23:38:26 -0700670func (s impl{{$iface.Name}}ServerStub) {{$method.Name}}({{argNameTypes "i" $ctxVar (serverCallStubVar $data $iface $method) "" $data $method.InArgs}}) {{argParens (argTypes "" "error" $data $method.OutArgs)}} {
Todd Wang232d6492015-02-25 18:04:54 -0800671{{serverStubImpl $data $iface $method}}
672}
673{{end}}
674
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700675func (s impl{{$iface.Name}}ServerStub) Globber() *{{$rpc_}}GlobState {
Todd Wang232d6492015-02-25 18:04:54 -0800676 return s.gs
677}
678
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700679func (s impl{{$iface.Name}}ServerStub) Describe__() []{{$rpc_}}InterfaceDesc {
680 return []{{$rpc_}}InterfaceDesc{ {{$iface.Name}}Desc{{range $embed := $iface.TransitiveEmbeds}}, {{embedGo $data $embed}}Desc{{end}} }
Todd Wang232d6492015-02-25 18:04:54 -0800681}
682
683// {{$iface.Name}}Desc describes the {{$iface.Name}} interface.
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700684var {{$iface.Name}}Desc {{$rpc_}}InterfaceDesc = desc{{$iface.Name}}
Todd Wang232d6492015-02-25 18:04:54 -0800685
686// desc{{$iface.Name}} hides the desc to keep godoc clean.
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700687var desc{{$iface.Name}} = {{$rpc_}}InterfaceDesc{ {{if $iface.Name}}
Todd Wang232d6492015-02-25 18:04:54 -0800688 Name: "{{$iface.Name}}",{{end}}{{if $iface.File.Package.Path}}
689 PkgPath: "{{$iface.File.Package.Path}}",{{end}}{{if $iface.Doc}}
690 Doc: {{quoteStripDoc $iface.Doc}},{{end}}{{if $iface.Embeds}}
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700691 Embeds: []{{$rpc_}}EmbedDesc{ {{range $embed := $iface.Embeds}}
Todd Wang232d6492015-02-25 18:04:54 -0800692 { "{{$embed.Name}}", "{{$embed.File.Package.Path}}", {{quoteStripDoc $embed.Doc}} },{{end}}
693 },{{end}}{{if $iface.Methods}}
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700694 Methods: []{{$rpc_}}MethodDesc{ {{range $method := $iface.Methods}}
Todd Wang232d6492015-02-25 18:04:54 -0800695 { {{if $method.Name}}
696 Name: "{{$method.Name}}",{{end}}{{if $method.Doc}}
697 Doc: {{quoteStripDoc $method.Doc}},{{end}}{{if $method.InArgs}}
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700698 InArgs: []{{$rpc_}}ArgDesc{ {{range $arg := $method.InArgs}}
Todd Wang232d6492015-02-25 18:04:54 -0800699 { "{{$arg.Name}}", {{quoteStripDoc $arg.Doc}} }, // {{typeGo $data $arg.Type}}{{end}}
700 },{{end}}{{if $method.OutArgs}}
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700701 OutArgs: []{{$rpc_}}ArgDesc{ {{range $arg := $method.OutArgs}}
Todd Wang232d6492015-02-25 18:04:54 -0800702 { "{{$arg.Name}}", {{quoteStripDoc $arg.Doc}} }, // {{typeGo $data $arg.Type}}{{end}}
703 },{{end}}{{if $method.Tags}}
704 Tags: []*{{$data.Pkg "v.io/v23/vdl"}}Value{ {{range $tag := $method.Tags}}{{tagValue $data $tag}} ,{{end}} },{{end}}
705 },{{end}}
706 },{{end}}
707}
708
709{{range $method := $iface.Methods}}
710{{if isStreamingMethod $method}}
711{{$serverStream := uniqueName $iface $method "ServerStream"}}
Suharsh Sivakumar31f49852015-03-03 16:13:20 -0800712{{$serverCall := uniqueName $iface $method "ServerCall"}}
713{{$serverCallStub := uniqueName $iface $method "ServerCallStub"}}
714{{$serverRecvImpl := uniqueNameImpl $iface $method "ServerCallRecv"}}
715{{$serverSendImpl := uniqueNameImpl $iface $method "ServerCallSend"}}
Todd Wang232d6492015-02-25 18:04:54 -0800716
717// {{$serverStream}} is the server stream for {{$iface.Name}}.{{$method.Name}}.
718type {{$serverStream}} interface { {{if $method.InStream}}
719 // RecvStream returns the receiver side of the {{$iface.Name}}.{{$method.Name}} server stream.
720 RecvStream() interface {
721 // Advance stages an item so that it may be retrieved via Value. Returns
722 // true iff there is an item to retrieve. Advance must be called before
723 // Value is called. May block if an item is not available.
724 Advance() bool
725 // Value returns the item that was staged by Advance. May panic if Advance
726 // returned false or was not called. Never blocks.
727 Value() {{typeGo $data $method.InStream}}
728 // Err returns any error encountered by Advance. Never blocks.
729 Err() error
730 } {{end}}{{if $method.OutStream}}
731 // SendStream returns the send side of the {{$iface.Name}}.{{$method.Name}} server stream.
732 SendStream() interface {
733 // Send places the item onto the output stream. Returns errors encountered
734 // while sending. Blocks if there is no buffer space; will unblock when
735 // buffer space is available.
736 Send(item {{typeGo $data $method.OutStream}}) error
737 } {{end}}
738}
739
Suharsh Sivakumar31f49852015-03-03 16:13:20 -0800740// {{$serverCall}} represents the context passed to {{$iface.Name}}.{{$method.Name}}.
741type {{$serverCall}} interface {
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700742 {{$rpc_}}ServerCall
Todd Wang232d6492015-02-25 18:04:54 -0800743 {{$serverStream}}
744}
745
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700746// {{$serverCallStub}} is a wrapper that converts rpc.StreamServerCall into
Suharsh Sivakumar31f49852015-03-03 16:13:20 -0800747// a typesafe stub that implements {{$serverCall}}.
748type {{$serverCallStub}} struct {
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700749 {{$rpc_}}StreamServerCall{{if $method.InStream}}
Todd Wang232d6492015-02-25 18:04:54 -0800750 valRecv {{typeGo $data $method.InStream}}
751 errRecv error{{end}}
752}
753
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700754// Init initializes {{$serverCallStub}} from rpc.StreamServerCall.
755func (s *{{$serverCallStub}}) Init(call {{$rpc_}}StreamServerCall) {
Matt Rosencrantz1dcd0a92015-02-27 11:05:59 -0800756 s.StreamServerCall = call
Todd Wang232d6492015-02-25 18:04:54 -0800757}
758
759{{if $method.InStream}}// RecvStream returns the receiver side of the {{$iface.Name}}.{{$method.Name}} server stream.
Suharsh Sivakumar31f49852015-03-03 16:13:20 -0800760func (s *{{$serverCallStub}}) RecvStream() interface {
Todd Wang232d6492015-02-25 18:04:54 -0800761 Advance() bool
762 Value() {{typeGo $data $method.InStream}}
763 Err() error
764} {
765 return {{$serverRecvImpl}}{s}
766}
767
768type {{$serverRecvImpl}} struct {
Suharsh Sivakumar31f49852015-03-03 16:13:20 -0800769 s *{{$serverCallStub}}
Todd Wang232d6492015-02-25 18:04:54 -0800770}
771
772func (s {{$serverRecvImpl}}) Advance() bool {
773 {{reInitStreamValue $data $method.InStream "s.s.valRecv"}}s.s.errRecv = s.s.Recv(&s.s.valRecv)
774 return s.s.errRecv == nil
775}
776func (s {{$serverRecvImpl}}) Value() {{typeGo $data $method.InStream}} {
777 return s.s.valRecv
778}
779func (s {{$serverRecvImpl}}) Err() error {
780 if s.s.errRecv == {{$data.Pkg "io"}}EOF {
781 return nil
782 }
783 return s.s.errRecv
784}
785{{end}}{{if $method.OutStream}}// SendStream returns the send side of the {{$iface.Name}}.{{$method.Name}} server stream.
Suharsh Sivakumar31f49852015-03-03 16:13:20 -0800786func (s *{{$serverCallStub}}) SendStream() interface {
Todd Wang232d6492015-02-25 18:04:54 -0800787 Send(item {{typeGo $data $method.OutStream}}) error
788} {
789 return {{$serverSendImpl}}{s}
790}
791
792type {{$serverSendImpl}} struct {
Suharsh Sivakumar31f49852015-03-03 16:13:20 -0800793 s *{{$serverCallStub}}
Todd Wang232d6492015-02-25 18:04:54 -0800794}
795
796func (s {{$serverSendImpl}}) Send(item {{typeGo $data $method.OutStream}}) error {
797 return s.s.Send(item)
798}
799{{end}}{{end}}{{end}}
800
801{{end}}
802`