/* capnpc-go is the Cap'n proto code generator for Go. It reads a CodeGeneratorRequest from stdin and for a file foo.capnp it writes foo.capnp.go. This is usually invoked from `capnp compile -ogo`. See https://capnproto.org/otherlang.html#how-to-write-compiler-plugins for more details. */ package main import ( "bytes" "compress/zlib" "errors" "flag" "fmt" "go/format" "math" "os" "path/filepath" "sort" "strconv" "strings" "text/template" "zombiezen.com/go/capnproto2" "zombiezen.com/go/capnproto2/internal/schema" ) // Non-stdlib import paths. const ( capnpImport = "zombiezen.com/go/capnproto2" textImport = capnpImport + "/encoding/text" schemasImport = capnpImport + "/schemas" serverImport = capnpImport + "/server" contextImport = "golang.org/x/net/context" ) // genoptions are parameters that control code generation. // Usually passed on the command line. type genoptions struct { promises bool schemas bool structStrings bool } type renderer interface { Render(name string, params interface{}) error Bytes() []byte } type templateRenderer struct { buf bytes.Buffer t *template.Template } // Render calls ExecuteTemplate to render to its buffer. func (tr *templateRenderer) Render(name string, params interface{}) error { return tr.t.ExecuteTemplate(&tr.buf, name, params) } // Bytes returns the accumulated bytes. func (tr *templateRenderer) Bytes() []byte { return tr.buf.Bytes() } // generator builds up the generated code for a single file. type generator struct { r renderer fileID uint64 nodes nodeMap imports imports data staticData opts genoptions } func newGenerator(fileID uint64, nodes nodeMap, opts genoptions) *generator { g := &generator{ r: &templateRenderer{t: templates}, fileID: fileID, nodes: nodes, opts: opts, } g.imports.init() g.data.init(fileID) return g } // Basename returns the name of the schema file with the directory name removed. func (g *generator) Basename() (string, error) { f, err := g.nodes.mustFind(g.fileID) if err != nil { return "", err } dn, err := f.DisplayName() if err != nil { return "", err } return filepath.Base(dn), nil } func (g *generator) Imports() *imports { return &g.imports } func (g *generator) Capnp() string { return g.imports.Capnp() } // generate produces unformatted Go source code from the nodes defined in it. func (g *generator) generate() []byte { var out bytes.Buffer out.WriteString("// Code generated by capnpc-go. DO NOT EDIT.\n\n") fmt.Fprintf(&out, "package %s\n\n", g.nodes[g.fileID].pkg) out.WriteString("import (\n") for _, imp := range g.imports.usedImports() { fmt.Fprintf(&out, "%v\n", imp) } out.WriteString(")\n") out.Write(g.r.Bytes()) if len(g.data.buf) > 0 { writeByteLiteral(&out, g.data.name, g.data.buf) } return out.Bytes() } func writeByteLiteral(out *bytes.Buffer, name string, data []byte) { fmt.Fprintf(out, "var %s = []byte{", name) for i, b := range data { if i%8 == 0 { out.WriteByte('\n') } fmt.Fprintf(out, "%d,", b) } fmt.Fprintf(out, "\n}\n") } func (g *generator) defineSchemaVar() error { msg, seg, _ := capnp.NewMessage(capnp.SingleSegment(nil)) req, _ := schema.NewRootCodeGeneratorRequest(seg) fnodes := g.nodes[g.fileID].nodes ids := make([]uint64, len(fnodes)) for i, n := range fnodes { ids[i] = n.Id() } sort.Sort(uint64Slice(ids)) // TODO(light): find largest object size and use that to allocate list nodes, _ := req.NewNodes(int32(len(g.nodes))) i := 0 for _, id := range ids { n := g.nodes[id] if err := nodes.Set(i, n.Node); err != nil { return err } i++ } var buf bytes.Buffer z, _ := zlib.NewWriterLevel(&buf, zlib.BestCompression) if err := capnp.NewPackedEncoder(z).Encode(msg); err != nil { return err } if err := z.Close(); err != nil { return err } return renderSchemaVar(g.r, schemaVarParams{ G: g, FileID: g.fileID, NodeIDs: ids, schema: buf.Bytes(), }) } // importForNode returns the import spec needed to reference n from // rel's scope. If none is needed (they are in the same package), then // the zero importSpec is returned. func importForNode(n, rel *node) (importSpec, error) { if n.pkg == "" { return importSpec{}, fmt.Errorf("internal error (bad schema?): missing package declaration for %s", n) } if n.imp == "" { return importSpec{}, fmt.Errorf("internal error (bad schema?): missing import declaration for %s", n) } if rel.imp == "" { return importSpec{}, fmt.Errorf("internal error (bad schema?): missing import declaration for %s", rel) } if n.imp == rel.imp { return importSpec{}, nil } return importSpec{path: n.imp, name: n.pkg}, nil } func (g *generator) RemoteNodeNew(n, rel *node) (string, error) { ref, err := makeNodeTypeRef(n, rel) if err != nil { return "", err } if ref.newfunc == "" { return "", fmt.Errorf("no new function for %s", ref.name) } if ref.imp.path == "" { return ref.newfunc, nil } qname := g.imports.add(ref.imp) return qname + "." + ref.newfunc, nil } func (g *generator) RemoteNodeName(n, rel *node) (string, error) { ref, err := makeNodeTypeRef(n, rel) if err != nil { return "", err } if ref.imp.path == "" { return ref.name, nil } qname := g.imports.add(ref.imp) return qname + "." + ref.name, nil } func (g *generator) RemoteTypeNew(t schema.Type, rel *node) (string, error) { ref, err := makeTypeRef(t, rel, g.nodes) if err != nil { return "", err } if ref.newfunc == "" { return "", fmt.Errorf("no new function for %s", ref.name) } if ref.imp.path == "" { return ref.newfunc, nil } qname := g.imports.add(ref.imp) return qname + "." + ref.newfunc, nil } func (g *generator) RemoteTypeName(t schema.Type, rel *node) (string, error) { ref, err := makeTypeRef(t, rel, g.nodes) if err != nil { return "", err } if ref.imp.path == "" { return ref.name, nil } qname := g.imports.add(ref.imp) return qname + "." + ref.name, nil } func (g *generator) defineEnum(n *node) error { es, _ := n.Enum().Enumerants() ev := make([]enumval, es.Len()) for i := 0; i < es.Len(); i++ { e := es.At(i) ev[e.CodeOrder()] = makeEnumval(n, i, e) } nann, _ := n.Annotations() err := renderEnum(g.r, enumParams{ G: g, Node: n, Annotations: parseAnnotations(nann), EnumValues: ev, }) if err != nil { return fmt.Errorf("enum %s: %v", n, err) } return nil } func isValueOfType(v schema.Value, t schema.Type) bool { // Ensure that the value is for the given type. The schema ensures the union ordinals match. return !v.IsValid() || int(v.Which()) == int(t.Which()) } // Value formats a value from a schema (like a field default) as Go source. func (g *generator) Value(rel *node, t schema.Type, v schema.Value) (string, error) { if !isValueOfType(v, t) { return "", fmt.Errorf("value type is %v, but found %v value", t.Which(), v.Which()) } switch t.Which() { case schema.Type_Which_void: return "struct{}{}", nil case schema.Type_Which_interface: // The only statically representable interface value is null. return g.imports.Capnp() + ".Client(nil)", nil case schema.Type_Which_bool: if v.Bool() { return "true", nil } else { return "false", nil } case schema.Type_Which_uint8, schema.Type_Which_uint16, schema.Type_Which_uint32, schema.Type_Which_uint64: return fmt.Sprintf("uint%d(%d)", intbits(t.Which()), uintValue(v)), nil case schema.Type_Which_int8, schema.Type_Which_int16, schema.Type_Which_int32, schema.Type_Which_int64: return fmt.Sprintf("int%d(%d)", intbits(t.Which()), intValue(v)), nil case schema.Type_Which_float32: return fmt.Sprintf("%s.Float32frombits(0x%x)", g.imports.Math(), math.Float32bits(v.Float32())), nil case schema.Type_Which_float64: return fmt.Sprintf("%s.Float64frombits(0x%x)", g.imports.Math(), math.Float64bits(v.Float64())), nil case schema.Type_Which_text: text, _ := v.Text() return strconv.Quote(text), nil case schema.Type_Which_data: buf := make([]byte, 0, 1024) buf = append(buf, "[]byte{"...) data, _ := v.Data() for i, b := range data { if i > 0 { buf = append(buf, ',', ' ') } buf = strconv.AppendUint(buf, uint64(b), 10) } buf = append(buf, '}') return string(buf), nil case schema.Type_Which_enum: en := g.nodes[t.Enum().TypeId()] if en == nil || !en.IsValid() || en.Which() != schema.Node_Which_enum { return "", errors.New("expected enum type") } enums, _ := en.Enum().Enumerants() val := int(v.Enum()) if val >= enums.Len() { rn, err := g.RemoteNodeName(en, rel) if err != nil { return "", err } return fmt.Sprintf("%s(%d)", rn, val), nil } ev := makeEnumval(en, val, enums.At(val)) imp, err := importForNode(en, rel) if err != nil { return "", err } if imp.path == "" { return ev.FullName(), nil } qname := g.imports.add(imp) return qname + "." + ev.FullName(), nil case schema.Type_Which_structType: data, _ := v.StructValuePtr() var buf bytes.Buffer tn, err := g.nodes.mustFind(t.StructType().TypeId()) if err != nil { return "", err } sd, err := g.data.copyData(data) if err != nil { return "", err } err = templates.ExecuteTemplate(&buf, "structValue", structValueParams{ G: g, Node: rel, Typ: tn, Value: sd, }) return buf.String(), err case schema.Type_Which_anyPointer: data, _ := v.AnyPointerPtr() var buf bytes.Buffer sd, err := g.data.copyData(data) if err != nil { return "", err } err = templates.ExecuteTemplate(&buf, "pointerValue", pointerValueParams{ G: g, Value: sd, }) return buf.String(), err case schema.Type_Which_list: data, _ := v.ListPtr() var buf bytes.Buffer ftyp, err := g.RemoteTypeName(t, rel) if err != nil { return "", err } sd, err := g.data.copyData(data) if err != nil { return "", err } err = templates.ExecuteTemplate(&buf, "listValue", listValueParams{ G: g, Typ: ftyp, Value: sd, }) return buf.String(), err default: return "", fmt.Errorf("unhandled value type %v", t.Which()) } } func (g *generator) defineAnnotation(n *node) error { err := renderAnnotation(g.r, annotationParams{ G: g, Node: n, }) if err != nil { return fmt.Errorf("annotation %s: %v", n, err) } return nil } func isGoConstType(t schema.Type) bool { w := t.Which() return w == schema.Type_Which_bool || w == schema.Type_Which_int8 || w == schema.Type_Which_uint8 || w == schema.Type_Which_int16 || w == schema.Type_Which_uint16 || w == schema.Type_Which_int32 || w == schema.Type_Which_uint32 || w == schema.Type_Which_int64 || w == schema.Type_Which_uint64 || w == schema.Type_Which_text || w == schema.Type_Which_enum } func (g *generator) defineConstNodes(nodes []*node) error { constNodes := make([]*node, 0, len(nodes)) for _, n := range nodes { if n.Which() != schema.Node_Which_const { continue } t, _ := n.Const().Type() if isGoConstType(t) { constNodes = append(constNodes, n) } } nc := len(constNodes) for _, n := range nodes { if n.Which() != schema.Node_Which_const { continue } t, _ := n.Const().Type() if !isGoConstType(t) { constNodes = append(constNodes, n) } } if len(constNodes) == 0 { // short path return nil } err := renderConstants(g.r, constantsParams{ G: g, Consts: constNodes[:nc], Vars: constNodes[nc:], }) if err != nil { return fmt.Errorf("file constants: %v", err) } return nil } func (g *generator) defineField(n *node, f field) (err error) { defer func() { if err != nil { err = fmt.Errorf("field %s.%s: %v", n.shortDisplayName(), f.Name, err) } }() fann, _ := f.Annotations() ann := parseAnnotations(fann) t, _ := f.Slot().Type() def, _ := f.Slot().DefaultValue() if !isValueOfType(def, t) { return fmt.Errorf("default value type is %v, but found %v value", t.Which(), def.Which()) } ftyp, err := g.RemoteTypeName(t, n) if err != nil { return err } params := structFieldParams{ G: g, Node: n, Field: f, Annotations: ann, FieldType: ftyp, } switch t.Which() { case schema.Type_Which_void: return renderStructVoidField(g.r, structVoidFieldParams(params)) case schema.Type_Which_bool: return renderStructBoolField(g.r, structBoolFieldParams{ structFieldParams: params, Default: def.Bool(), }) case schema.Type_Which_uint8, schema.Type_Which_uint16, schema.Type_Which_uint32, schema.Type_Which_uint64: return renderStructUintField(g.r, structUintFieldParams{ structFieldParams: params, Bits: intbits(t.Which()), Default: uintValue(def), }) case schema.Type_Which_int8, schema.Type_Which_int16, schema.Type_Which_int32, schema.Type_Which_int64: return renderStructIntField(g.r, structIntFieldParams{ structUintFieldParams: structUintFieldParams{ structFieldParams: params, Bits: intbits(t.Which()), Default: uint64(intFieldDefaultMask(def)), }, }) case schema.Type_Which_enum: rn, err := g.RemoteTypeName(t, n) if err != nil { return err } return renderStructIntField(g.r, structIntFieldParams{ structUintFieldParams: structUintFieldParams{ structFieldParams: params, Bits: 16, Default: uint64(def.Enum()), }, EnumName: rn, }) case schema.Type_Which_float32: return renderStructFloatField(g.r, structFloatFieldParams{ structFieldParams: params, Bits: 32, Default: uint64(math.Float32bits(def.Float32())), }) case schema.Type_Which_float64: return renderStructFloatField(g.r, structFloatFieldParams{ structFieldParams: params, Bits: 64, Default: math.Float64bits(def.Float64()), }) case schema.Type_Which_text: d, err := def.Text() if err != nil { return err } return renderStructTextField(g.r, structTextFieldParams{ structFieldParams: params, Default: d, }) case schema.Type_Which_data: d, err := def.Data() if err != nil { return err } return renderStructDataField(g.r, structDataFieldParams{ structFieldParams: params, Default: d, }) case schema.Type_Which_structType: var defref staticDataRef if sf, err := def.StructValuePtr(); err != nil { return err } else if sf.IsValid() { defref, err = g.data.copyData(sf) if err != nil { return err } } tn, err := g.nodes.mustFind(t.StructType().TypeId()) if err != nil { return err } return renderStructStructField(g.r, structStructFieldParams{ structFieldParams: params, TypeNode: tn, Default: defref, }) case schema.Type_Which_anyPointer: var defref staticDataRef if p, err := def.AnyPointerPtr(); err != nil { return err } else if p.IsValid() { defref, err = g.data.copyData(p) if err != nil { return err } } return renderStructPointerField(g.r, structPointerFieldParams{ structFieldParams: params, Default: defref, }) case schema.Type_Which_list: var defref staticDataRef if l, err := def.ListPtr(); err != nil { return err } else if l.IsValid() { defref, err = g.data.copyData(l) if err != nil { return err } } return renderStructListField(g.r, structListFieldParams{ structFieldParams: params, Default: defref, }) case schema.Type_Which_interface: return renderStructInterfaceField(g.r, structInterfaceFieldParams(params)) default: return fmt.Errorf("defining unhandled field type %v", t.Which()) } } // typeRef is a Go reference to a Cap'n Proto type. type typeRef struct { name string newfunc string // if absent, there is no New function for this type. imp importSpec // optional } func makeNodeTypeRef(n, rel *node) (typeRef, error) { imp, err := importForNode(n, rel) if err != nil { return typeRef{}, err } switch n.Which() { case schema.Node_Which_structNode: return typeRef{ name: n.Name, newfunc: "New" + n.Name, imp: imp, }, nil case schema.Node_Which_enum, schema.Node_Which_interface: return typeRef{ name: n.Name, imp: imp, }, nil } return typeRef{}, fmt.Errorf("unable to reference type of node %v", n.Which()) } var ( staticTypeRefs = map[schema.Type_Which]typeRef{ schema.Type_Which_void: {}, schema.Type_Which_bool: {name: "bool"}, schema.Type_Which_int8: {name: "int8"}, schema.Type_Which_int16: {name: "int16"}, schema.Type_Which_int32: {name: "int32"}, schema.Type_Which_int64: {name: "int64"}, schema.Type_Which_uint8: {name: "uint8"}, schema.Type_Which_uint16: {name: "uint16"}, schema.Type_Which_uint32: {name: "uint32"}, schema.Type_Which_uint64: {name: "uint64"}, schema.Type_Which_float32: {name: "float32"}, schema.Type_Which_float64: {name: "float64"}, schema.Type_Which_text: {name: "string"}, schema.Type_Which_data: {name: "[]byte"}, schema.Type_Which_anyPointer: {name: "Pointer", imp: capnpImportSpec}, } staticListTypeRefs = map[schema.Type_Which]typeRef{ // TODO(light): omitting newfunc since it doesn't have a similar type signature (no errors). schema.Type_Which_void: typeRef{name: "VoidList", imp: capnpImportSpec}, schema.Type_Which_bool: typeRef{name: "BitList", newfunc: "NewBitList", imp: capnpImportSpec}, schema.Type_Which_int8: typeRef{name: "Int8List", newfunc: "NewInt8List", imp: capnpImportSpec}, schema.Type_Which_uint8: typeRef{name: "UInt8List", newfunc: "NewUInt8List", imp: capnpImportSpec}, schema.Type_Which_int16: typeRef{name: "Int16List", newfunc: "NewInt16List", imp: capnpImportSpec}, schema.Type_Which_uint16: typeRef{name: "UInt16List", newfunc: "NewUInt16List", imp: capnpImportSpec}, schema.Type_Which_int32: typeRef{name: "Int32List", newfunc: "NewInt32List", imp: capnpImportSpec}, schema.Type_Which_uint32: typeRef{name: "UInt32List", newfunc: "NewUInt32List", imp: capnpImportSpec}, schema.Type_Which_int64: typeRef{name: "Int64List", newfunc: "NewInt64List", imp: capnpImportSpec}, schema.Type_Which_uint64: typeRef{name: "UInt64List", newfunc: "NewUInt64List", imp: capnpImportSpec}, schema.Type_Which_float32: typeRef{name: "Float32List", newfunc: "NewFloat32List", imp: capnpImportSpec}, schema.Type_Which_float64: typeRef{name: "Float64List", newfunc: "NewFloat64List", imp: capnpImportSpec}, schema.Type_Which_text: typeRef{name: "TextList", newfunc: "NewTextList", imp: capnpImportSpec}, schema.Type_Which_data: typeRef{name: "DataList", newfunc: "NewDataList", imp: capnpImportSpec}, } ) func makeTypeRef(t schema.Type, rel *node, nodes nodeMap) (typeRef, error) { nodeRef := func(id uint64) (typeRef, error) { ni, err := nodes.mustFind(id) if err != nil { return typeRef{}, err } return makeNodeTypeRef(ni, rel) } if ref, ok := staticTypeRefs[t.Which()]; ok { return ref, nil } switch t.Which() { case schema.Type_Which_enum: return nodeRef(t.Enum().TypeId()) case schema.Type_Which_structType: return nodeRef(t.StructType().TypeId()) case schema.Type_Which_interface: return nodeRef(t.Interface().TypeId()) case schema.Type_Which_list: lt, _ := t.List().ElementType() if ref, ok := staticListTypeRefs[lt.Which()]; ok { return ref, nil } switch lt.Which() { case schema.Type_Which_enum: ref, err := nodeRef(lt.Enum().TypeId()) if err != nil { return ref, err } ref.name = ref.name + "_List" ref.newfunc = "New" + ref.name return ref, nil case schema.Type_Which_structType: ref, err := nodeRef(lt.StructType().TypeId()) if err != nil { return ref, err } ref.name = ref.name + "_List" ref.newfunc = "New" + ref.name return ref, nil case schema.Type_Which_anyPointer, schema.Type_Which_list, schema.Type_Which_interface: return typeRef{name: "PointerList", newfunc: "NewPointerList", imp: capnpImportSpec}, nil } } return typeRef{}, fmt.Errorf("unable to reference type %v", t.Which()) } // intFieldDefaultMask returns the XOR mask used when getting or setting // signed integer struct fields. func intFieldDefaultMask(v schema.Value) uint64 { mask := uint64(1)< 0 { err := renderStructEnums(g.r, structEnumsParams{ G: g, Node: n, Fields: members, EnumString: es, }) if err != nil { return fmt.Errorf("struct enums for %s: %v", n, err) } } for _, f := range fields { if f.Which() == schema.Field_Which_group { grp, err := g.nodes.mustFind(f.Group().TypeId()) if err != nil { return err } if err := g.defineStructEnums(grp); err != nil { return err } } } return nil } func (g *generator) defineStructFuncs(n *node) error { err := renderStructFuncs(g.r, structFuncsParams{ G: g, Node: n, }) if err != nil { return fmt.Errorf("struct funcs for %s: %v", n, err) } for _, f := range n.codeOrderFields() { switch f.Which() { case schema.Field_Which_slot: if err := g.defineField(n, f); err != nil { return err } case schema.Field_Which_group: grp, err := g.nodes.mustFind(f.Group().TypeId()) if err != nil { return err } err = renderStructGroup(g.r, structGroupParams{ G: g, Node: n, Group: grp, Field: f, }) if err != nil { return fmt.Errorf("struct group for %s: %v", grp, err) } if err := g.defineStructFuncs(grp); err != nil { return err } } } return nil } func (g *generator) ObjectSize(n *node) (string, error) { if n.Which() != schema.Node_Which_structNode { return "", fmt.Errorf("object size called for %v node", n.Which()) } return fmt.Sprintf("%s.ObjectSize{DataSize: %d, PointerCount: %d}", g.imports.Capnp(), int(n.StructNode().DataWordCount())*8, n.StructNode().PointerCount()), nil } func (g *generator) defineBaseStructFuncs(n *node) error { err := renderBaseStructFuncs(g.r, baseStructFuncsParams{ G: g, Node: n, StringMethod: g.opts.structStrings, }) if err != nil { return fmt.Errorf("base struct functions for %s: %v", n, err) } return nil } func (g *generator) defineStructList(n *node) error { err := renderStructList(g.r, structListParams{ G: g, Node: n, StringMethod: g.opts.structStrings, }) if err != nil { return fmt.Errorf("new struct function for %s: %v", n, err) } return nil } func (g *generator) defineStructPromise(n *node) error { err := renderPromise(g.r, promiseParams{ G: g, Node: n, Fields: n.codeOrderFields(), }) if err != nil { return fmt.Errorf("promise for struct %s: %v", n, err) } for _, f := range n.codeOrderFields() { switch f.Which() { case schema.Field_Which_slot: t, _ := f.Slot().Type() if tw := t.Which(); tw != schema.Type_Which_structType && tw != schema.Type_Which_interface && tw != schema.Type_Which_anyPointer { continue } if err := g.definePromiseField(n, f); err != nil { return fmt.Errorf("promise field %s.%s: %v", n.shortDisplayName(), f.Name, err) } case schema.Field_Which_group: grp, err := g.nodes.mustFind(f.Group().TypeId()) if err != nil { return fmt.Errorf("promise group %s.%s: %v", n.shortDisplayName(), f.Name, err) } err = renderPromiseGroup(g.r, promiseGroupParams{ G: g, Node: n, Field: f, Group: grp, }) if err != nil { return fmt.Errorf("promise for group %s: %v", grp, err) } if err := g.defineStructPromise(grp); err != nil { return err } } } return nil } func (g *generator) definePromiseField(n *node, f field) error { slot := f.Slot() switch t, _ := slot.Type(); t.Which() { case schema.Type_Which_structType: ni, err := g.nodes.mustFind(t.StructType().TypeId()) if err != nil { return err } params := promiseFieldStructParams{ G: g, Node: n, Field: f, Struct: ni, } if def, _ := slot.DefaultValue(); def.IsValid() && def.Which() == schema.Value_Which_structValue { if sf, _ := def.StructValuePtr(); sf.IsValid() { params.Default, err = g.data.copyData(sf) if err != nil { return err } } } return renderPromiseFieldStruct(g.r, params) case schema.Type_Which_anyPointer: return renderPromiseFieldAnyPointer(g.r, promiseFieldAnyPointerParams{ G: g, Node: n, Field: f, }) case schema.Type_Which_interface: ni, err := g.nodes.mustFind(t.Interface().TypeId()) if err != nil { return err } return renderPromiseFieldInterface(g.r, promiseFieldInterfaceParams{ G: g, Node: n, Field: f, Interface: ni, }) default: panic("unreachable") } } func (g *generator) defineInterface(n *node) error { m, err := methodSet(nil, n, g.nodes) if err != nil { return fmt.Errorf("building method set of interface %s: %v", n, err) } nann, _ := n.Annotations() err = renderInterfaceClient(g.r, interfaceClientParams{ G: g, Node: n, Annotations: parseAnnotations(nann), Methods: m, }) if err != nil { return fmt.Errorf("interface client %s: %v", n, err) } err = renderInterfaceServer(g.r, interfaceServerParams{ G: g, Node: n, Annotations: parseAnnotations(nann), Methods: m, }) if err != nil { return fmt.Errorf("interface server %s: %v", n, err) } return nil } type enumString []string func (es enumString) ValueString() string { return strings.Join([]string(es), "") } func (es enumString) SliceFor(i int) string { n := 0 for _, v := range es[:i] { n += len(v) } return fmt.Sprintf("[%d:%d]", n, n+len(es[i])) } func (g *generator) defineFile() error { f := g.nodes[g.fileID] if f == nil { return fmt.Errorf("no node in schema matches %#x", g.fileID) } if f.pkg == "" { return errors.New("missing package annotation") } if f.imp == "" { return errors.New("missing import annotation") } for _, n := range f.nodes { if n.Which() == schema.Node_Which_annotation { if err := g.defineAnnotation(n); err != nil { return err } } } if err := g.defineConstNodes(f.nodes); err != nil { return err } for _, n := range f.nodes { var err error switch n.Which() { case schema.Node_Which_enum: err = g.defineEnum(n) case schema.Node_Which_structNode: if !n.StructNode().IsGroup() { err = g.defineStruct(n) } case schema.Node_Which_interface: err = g.defineInterface(n) } if err != nil { return err } } if g.opts.schemas { if err := g.defineSchemaVar(); err != nil { return err } } return nil } func generateFile(reqf schema.CodeGeneratorRequest_RequestedFile, nodes nodeMap, opts genoptions) error { if opts.structStrings && !opts.schemas { return errors.New("cannot generate struct String() methods without embedding schemas") } 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) if err != nil { return err } } unformatted := g.generate() formatted, fmtErr := format.Source(unformatted) if fmtErr != nil { formatted = unformatted } file, err := os.Create(fname + ".go") if err != nil { return err } _, werr := file.Write(formatted) cerr := file.Close() if fmtErr != nil { return fmtErr } if werr != nil { return err } if cerr != nil { return err } return nil } func main() { var opts genoptions flag.BoolVar(&opts.promises, "promises", true, "generate code for promises") flag.BoolVar(&opts.schemas, "schemas", true, "embed schema information in generated code") flag.BoolVar(&opts.structStrings, "structstrings", true, "generate String() methods for structs (-schemas must be true)") flag.Parse() msg, err := capnp.NewDecoder(os.Stdin).Decode() if err != nil { fmt.Fprintln(os.Stderr, "capnpc-go: reading input:", err) os.Exit(1) } req, err := schema.ReadRootCodeGeneratorRequest(msg) if err != nil { fmt.Fprintln(os.Stderr, "capnpc-go: reading input:", err) os.Exit(1) } nodes, err := buildNodeMap(req) if err != nil { fmt.Fprintln(os.Stderr, "capnpc-go:", err) os.Exit(1) } success := true reqFiles, _ := req.RequestedFiles() for i := 0; i < reqFiles.Len(); i++ { reqf := reqFiles.At(i) err := generateFile(reqf, nodes, opts) if err != nil { fname, _ := reqf.Filename() fmt.Fprintf(os.Stderr, "capnpc-go: generating %s: %v\n", fname, err) success = false } } if !success { os.Exit(1) } } type uint64Slice []uint64 func (p uint64Slice) Len() int { return len(p) } func (p uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p uint64Slice) Less(i, j int) bool { return p[i] < p[j] }