diff --git a/capnpc-go/capnpc-go.go b/capnpc-go/capnpc-go.go index 30edcd84..b36749db 100644 --- a/capnpc-go/capnpc-go.go +++ b/capnpc-go/capnpc-go.go @@ -102,9 +102,9 @@ func (g *generator) Capnp() string { } // generate produces unformatted Go source code from the nodes defined in it. -func (g *generator) generate(pkg string) []byte { +func (g *generator) generate() []byte { var out bytes.Buffer - fmt.Fprintf(&out, "package %s\n\n", pkg) + fmt.Fprintf(&out, "package %s\n\n", g.nodes[g.fileID].pkg) out.WriteString("// AUTO GENERATED - DO NOT EDIT\n\n") out.WriteString("import (\n") for _, imp := range g.imports.usedImports() { @@ -1042,16 +1042,13 @@ func (es enumString) SliceFor(i int) string { return fmt.Sprintf("[%d:%d]", n, n+len(es[i])) } -func generateFile(reqf schema.CodeGeneratorRequest_RequestedFile, nodes nodeMap, opts genoptions) error { - id := reqf.Id() - fname, _ := reqf.Filename() - g := newGenerator(id, nodes, opts) - f := nodes[id] +func (g *generator) defineFile() error { + f := g.nodes[g.fileID] if f == nil { - return fmt.Errorf("no node in schema matches %#x", id) + return fmt.Errorf("no node in schema matches %#x", g.fileID) } if f.pkg == "" { - return fmt.Errorf("missing package annotation for %s", fname) + return errors.New("missing package annotation") } for _, n := range f.nodes { @@ -1080,6 +1077,16 @@ func generateFile(reqf schema.CodeGeneratorRequest_RequestedFile, nodes nodeMap, return err } } + return nil +} + +func generateFile(reqf schema.CodeGeneratorRequest_RequestedFile, nodes nodeMap, opts genoptions) error { + id := reqf.Id() + fname, _ := reqf.Filename() + g := newGenerator(id, nodes, opts) + if err := g.defineFile(); err != nil { + return err + } if dirPath, _ := filepath.Split(fname); dirPath != "" { err := os.MkdirAll(dirPath, os.ModePerm) @@ -1088,7 +1095,7 @@ func generateFile(reqf schema.CodeGeneratorRequest_RequestedFile, nodes nodeMap, } } - unformatted := g.generate(f.pkg) + unformatted := g.generate() formatted, fmtErr := format.Source(unformatted) if fmtErr != nil { formatted = unformatted diff --git a/capnpc-go/capnpc-go_test.go b/capnpc-go/capnpc-go_test.go index efb10704..b33f5955 100644 --- a/capnpc-go/capnpc-go_test.go +++ b/capnpc-go/capnpc-go_test.go @@ -12,9 +12,13 @@ import ( "zombiezen.com/go/capnproto2/internal/schema" ) -func mustReadTestFile(t *testing.T, name string) []byte { +func readTestFile(name string) ([]byte, error) { path := filepath.Join("testdata", name) - data, err := ioutil.ReadFile(path) + return ioutil.ReadFile(path) +} + +func mustReadTestFile(t *testing.T, name string) []byte { + data, err := readTestFile(name) if err != nil { t.Fatal(err) } @@ -228,6 +232,46 @@ func TestDefineConstNodes(t *testing.T) { } } +func TestDefineFile(t *testing.T) { + // Sanity check to make sure schemas don't error on generation. + + tests := []struct { + fileID uint64 + fname string + opts genoptions + }{ + {0xd68755941d99d05e, "scopes.capnp.out", genoptions{promises: true}}, + {0xecd50d792c3d9992, "util.capnp.out", genoptions{promises: true}}, + } + for _, test := range tests { + data, err := readTestFile(test.fname) + if err != nil { + t.Errorf("reading %s: %v", test.fname, err) + continue + } + msg, err := capnp.Unmarshal(data) + if err != nil { + t.Errorf("Unmarshaling %s: %v", test.fname, err) + continue + } + req, err := schema.ReadRootCodeGeneratorRequest(msg) + if err != nil { + t.Errorf("Reading code generator request %s: %v", test.fname, err) + continue + } + nodes, err := buildNodeMap(req) + if err != nil { + t.Errorf("buildNodeMap %s: %v", test.fname, err) + continue + } + g := newGenerator(test.fileID, nodes, test.opts) + if err := g.defineFile(); err != nil { + t.Errorf("defineFile %s: %v", test.fname, err) + continue + } + } +} + type traceRenderer struct { renderer calls []renderCall diff --git a/capnpc-go/templates.go b/capnpc-go/templates.go index b9c347fa..c006f7db 100644 --- a/capnpc-go/templates.go +++ b/capnpc-go/templates.go @@ -10,7 +10,7 @@ import ( var templates = template.Must(template.New("").Funcs(template.FuncMap{ "title": strings.Title, }).Parse( - "{{define \"_hasfield\"}}func (s {{.Node.Name}}) Has{{.Field.Name | title}}() bool {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\treturn p.IsValid() || err != nil \n}\n{{end}}{{define \"_interfaceMethod\"}}\t\t\tInterfaceID: {{.Interface.Id | printf \"%#x\"}},\n\t\t\tMethodID: {{.ID}},\n\t\t\tInterfaceName: {{.Interface.DisplayName | printf \"%q\"}},\n\t\t\tMethodName: {{.OriginalName | printf \"%q\"}},\n{{end}}{{define \"_settag\"}}{{if .Field.HasDiscriminant}}s.Struct.SetUint16({{.Node.DiscriminantOffset}}, {{.Field.DiscriminantValue}})\n{{end}}{{end}}{{define \"annotation\"}}const {{.Node.Name}} = uint64({{.Node.Id | printf \"%#x\"}})\n{{end}}{{define \"constants\"}}{{with .Consts}}// Constants defined in {{$.G.Basename}}.\nconst (\n{{range .}}\t{{.Name}} = {{$.G.Value . .Const.Type .Const.Value}}\n{{end}}\n)\n{{end}}\n{{with .Vars}}// Constants defined in {{$.G.Basename}}.\nvar (\n{{range .}}\t{{.Name}} = {{$.G.Value . .Const.Type .Const.Value}}\n{{end}}\n)\n{{end}}\n{{end}}{{define \"enum\"}}{{with .Annotations.Doc}}// {{.}}\n{{end}}type {{.Node.Name}} uint16\n\n{{with .EnumValues}}// Values of {{$.Node.Name}}.\nconst (\n{{range .}}{{.FullName}} {{$.Node.Name}} = {{.Val}}\n{{end}}\n)\n\n// String returns the enum's constant name.\nfunc (c {{$.Node.Name}}) String() string {\n\tswitch c {\n\t{{range .}}{{if .Tag}}case {{.FullName}}: return {{printf \"%q\" .Tag}}\n\t{{end}}{{end}}\n\tdefault: return \"\"\n\t}\n}\n\n// {{$.Node.Name}}FromString returns the enum value with a name,\n// or the zero value if there's no such value.\nfunc {{$.Node.Name}}FromString(c string) {{$.Node.Name}} {\n\tswitch c {\n\t{{range .}}{{if .Tag}}case {{printf \"%q\" .Tag}}: return {{.FullName}}\n\t{{end}}{{end}}\n\tdefault: return 0\n\t}\n}\n{{end}}\n\ntype {{.Node.Name}}_List struct { {{$.G.Capnp}}.List }\n\nfunc New{{.Node.Name}}_List(s *{{$.G.Capnp}}.Segment, sz int32) ({{.Node.Name}}_List, error) {\n\tl, err := {{.G.Capnp}}.NewUInt16List(s, sz)\n\tif err != nil {\n\t\treturn {{.Node.Name}}_List{}, err\n\t}\n\treturn {{.Node.Name}}_List{l.List}, nil\n}\n\nfunc (l {{.Node.Name}}_List) At(i int) {{.Node.Name}} {\n\tul := {{.G.Capnp}}.UInt16List{List: l.List}\n\treturn {{.Node.Name}}(ul.At(i))\n}\n\nfunc (l {{.Node.Name}}_List) Set(i int, v {{.Node.Name}}) {\n\tul := {{.G.Capnp}}.UInt16List{List: l.List}\n\tul.Set(i, uint16(v))\n}\n{{end}}{{define \"interfaceClient\"}}{{with .Annotations.Doc}}// {{.}}\n{{end}}type {{.Node.Name}} struct { Client {{.G.Capnp}}.Client }\n\n{{range .Methods}}func (c {{$.Node.Name}}) {{.Name | title}}(ctx {{$.G.Imports.Context}}.Context, params func({{$.G.RemoteNodeName .Params $.Node}}) error, opts ...{{$.G.Capnp}}.CallOption) {{$.G.RemoteName .Results $.Node}}_Promise {\n\tif c.Client == nil {\n\t\treturn {{$.G.RemoteNodeName .Results $.Node}}_Promise{Pipeline: {{$.G.Capnp}}.NewPipeline({{$.G.Capnp}}.ErrorAnswer({{$.G.Capnp}}.ErrNullClient))}\n\t}\n\tcall := &{{$.G.Capnp}}.Call{\n\t\tCtx: ctx,\n\t\tMethod: {{$.G.Capnp}}.Method{\n\t\t\t{{template \"_interfaceMethod\" .}}\n\t\t},\n\t\tOptions: {{$.G.Capnp}}.NewCallOptions(opts),\n\t}\n\tif params != nil {\n\t\tcall.ParamsSize = {{$.G.ObjectSize .Params}}\n\t\tcall.ParamsFunc = func(s {{$.G.Capnp}}.Struct) error { return params({{$.G.RemoteNodeName .Params $.Node}}{Struct: s}) }\n\t}\n\treturn {{$.G.RemoteNodeName .Results $.Node}}_Promise{Pipeline: {{$.G.Capnp}}.NewPipeline(c.Client.Call(call))}\n}\n{{end}}\n{{end}}{{define \"interfaceServer\"}}type {{.Node.Name}}_Server interface {\n\t{{range .Methods}}\n\t{{.Name | title}}({{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}) error\n\t{{end}}\n}\n\nfunc {{.Node.Name}}_ServerToClient(s {{.Node.Name}}_Server) {{.Node.Name}} {\n\tc, _ := s.({{.G.Imports.Server}}.Closer)\n\treturn {{.Node.Name}}{Client: {{.G.Imports.Server}}.New({{.Node.Name}}_Methods(nil, s), c)}\n}\n\nfunc {{.Node.Name}}_Methods(methods []{{.G.Imports.Server}}.Method, s {{.Node.Name}}_Server) []{{.G.Imports.Server}}.Method {\n\tif cap(methods) == 0 {\n\t\tmethods = make([]{{.G.Imports.Server}}.Method, 0, {{len .Methods}})\n\t}\n\t{{range .Methods}}\n\tmethods = append(methods, {{$.G.Imports.Server}}.Method{\n\t\tMethod: {{$.G.Capnp}}.Method{\n\t\t\t{{template \"_interfaceMethod\" .}}\n\t\t},\n\t\tImpl: func(c {{$.G.Imports.Context}}.Context, opts {{$.G.Capnp}}.CallOptions, p, r {{$.G.Capnp}}.Struct) error {\n\t\t\tcall := {{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}{c, opts, {{$.G.RemoteName .Params $.Node}}{Struct: p}, {{$.G.RemoteName .Results $.Node}}{Struct: r} }\n\t\t\treturn s.{{.Name | title}}(call)\n\t\t},\n\t\tResultsSize: {{$.G.ObjectSize .Results}},\n\t})\n\t{{end}}\n\treturn methods\n}\n{{range .Methods}}{{if eq .Interface.Id $.Node.Id}}\n// {{$.Node.Name}}_{{.Name}} holds the arguments for a server call to {{$.Node.Name}}.{{.Name}}.\ntype {{$.Node.Name}}_{{.Name}} struct {\n\tCtx {{$.G.Imports.Context}}.Context\n\tOptions {{$.G.Capnp}}.CallOptions\n\tParams {{$.G.RemoteNodeName .Params $.Node}}\n\tResults {{$.G.RemoteNodeName .Results $.Node}}\n}\n{{end}}{{end}}\n{{end}}{{define \"listValue\"}}{{.Typ}}{List: {{.G.Capnp}}.MustUnmarshalRootPtr({{.Value}}).List()}{{end}}{{define \"newStructFunc\"}}func New{{.Node.Name}}(s *{{.G.Capnp}}.Segment) ({{.Node.Name}}, error) {\n\tst, err := {{$.G.Capnp}}.NewStruct(s, {{.G.ObjectSize .Node}})\n\tif err != nil {\n\t\treturn {{.Node.Name}}{}, err\n\t}\n\treturn {{.Node.Name}}{st}, nil\n}\n\nfunc NewRoot{{.Node.Name}}(s *{{.G.Capnp}}.Segment) ({{.Node.Name}}, error) {\n\tst, err := {{.G.Capnp}}.NewRootStruct(s, {{.G.ObjectSize .Node}})\n\tif err != nil {\n\t\treturn {{.Node.Name}}{}, err\n\t}\n\treturn {{.Node.Name}}{st}, nil\n}\n\nfunc ReadRoot{{.Node.Name}}(msg *{{.G.Capnp}}.Message) ({{.Node.Name}}, error) {\n\troot, err := msg.RootPtr()\n\tif err != nil {\n\t\treturn {{.Node.Name}}{}, err\n\t}\n\treturn {{.Node.Name}}{root.Struct()}, nil\n}\n{{end}}{{define \"pointerValue\"}}{{.G.Capnp}}.MustUnmarshalRootPtr({{.Value}}){{end}}{{define \"promise\"}}// {{.Node.Name}}_Promise is a wrapper for a {{.Node.Name}} promised by a client call.\ntype {{.Node.Name}}_Promise struct { *{{.G.Capnp}}.Pipeline }\n\nfunc (p {{.Node.Name}}_Promise) Struct() ({{.Node.Name}}, error) {\n\ts, err := p.Pipeline.Struct()\n\treturn {{.Node.Name}}{s}, err\n}\n\n{{end}}{{define \"promiseFieldAnyPointer\"}}func (p {{.Node.Name}}_Promise) {{.Field.Name | title}}() *{{.G.Capnp}}.Pipeline {\n\treturn p.Pipeline.GetPipeline({{.Field.Slot.Offset}})\n}\n\n{{end}}{{define \"promiseFieldInterface\"}}func (p {{.Node.Name}}_Promise) {{.Field.Name | title}}() {{.G.RemoteNodeName .Interface .Node}} {\n\treturn {{.G.RemoteNodeName .Interface .Node}}{Client: p.Pipeline.GetPipeline({{.Field.Slot.Offset}}).Client()}\n}\n\n{{end}}{{define \"promiseFieldStruct\"}}func (p {{.Node.Name}}_Promise) {{.Field.Name | title}}() {{.G.RemoteNodeName .Struct .Node}}_Promise {\n\treturn {{.G.RemoteNodeName .Struct .Node}}_Promise{Pipeline: p.Pipeline.{{if .Default.IsValid}}GetPipelineDefault({{.Field.Slot.Offset}}, {{.Default}}){{else}}GetPipeline({{.Field.Slot.Offset}}){{end}} }\n}\n\n{{end}}{{define \"promiseGroup\"}}func (p {{.Node.Name}}_Promise) {{.Field.Name | title}}() {{.Group.Name}}_Promise { return {{.Group.Name}}_Promise{p.Pipeline} }\n{{end}}{{define \"structBoolField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() bool {\n\treturn {{if .Default}}!{{end}}s.Struct.Bit({{.Field.Slot.Offset}})\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v bool) {\n\t{{template \"_settag\" .}}s.Struct.SetBit({{.Field.Slot.Offset}}, {{if .Default}}!{{end}}v)\n}\n\n{{end}}{{define \"structDataField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() ({{.FieldType}}, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t{{with .Default}}return {{$.FieldType}}(p.DataDefault({{printf \"%#v\" .}})), nil{{else}}return {{.FieldType}}(p.Data()), nil{{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.FieldType}}) error {\n\t{{template \"_settag\" .}}d, err := {{.G.Capnp}}.NewData(s.Struct.Segment(), []byte(v))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn s.Struct.SetPtr({{.Field.Slot.Offset}}, d.List.ToPtr())\n}\n\n{{end}}{{define \"structEnums\"}}type {{.Node.Name}}_Which uint16\n\nconst (\n{{range .Fields}}\t{{$.Node.Name}}_Which_{{.Name}} {{$.Node.Name}}_Which = {{.DiscriminantValue}}\n{{end}}\n)\n\nfunc (w {{.Node.Name}}_Which) String() string {\n\tconst s = {{.EnumString.ValueString | printf \"%q\"}}\n\tswitch w {\n\t{{range $i, $f := .Fields}}case {{$.Node.Name}}_Which_{{.Name}}:\n\t\treturn s{{$.EnumString.SliceFor $i}}\n\t{{end}}\n\t}\n\treturn \"{{.Node.Name}}_Which(\" + {{.G.Imports.Strconv}}.FormatUint(uint64(w), 10) + \")\"\n}\n\n{{end}}{{define \"structFloatField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() float{{.Bits}} {\n\treturn {{.G.Imports.Math}}.Float{{.Bits}}frombits(s.Struct.Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{printf \"%#x\" .}}{{end}})\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v float{{.Bits}}) {\n\t{{template \"_settag\" .}}s.Struct.SetUint{{.Bits}}({{.Offset}}, {{.G.Imports.Math}}.Float{{.Bits}}bits(v){{with .Default}}^{{printf \"%#x\" .}}{{end}})\n}\n\n{{end}}{{define \"structFuncs\"}}{{if gt .Node.StructNode.DiscriminantCount 0}}\nfunc (s {{.Node.Name}}) Which() {{.Node.Name}}_Which {\n\treturn {{.Node.Name}}_Which(s.Struct.Uint16({{.Node.DiscriminantOffset}}))\n}\n{{end}}{{end}}{{define \"structGroup\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() {{.Group.Name}} { return {{.Group.Name}}(s) }{{if .Field.HasDiscriminant}}\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}() { {{template \"_settag\" .}} }\n{{end}}{{end}}{{define \"structIntField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() {{.ReturnType}} {\n\treturn {{.ReturnType}}(s.Struct.Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{.}}{{end}})\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.ReturnType}}) {\n\t{{template \"_settag\" .}}s.Struct.SetUint{{.Bits}}({{.Offset}}, uint{{.Bits}}(v){{with .Default}}^{{.}}{{end}})\n}\n\n{{end}}{{define \"structInterfaceField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() {{.FieldType}} {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\t\n\t\treturn {{.FieldType}}{}\n\t}\n\treturn {{.FieldType}}{Client: p.Interface().Client()}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.FieldType}}) error {\n\t{{template \"_settag\" .}}seg := s.Segment()\n\tif seg == nil {\n\t\t\n\t\treturn nil\n\t}\n\tvar in capnp.Interface\n\tif v.Client != nil {\n\t\tin = {{.G.Capnp}}.NewInterface(seg, seg.Message().AddCap(v.Client))\n\t}\n\treturn s.Struct.SetPtr({{.Field.Slot.Offset}}, in.ToPtr())\n}\n\n{{end}}{{define \"structList\"}}// {{.Node.Name}}_List is a list of {{.Node.Name}}.\ntype {{.Node.Name}}_List struct{ {{.G.Capnp}}.List }\n\n// New{{.Node.Name}} creates a new list of {{.Node.Name}}.\nfunc New{{.Node.Name}}_List(s *{{.G.Capnp}}.Segment, sz int32) ({{.Node.Name}}_List, error) {\n\tl, err := {{.G.Capnp}}.NewCompositeList(s, {{.G.ObjectSize .Node}}, sz)\n\tif err != nil {\n\t\treturn {{.Node.Name}}_List{}, err\n\t}\n\treturn {{.Node.Name}}_List{l}, nil\n}\n\nfunc (s {{.Node.Name}}_List) At(i int) {{.Node.Name}} { return {{.Node.Name}}{ s.List.Struct(i) } }\nfunc (s {{.Node.Name}}_List) Set(i int, v {{.Node.Name}}) error { return s.List.SetStruct(i, v.Struct) }\n{{end}}{{define \"structListField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() ({{.FieldType}}, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\t{{if .Default.IsValid}}l, err := p.ListDefault({{.Default}})\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\treturn {{.FieldType}}{List: l}, nil{{else}}return {{.FieldType}}{List: p.List()}, nil{{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.FieldType}}) error {\n\t{{template \"_settag\" .}}return s.Struct.SetPtr({{.Field.Slot.Offset}}, v.List.ToPtr())\n}\n\n// New{{.Field.Name | title}} sets the {{.Field.Name}} field to a newly\n// allocated {{.FieldType}}, preferring placement in s's segment.\nfunc (s {{.Node.Name}}) New{{.Field.Name | title}}(n int32) ({{.FieldType}}, error) {\n\t{{template \"_settag\" .}}l, err := {{.G.RemoteTypeNew .Field.Slot.Type .Node}}(s.Struct.Segment(), n)\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\terr = s.Struct.SetPtr({{.Field.Slot.Offset}}, l.List.ToPtr())\n\treturn l, err\n}\n\n{{end}}{{define \"structPointerField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() ({{.G.Capnp}}.Pointer, error) {\n\t{{if .Default.IsValid}}p, err := s.Struct.Pointer({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn {{.G.Capnp}}.PointerDefault(p, {{.Default}}){{else}}return s.Struct.Pointer({{.Field.Slot.Offset}}){{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) {{.Field.Name | title}}Ptr() ({{.G.Capnp}}.Ptr, error) {\n\t{{if .Default.IsValid}}p, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn p.Default({{.Default}}){{else}}return s.Struct.Ptr({{.Field.Slot.Offset}}){{end}}\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.G.Capnp}}.Pointer) error {\n\t{{template \"_settag\" .}}return s.Struct.SetPointer({{.Field.Slot.Offset}}, v)\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}Ptr(v {{.G.Capnp}}.Ptr) error {\n\t{{template \"_settag\" .}}return s.Struct.SetPtr({{.Field.Slot.Offset}}, v)\n}\n\n{{end}}{{define \"structStructField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() ({{.FieldType}}, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\t{{if .Default.IsValid}}ss, err := p.StructDefault({{.Default}})\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\treturn {{.FieldType}}{Struct: ss}, nil{{else}}return {{.FieldType}}{Struct: p.Struct()}, nil{{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.FieldType}}) error {\n\t{{template \"_settag\" .}}return s.Struct.SetPtr({{.Field.Slot.Offset}}, v.Struct.ToPtr())\n}\n\n// New{{.Field.Name | title}} sets the {{.Field.Name}} field to a newly\n// allocated {{.FieldType}} struct, preferring placement in s's segment.\nfunc (s {{.Node.Name}}) New{{.Field.Name | title}}() ({{.FieldType}}, error) {\n\t{{template \"_settag\" .}}ss, err := {{.G.RemoteNodeNew .TypeNode .Node}}(s.Struct.Segment())\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\terr = s.Struct.SetPtr({{.Field.Slot.Offset}}, ss.Struct.ToPtr())\n\treturn ss, err\n}\n\n{{end}}{{define \"structTextField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() (string, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\t{{with .Default}}return p.TextDefault({{printf \"%q\" .}}), nil{{else}}return p.Text(), nil{{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) {{.Field.Name | title}}Bytes() ([]byte, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t{{with .Default}}return p.DataDefault([]byte({{printf \"%q\" .}})), nil{{else}}return p.Data(), nil{{end}}\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v string) error {\n\t{{template \"_settag\" .}}t, err := {{.G.Capnp}}.NewText(s.Struct.Segment(), v)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn s.Struct.SetPtr({{.Field.Slot.Offset}}, t.List.ToPtr())\n}\n\n{{end}}{{define \"structTypes\"}}{{with .Annotations.Doc}}// {{.}}\n{{end}}type {{.Node.Name}} {{if .IsBase}}struct{ {{.G.Capnp}}.Struct }{{else}}{{.BaseNode.Name}}{{end}}\n{{end}}{{define \"structUintField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() uint{{.Bits}} {\n\treturn s.Struct.Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{.}}{{end}}\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v uint{{.Bits}}) {\n\t{{template \"_settag\" .}}s.Struct.SetUint{{.Bits}}({{.Offset}}, v{{with .Default}}^{{.}}{{end}})\n}\n\n{{end}}{{define \"structValue\"}}{{.G.RemoteNodeName .Typ .Node}}{Struct: {{.G.Capnp}}.MustUnmarshalRootPtr({{.Value}}).Struct()}{{end}}{{define \"structVoidField\"}}{{if .Field.HasDiscriminant}}func (s {{.Node.Name}}) Set{{.Field.Name | title}}() {\n\t{{template \"_settag\" .}}\n}\n\n{{end}}{{end}}")) + "{{define \"_hasfield\"}}func (s {{.Node.Name}}) Has{{.Field.Name | title}}() bool {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\treturn p.IsValid() || err != nil \n}\n{{end}}{{define \"_interfaceMethod\"}}\t\t\tInterfaceID: {{.Interface.Id | printf \"%#x\"}},\n\t\t\tMethodID: {{.ID}},\n\t\t\tInterfaceName: {{.Interface.DisplayName | printf \"%q\"}},\n\t\t\tMethodName: {{.OriginalName | printf \"%q\"}},\n{{end}}{{define \"_settag\"}}{{if .Field.HasDiscriminant}}s.Struct.SetUint16({{.Node.DiscriminantOffset}}, {{.Field.DiscriminantValue}})\n{{end}}{{end}}{{define \"annotation\"}}const {{.Node.Name}} = uint64({{.Node.Id | printf \"%#x\"}})\n{{end}}{{define \"constants\"}}{{with .Consts}}// Constants defined in {{$.G.Basename}}.\nconst (\n{{range .}}\t{{.Name}} = {{$.G.Value . .Const.Type .Const.Value}}\n{{end}}\n)\n{{end}}\n{{with .Vars}}// Constants defined in {{$.G.Basename}}.\nvar (\n{{range .}}\t{{.Name}} = {{$.G.Value . .Const.Type .Const.Value}}\n{{end}}\n)\n{{end}}\n{{end}}{{define \"enum\"}}{{with .Annotations.Doc}}// {{.}}\n{{end}}type {{.Node.Name}} uint16\n\n{{with .EnumValues}}// Values of {{$.Node.Name}}.\nconst (\n{{range .}}{{.FullName}} {{$.Node.Name}} = {{.Val}}\n{{end}}\n)\n\n// String returns the enum's constant name.\nfunc (c {{$.Node.Name}}) String() string {\n\tswitch c {\n\t{{range .}}{{if .Tag}}case {{.FullName}}: return {{printf \"%q\" .Tag}}\n\t{{end}}{{end}}\n\tdefault: return \"\"\n\t}\n}\n\n// {{$.Node.Name}}FromString returns the enum value with a name,\n// or the zero value if there's no such value.\nfunc {{$.Node.Name}}FromString(c string) {{$.Node.Name}} {\n\tswitch c {\n\t{{range .}}{{if .Tag}}case {{printf \"%q\" .Tag}}: return {{.FullName}}\n\t{{end}}{{end}}\n\tdefault: return 0\n\t}\n}\n{{end}}\n\ntype {{.Node.Name}}_List struct { {{$.G.Capnp}}.List }\n\nfunc New{{.Node.Name}}_List(s *{{$.G.Capnp}}.Segment, sz int32) ({{.Node.Name}}_List, error) {\n\tl, err := {{.G.Capnp}}.NewUInt16List(s, sz)\n\tif err != nil {\n\t\treturn {{.Node.Name}}_List{}, err\n\t}\n\treturn {{.Node.Name}}_List{l.List}, nil\n}\n\nfunc (l {{.Node.Name}}_List) At(i int) {{.Node.Name}} {\n\tul := {{.G.Capnp}}.UInt16List{List: l.List}\n\treturn {{.Node.Name}}(ul.At(i))\n}\n\nfunc (l {{.Node.Name}}_List) Set(i int, v {{.Node.Name}}) {\n\tul := {{.G.Capnp}}.UInt16List{List: l.List}\n\tul.Set(i, uint16(v))\n}\n{{end}}{{define \"interfaceClient\"}}{{with .Annotations.Doc}}// {{.}}\n{{end}}type {{.Node.Name}} struct { Client {{.G.Capnp}}.Client }\n\n{{range .Methods}}func (c {{$.Node.Name}}) {{.Name | title}}(ctx {{$.G.Imports.Context}}.Context, params func({{$.G.RemoteNodeName .Params $.Node}}) error, opts ...{{$.G.Capnp}}.CallOption) {{$.G.RemoteNodeName .Results $.Node}}_Promise {\n\tif c.Client == nil {\n\t\treturn {{$.G.RemoteNodeName .Results $.Node}}_Promise{Pipeline: {{$.G.Capnp}}.NewPipeline({{$.G.Capnp}}.ErrorAnswer({{$.G.Capnp}}.ErrNullClient))}\n\t}\n\tcall := &{{$.G.Capnp}}.Call{\n\t\tCtx: ctx,\n\t\tMethod: {{$.G.Capnp}}.Method{\n\t\t\t{{template \"_interfaceMethod\" .}}\n\t\t},\n\t\tOptions: {{$.G.Capnp}}.NewCallOptions(opts),\n\t}\n\tif params != nil {\n\t\tcall.ParamsSize = {{$.G.ObjectSize .Params}}\n\t\tcall.ParamsFunc = func(s {{$.G.Capnp}}.Struct) error { return params({{$.G.RemoteNodeName .Params $.Node}}{Struct: s}) }\n\t}\n\treturn {{$.G.RemoteNodeName .Results $.Node}}_Promise{Pipeline: {{$.G.Capnp}}.NewPipeline(c.Client.Call(call))}\n}\n{{end}}\n{{end}}{{define \"interfaceServer\"}}type {{.Node.Name}}_Server interface {\n\t{{range .Methods}}\n\t{{.Name | title}}({{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}) error\n\t{{end}}\n}\n\nfunc {{.Node.Name}}_ServerToClient(s {{.Node.Name}}_Server) {{.Node.Name}} {\n\tc, _ := s.({{.G.Imports.Server}}.Closer)\n\treturn {{.Node.Name}}{Client: {{.G.Imports.Server}}.New({{.Node.Name}}_Methods(nil, s), c)}\n}\n\nfunc {{.Node.Name}}_Methods(methods []{{.G.Imports.Server}}.Method, s {{.Node.Name}}_Server) []{{.G.Imports.Server}}.Method {\n\tif cap(methods) == 0 {\n\t\tmethods = make([]{{.G.Imports.Server}}.Method, 0, {{len .Methods}})\n\t}\n\t{{range .Methods}}\n\tmethods = append(methods, {{$.G.Imports.Server}}.Method{\n\t\tMethod: {{$.G.Capnp}}.Method{\n\t\t\t{{template \"_interfaceMethod\" .}}\n\t\t},\n\t\tImpl: func(c {{$.G.Imports.Context}}.Context, opts {{$.G.Capnp}}.CallOptions, p, r {{$.G.Capnp}}.Struct) error {\n\t\t\tcall := {{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}{c, opts, {{$.G.RemoteNodeName .Params $.Node}}{Struct: p}, {{$.G.RemoteNodeName .Results $.Node}}{Struct: r} }\n\t\t\treturn s.{{.Name | title}}(call)\n\t\t},\n\t\tResultsSize: {{$.G.ObjectSize .Results}},\n\t})\n\t{{end}}\n\treturn methods\n}\n{{range .Methods}}{{if eq .Interface.Id $.Node.Id}}\n// {{$.Node.Name}}_{{.Name}} holds the arguments for a server call to {{$.Node.Name}}.{{.Name}}.\ntype {{$.Node.Name}}_{{.Name}} struct {\n\tCtx {{$.G.Imports.Context}}.Context\n\tOptions {{$.G.Capnp}}.CallOptions\n\tParams {{$.G.RemoteNodeName .Params $.Node}}\n\tResults {{$.G.RemoteNodeName .Results $.Node}}\n}\n{{end}}{{end}}\n{{end}}{{define \"listValue\"}}{{.Typ}}{List: {{.G.Capnp}}.MustUnmarshalRootPtr({{.Value}}).List()}{{end}}{{define \"newStructFunc\"}}func New{{.Node.Name}}(s *{{.G.Capnp}}.Segment) ({{.Node.Name}}, error) {\n\tst, err := {{$.G.Capnp}}.NewStruct(s, {{.G.ObjectSize .Node}})\n\tif err != nil {\n\t\treturn {{.Node.Name}}{}, err\n\t}\n\treturn {{.Node.Name}}{st}, nil\n}\n\nfunc NewRoot{{.Node.Name}}(s *{{.G.Capnp}}.Segment) ({{.Node.Name}}, error) {\n\tst, err := {{.G.Capnp}}.NewRootStruct(s, {{.G.ObjectSize .Node}})\n\tif err != nil {\n\t\treturn {{.Node.Name}}{}, err\n\t}\n\treturn {{.Node.Name}}{st}, nil\n}\n\nfunc ReadRoot{{.Node.Name}}(msg *{{.G.Capnp}}.Message) ({{.Node.Name}}, error) {\n\troot, err := msg.RootPtr()\n\tif err != nil {\n\t\treturn {{.Node.Name}}{}, err\n\t}\n\treturn {{.Node.Name}}{root.Struct()}, nil\n}\n{{end}}{{define \"pointerValue\"}}{{.G.Capnp}}.MustUnmarshalRootPtr({{.Value}}){{end}}{{define \"promise\"}}// {{.Node.Name}}_Promise is a wrapper for a {{.Node.Name}} promised by a client call.\ntype {{.Node.Name}}_Promise struct { *{{.G.Capnp}}.Pipeline }\n\nfunc (p {{.Node.Name}}_Promise) Struct() ({{.Node.Name}}, error) {\n\ts, err := p.Pipeline.Struct()\n\treturn {{.Node.Name}}{s}, err\n}\n\n{{end}}{{define \"promiseFieldAnyPointer\"}}func (p {{.Node.Name}}_Promise) {{.Field.Name | title}}() *{{.G.Capnp}}.Pipeline {\n\treturn p.Pipeline.GetPipeline({{.Field.Slot.Offset}})\n}\n\n{{end}}{{define \"promiseFieldInterface\"}}func (p {{.Node.Name}}_Promise) {{.Field.Name | title}}() {{.G.RemoteNodeName .Interface .Node}} {\n\treturn {{.G.RemoteNodeName .Interface .Node}}{Client: p.Pipeline.GetPipeline({{.Field.Slot.Offset}}).Client()}\n}\n\n{{end}}{{define \"promiseFieldStruct\"}}func (p {{.Node.Name}}_Promise) {{.Field.Name | title}}() {{.G.RemoteNodeName .Struct .Node}}_Promise {\n\treturn {{.G.RemoteNodeName .Struct .Node}}_Promise{Pipeline: p.Pipeline.{{if .Default.IsValid}}GetPipelineDefault({{.Field.Slot.Offset}}, {{.Default}}){{else}}GetPipeline({{.Field.Slot.Offset}}){{end}} }\n}\n\n{{end}}{{define \"promiseGroup\"}}func (p {{.Node.Name}}_Promise) {{.Field.Name | title}}() {{.Group.Name}}_Promise { return {{.Group.Name}}_Promise{p.Pipeline} }\n{{end}}{{define \"structBoolField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() bool {\n\treturn {{if .Default}}!{{end}}s.Struct.Bit({{.Field.Slot.Offset}})\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v bool) {\n\t{{template \"_settag\" .}}s.Struct.SetBit({{.Field.Slot.Offset}}, {{if .Default}}!{{end}}v)\n}\n\n{{end}}{{define \"structDataField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() ({{.FieldType}}, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t{{with .Default}}return {{$.FieldType}}(p.DataDefault({{printf \"%#v\" .}})), nil{{else}}return {{.FieldType}}(p.Data()), nil{{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.FieldType}}) error {\n\t{{template \"_settag\" .}}d, err := {{.G.Capnp}}.NewData(s.Struct.Segment(), []byte(v))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn s.Struct.SetPtr({{.Field.Slot.Offset}}, d.List.ToPtr())\n}\n\n{{end}}{{define \"structEnums\"}}type {{.Node.Name}}_Which uint16\n\nconst (\n{{range .Fields}}\t{{$.Node.Name}}_Which_{{.Name}} {{$.Node.Name}}_Which = {{.DiscriminantValue}}\n{{end}}\n)\n\nfunc (w {{.Node.Name}}_Which) String() string {\n\tconst s = {{.EnumString.ValueString | printf \"%q\"}}\n\tswitch w {\n\t{{range $i, $f := .Fields}}case {{$.Node.Name}}_Which_{{.Name}}:\n\t\treturn s{{$.EnumString.SliceFor $i}}\n\t{{end}}\n\t}\n\treturn \"{{.Node.Name}}_Which(\" + {{.G.Imports.Strconv}}.FormatUint(uint64(w), 10) + \")\"\n}\n\n{{end}}{{define \"structFloatField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() float{{.Bits}} {\n\treturn {{.G.Imports.Math}}.Float{{.Bits}}frombits(s.Struct.Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{printf \"%#x\" .}}{{end}})\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v float{{.Bits}}) {\n\t{{template \"_settag\" .}}s.Struct.SetUint{{.Bits}}({{.Offset}}, {{.G.Imports.Math}}.Float{{.Bits}}bits(v){{with .Default}}^{{printf \"%#x\" .}}{{end}})\n}\n\n{{end}}{{define \"structFuncs\"}}{{if gt .Node.StructNode.DiscriminantCount 0}}\nfunc (s {{.Node.Name}}) Which() {{.Node.Name}}_Which {\n\treturn {{.Node.Name}}_Which(s.Struct.Uint16({{.Node.DiscriminantOffset}}))\n}\n{{end}}{{end}}{{define \"structGroup\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() {{.Group.Name}} { return {{.Group.Name}}(s) }{{if .Field.HasDiscriminant}}\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}() { {{template \"_settag\" .}} }\n{{end}}{{end}}{{define \"structIntField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() {{.ReturnType}} {\n\treturn {{.ReturnType}}(s.Struct.Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{.}}{{end}})\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.ReturnType}}) {\n\t{{template \"_settag\" .}}s.Struct.SetUint{{.Bits}}({{.Offset}}, uint{{.Bits}}(v){{with .Default}}^{{.}}{{end}})\n}\n\n{{end}}{{define \"structInterfaceField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() {{.FieldType}} {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\t\n\t\treturn {{.FieldType}}{}\n\t}\n\treturn {{.FieldType}}{Client: p.Interface().Client()}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.FieldType}}) error {\n\t{{template \"_settag\" .}}seg := s.Segment()\n\tif seg == nil {\n\t\t\n\t\treturn nil\n\t}\n\tvar in capnp.Interface\n\tif v.Client != nil {\n\t\tin = {{.G.Capnp}}.NewInterface(seg, seg.Message().AddCap(v.Client))\n\t}\n\treturn s.Struct.SetPtr({{.Field.Slot.Offset}}, in.ToPtr())\n}\n\n{{end}}{{define \"structList\"}}// {{.Node.Name}}_List is a list of {{.Node.Name}}.\ntype {{.Node.Name}}_List struct{ {{.G.Capnp}}.List }\n\n// New{{.Node.Name}} creates a new list of {{.Node.Name}}.\nfunc New{{.Node.Name}}_List(s *{{.G.Capnp}}.Segment, sz int32) ({{.Node.Name}}_List, error) {\n\tl, err := {{.G.Capnp}}.NewCompositeList(s, {{.G.ObjectSize .Node}}, sz)\n\tif err != nil {\n\t\treturn {{.Node.Name}}_List{}, err\n\t}\n\treturn {{.Node.Name}}_List{l}, nil\n}\n\nfunc (s {{.Node.Name}}_List) At(i int) {{.Node.Name}} { return {{.Node.Name}}{ s.List.Struct(i) } }\nfunc (s {{.Node.Name}}_List) Set(i int, v {{.Node.Name}}) error { return s.List.SetStruct(i, v.Struct) }\n{{end}}{{define \"structListField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() ({{.FieldType}}, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\t{{if .Default.IsValid}}l, err := p.ListDefault({{.Default}})\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\treturn {{.FieldType}}{List: l}, nil{{else}}return {{.FieldType}}{List: p.List()}, nil{{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.FieldType}}) error {\n\t{{template \"_settag\" .}}return s.Struct.SetPtr({{.Field.Slot.Offset}}, v.List.ToPtr())\n}\n\n// New{{.Field.Name | title}} sets the {{.Field.Name}} field to a newly\n// allocated {{.FieldType}}, preferring placement in s's segment.\nfunc (s {{.Node.Name}}) New{{.Field.Name | title}}(n int32) ({{.FieldType}}, error) {\n\t{{template \"_settag\" .}}l, err := {{.G.RemoteTypeNew .Field.Slot.Type .Node}}(s.Struct.Segment(), n)\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\terr = s.Struct.SetPtr({{.Field.Slot.Offset}}, l.List.ToPtr())\n\treturn l, err\n}\n\n{{end}}{{define \"structPointerField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() ({{.G.Capnp}}.Pointer, error) {\n\t{{if .Default.IsValid}}p, err := s.Struct.Pointer({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn {{.G.Capnp}}.PointerDefault(p, {{.Default}}){{else}}return s.Struct.Pointer({{.Field.Slot.Offset}}){{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) {{.Field.Name | title}}Ptr() ({{.G.Capnp}}.Ptr, error) {\n\t{{if .Default.IsValid}}p, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn p.Default({{.Default}}){{else}}return s.Struct.Ptr({{.Field.Slot.Offset}}){{end}}\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.G.Capnp}}.Pointer) error {\n\t{{template \"_settag\" .}}return s.Struct.SetPointer({{.Field.Slot.Offset}}, v)\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}Ptr(v {{.G.Capnp}}.Ptr) error {\n\t{{template \"_settag\" .}}return s.Struct.SetPtr({{.Field.Slot.Offset}}, v)\n}\n\n{{end}}{{define \"structStructField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() ({{.FieldType}}, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\t{{if .Default.IsValid}}ss, err := p.StructDefault({{.Default}})\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\treturn {{.FieldType}}{Struct: ss}, nil{{else}}return {{.FieldType}}{Struct: p.Struct()}, nil{{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v {{.FieldType}}) error {\n\t{{template \"_settag\" .}}return s.Struct.SetPtr({{.Field.Slot.Offset}}, v.Struct.ToPtr())\n}\n\n// New{{.Field.Name | title}} sets the {{.Field.Name}} field to a newly\n// allocated {{.FieldType}} struct, preferring placement in s's segment.\nfunc (s {{.Node.Name}}) New{{.Field.Name | title}}() ({{.FieldType}}, error) {\n\t{{template \"_settag\" .}}ss, err := {{.G.RemoteNodeNew .TypeNode .Node}}(s.Struct.Segment())\n\tif err != nil {\n\t\treturn {{.FieldType}}{}, err\n\t}\n\terr = s.Struct.SetPtr({{.Field.Slot.Offset}}, ss.Struct.ToPtr())\n\treturn ss, err\n}\n\n{{end}}{{define \"structTextField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() (string, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\t{{with .Default}}return p.TextDefault({{printf \"%q\" .}}), nil{{else}}return p.Text(), nil{{end}}\n}\n\n{{template \"_hasfield\" .}}\n\nfunc (s {{.Node.Name}}) {{.Field.Name | title}}Bytes() ([]byte, error) {\n\tp, err := s.Struct.Ptr({{.Field.Slot.Offset}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t{{with .Default}}return p.DataDefault([]byte({{printf \"%q\" .}})), nil{{else}}return p.Data(), nil{{end}}\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v string) error {\n\t{{template \"_settag\" .}}t, err := {{.G.Capnp}}.NewText(s.Struct.Segment(), v)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn s.Struct.SetPtr({{.Field.Slot.Offset}}, t.List.ToPtr())\n}\n\n{{end}}{{define \"structTypes\"}}{{with .Annotations.Doc}}// {{.}}\n{{end}}type {{.Node.Name}} {{if .IsBase}}struct{ {{.G.Capnp}}.Struct }{{else}}{{.BaseNode.Name}}{{end}}\n{{end}}{{define \"structUintField\"}}func (s {{.Node.Name}}) {{.Field.Name | title}}() uint{{.Bits}} {\n\treturn s.Struct.Uint{{.Bits}}({{.Offset}}){{with .Default}} ^ {{.}}{{end}}\n}\n\nfunc (s {{.Node.Name}}) Set{{.Field.Name | title}}(v uint{{.Bits}}) {\n\t{{template \"_settag\" .}}s.Struct.SetUint{{.Bits}}({{.Offset}}, v{{with .Default}}^{{.}}{{end}})\n}\n\n{{end}}{{define \"structValue\"}}{{.G.RemoteNodeName .Typ .Node}}{Struct: {{.G.Capnp}}.MustUnmarshalRootPtr({{.Value}}).Struct()}{{end}}{{define \"structVoidField\"}}{{if .Field.HasDiscriminant}}func (s {{.Node.Name}}) Set{{.Field.Name | title}}() {\n\t{{template \"_settag\" .}}\n}\n\n{{end}}{{end}}")) func renderAnnotation(r renderer, p annotationParams) error { return r.Render("annotation", p) diff --git a/capnpc-go/templates/interfaceClient b/capnpc-go/templates/interfaceClient index ea591736..7997ea02 100644 --- a/capnpc-go/templates/interfaceClient +++ b/capnpc-go/templates/interfaceClient @@ -4,7 +4,7 @@ type {{.Node.Name}} struct { Client {{.G.Capnp}}.Client } {{range .Methods -}} -func (c {{$.Node.Name}}) {{.Name|title}}(ctx {{$.G.Imports.Context}}.Context, params func({{$.G.RemoteNodeName .Params $.Node}}) error, opts ...{{$.G.Capnp}}.CallOption) {{$.G.RemoteName .Results $.Node}}_Promise { +func (c {{$.Node.Name}}) {{.Name|title}}(ctx {{$.G.Imports.Context}}.Context, params func({{$.G.RemoteNodeName .Params $.Node}}) error, opts ...{{$.G.Capnp}}.CallOption) {{$.G.RemoteNodeName .Results $.Node}}_Promise { if c.Client == nil { return {{$.G.RemoteNodeName .Results $.Node}}_Promise{Pipeline: {{$.G.Capnp}}.NewPipeline({{$.G.Capnp}}.ErrorAnswer({{$.G.Capnp}}.ErrNullClient))} } diff --git a/capnpc-go/templates/interfaceServer b/capnpc-go/templates/interfaceServer index d33c1f3d..ea8a263d 100644 --- a/capnpc-go/templates/interfaceServer +++ b/capnpc-go/templates/interfaceServer @@ -19,7 +19,7 @@ func {{.Node.Name}}_Methods(methods []{{.G.Imports.Server}}.Method, s {{.Node.Na {{template "_interfaceMethod" .}} }, Impl: func(c {{$.G.Imports.Context}}.Context, opts {{$.G.Capnp}}.CallOptions, p, r {{$.G.Capnp}}.Struct) error { - call := {{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}{c, opts, {{$.G.RemoteName .Params $.Node}}{Struct: p}, {{$.G.RemoteName .Results $.Node}}{Struct: r} } + call := {{$.G.RemoteNodeName .Interface $.Node}}_{{.Name}}{c, opts, {{$.G.RemoteNodeName .Params $.Node}}{Struct: p}, {{$.G.RemoteNodeName .Results $.Node}}{Struct: r} } return s.{{.Name|title}}(call) }, ResultsSize: {{$.G.ObjectSize .Results}}, diff --git a/capnpc-go/testdata/util.capnp b/capnpc-go/testdata/util.capnp new file mode 100644 index 00000000..c3a79d27 --- /dev/null +++ b/capnpc-go/testdata/util.capnp @@ -0,0 +1,173 @@ +# Sandstorm - Personal Cloud Sandbox +# Copyright (c) 2014 Sandstorm Development Group, Inc. and contributors +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Derived from sandstorm at 08fafe55995edfe188777eb00b5a2d1826032c59. +# Using Go annotations. + +@0xecd50d792c3d9992; + +using Go = import "go.capnp"; +$Go.package("util"); +$Go.import("zombiezen.com/go/capnproto2/capnpc-go/testdata/util"); + +using DateInNs = Int64; +using DurationInNs = UInt64; + +struct KeyValue { + key @0 :Text; + value @1 :Text; +} + +struct LocalizedText { + # Text intended to be displayed to a user. May be localized to multiple languages. + # + # TODO(soon): Maybe instead of packing all translations in here, we should have a message code + # and parameter substitutions, with the (message code, locale) -> text map stored elsewhere? + + defaultText @0 :Text; + # What to display if no localization matching the user's preferences is available. + + localizations @1 :List(Localization); + # Localized versions of the text. + + struct Localization { + locale @0 :Text; # IETF BCP 47 locale, e.g. "en" or "en-US". + text @1 :Text; # Localized text. + } +} + +interface Handle { + # Arbitrary handle to some resource provided by the platform. May or may not be persistent, + # depending on the use case. + # + # To "drop" a handle means to discard any references. The purpose of a handle is to detect when + # it has been dropped and to free the underlying resource and cancel any ongoing operation at + # that time. + # + # A handle can be persistent. Once you have called `save()` on it to obtain a SturdyRef, dropping + # the live reference will not cancel the operation. You must drop all live references *and* + # explicitly drop any SturdyRef. Every interface which supports restoring SturdyRefs also + # has a corresponding `drop()` method for this purpose. + # + # Unfortunately, there is no way to ensure that a SturdyRef will eventually be deleted. A grain + # could, for example, call `save()` and then simply fail to store the SturdyRef anywhere, causing + # it to be "leaked" until such a time as the grain itself is destroyed. Or worse, a whole server + # could be destroyed in a fire, leaking all SturdyRefs stored therein forever. Apps implementing + # persistent handles must be designed to account for this, probably by giving the owning user + # a way to inspect incoming references and remove them manually. Sandstorm automatically provides + # such an interface for all apps it hosts. +} + +interface ByteStream { + # Represents a destination for a stream of bytes. The bytes are ordered, but boundaries between + # messages are not semantically important. + # + # Streams are push-oriented (traditionally, "output streams") rather than pull-oriented ("input + # streams") because this most easily allows multiple packets to be in-flight at once while + # allowing flow control at either end. If we tried to design a pull-oriented stream, it would + # suffer from problems: + # * If we used a naive read() method that returns a simple data blob, you would need to make + # multiple simultaneous calls to deal with network latency. However, those calls could + # potentially return in the wrong order. Although you could restore order by keeping track of + # the order in which the calls were made, this would be a lot of work, and most callers would + # probably fail to do it. + # * We could instead have a read() method that returns a blob as well as a capability to read the + # next blob. You would then make multiple calls using pipelining. Even in this case, though, + # an unpredictable event loop could schedule a pipelined call's return before the parent call. + # Moreover, the interface would be awkward to use and implement. E.g. what happens if you call + # read() twice on the same capability? + + write @0 (data :Data); + # Add bytes. + # + # It's safe to make overlapping calls to `write()`, since Cap'n Proto enforces E-Order and so + # the calls will be delivered in order. However, it is a good idea to limit how much data is + # in-flight at a time, so that it doesn't fill up buffers and block other traffic. On the other + # hand, having only one `write()` in flight at a time will not fully utilize the available + # bandwidth if the connection has any significant latency, so parallelizing a few `write()`s is + # a good idea. + # + # Similarly, the implementation of `ByteStream` can delay returning from `write()` as a way to + # hint to the caller that it should hold off on further writes. + + done @1 (); + # Call after the last write to indicate that there is no more data. If the `ByteStream` is + # discarded without a call to `done()`, the callee must assume that an error occurred and that + # the data is incomplete. + # + # This will not return until all bytes are successfully written to their final destination. + # It will throw an exception if any error occurs, including if the total number of bytes written + # did not match `expectSize()`. + + expectSize @2 (size :UInt64); + # Optionally called to let the receiver know exactly how much data will be written. This should + # normally be called before the first write(), but if called later, `size` indicates how many + # more bytes to expect _after_ the call. It is an error by the caller if more or fewer bytes are + # actually written before `done()`; this also implies that all calls to `expectSize()` must be + # consistent. The caller will ignore any exceptions thrown from this method, therefore it + # is not necessary for the callee to actually implement it. +} + +interface Blob @0xe53527a75d90198f { + # Represents a large byte blob. + + getSize @0 () -> (size :UInt64); + # Get the total size of the blob. May block if the blob is still being uploaded and the size is + # not yet known. + + writeTo @1 (stream :ByteStream, startAtOffset :UInt64 = 0) -> (handle :Handle); + # Write the contents of the blob to `stream`. + + getSlice @2 (offset :UInt64, size :UInt32) -> (data :Data); + # Read a slice of the blob starting at the given offset. `size` cannot be greater than Cap'n + # Proto's limit of 2^29-1, and reasonable servers will likely impose far lower limits. If the + # slice would cross past the end of the blob, it is truncated. Otherwise, `data` is always + # exactly `size` bytes (though the caller should check for security purposes). + # + # One technique that makes a lot of sense is to start off by calling e.g. `getSlice(0, 65536)`. + # If the returned data is less than 65536 bytes then you know you got the whole blob, otherwise + # you may want to switch to `writeTo`. +} + +interface Assignable(T) { + # An "assignable" -- a mutable memory cell. Supports subscribing to updates. + + get @0 () -> (value :T, setter :Setter); + # The returned setter functions the same as you'd get from `asSetter()` except that it will + # become disconnected the next time the Assignable is set by someone else. Thus, you may use this + # to implement optimistic concurrency control. + + asGetter @1 () -> (getter :Getter); + # Return a read-only capability for this assignable, co-hosted with the assignable itself for + # performance. If the assignable is persistent, the getter is as well. + + asSetter @2 () -> (setter :Setter); + # Return a write-only capability for this assignable, co-hosted with the assignable itself for + # performance. If the assignable is persistent, the setter is as well. + + interface Getter { + get @0 () -> (value :T); + + subscribe @1 (setter :Setter) -> (handle :Handle); + # Subscribe to updates. Calls the given setter any time the assignable's value changes. Drop + # the returned handle to stop receiving updates. If `setter` is persistent, `handle` will also + # be persistent. + } + + interface Setter { + set @0 (value :T) -> (); + } +} diff --git a/capnpc-go/testdata/util.capnp.out b/capnpc-go/testdata/util.capnp.out new file mode 100644 index 00000000..2347896a Binary files /dev/null and b/capnpc-go/testdata/util.capnp.out differ