cloudflared-mirror/vendor/zombiezen.com/go/capnproto2/capnpc-go/nodes.go

337 lines
7.8 KiB
Go

package main
import (
"errors"
"fmt"
"strings"
"zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/internal/schema"
)
type node struct {
schema.Node
pkg string
imp string
nodes []*node // only for file nodes
Name string
}
func (n *node) codeOrderFields() []field {
fields, _ := n.StructNode().Fields()
numFields := fields.Len()
mbrs := make([]field, numFields)
for i := 0; i < numFields; i++ {
f := fields.At(i)
fann, _ := f.Annotations()
fname, _ := f.Name()
fname = parseAnnotations(fann).Rename(fname)
mbrs[f.CodeOrder()] = field{Field: f, Name: fname}
}
return mbrs
}
// DiscriminantOffset returns the byte offset of the struct union discriminant.
func (n *node) DiscriminantOffset() (uint32, error) {
if n == nil {
return 0, errors.New("discriminant offset called on nil node")
}
if n.Which() != schema.Node_Which_structNode {
return 0, fmt.Errorf("discriminant offset called on %v node", n.Which())
}
return n.StructNode().DiscriminantOffset() * 2, nil
}
func (n *node) shortDisplayName() string {
dn, _ := n.DisplayName()
return dn[n.DisplayNamePrefixLength():]
}
// String returns the node's display name.
func (n *node) String() string {
return displayName(n)
}
func displayName(n interface {
DisplayName() (string, error)
}) string {
dn, _ := n.DisplayName()
return dn
}
type field struct {
schema.Field
Name string
}
// HasDiscriminant reports whether the field is in a union.
func (f field) HasDiscriminant() bool {
return f.DiscriminantValue() != schema.Field_noDiscriminant
}
type enumval struct {
schema.Enumerant
Name string
Val int
Tag string
parent *node
}
func makeEnumval(enum *node, i int, e schema.Enumerant) enumval {
eann, _ := e.Annotations()
ann := parseAnnotations(eann)
name, _ := e.Name()
name = ann.Rename(name)
t := ann.Tag(name)
return enumval{e, name, i, t, enum}
}
func (e *enumval) FullName() string {
return e.parent.Name + "_" + e.Name
}
type interfaceMethod struct {
schema.Method
Interface *node
ID int
Name string
OriginalName string
Params *node
Results *node
}
func methodSet(methods []interfaceMethod, n *node, nodes nodeMap) ([]interfaceMethod, error) {
ms, _ := n.Interface().Methods()
for i := 0; i < ms.Len(); i++ {
m := ms.At(i)
mname, _ := m.Name()
mann, _ := m.Annotations()
pn, err := nodes.mustFind(m.ParamStructType())
if err != nil {
return methods, fmt.Errorf("could not find param type for %s.%s", n.shortDisplayName(), mname)
}
rn, err := nodes.mustFind(m.ResultStructType())
if err != nil {
return methods, fmt.Errorf("could not find result type for %s.%s", n.shortDisplayName(), mname)
}
methods = append(methods, interfaceMethod{
Method: m,
Interface: n,
ID: i,
OriginalName: mname,
Name: parseAnnotations(mann).Rename(mname),
Params: pn,
Results: rn,
})
}
// TODO(light): sort added methods by code order
supers, _ := n.Interface().Superclasses()
for i := 0; i < supers.Len(); i++ {
s := supers.At(i)
sn, err := nodes.mustFind(s.Id())
if err != nil {
return methods, fmt.Errorf("could not find superclass %#x of %s", s.Id(), n)
}
methods, err = methodSet(methods, sn, nodes)
if err != nil {
return methods, err
}
}
return methods, nil
}
// Tag types
const (
defaultTag = iota
noTag
customTag
)
type annotations struct {
Doc string
Package string
Import string
TagType int
CustomTag string
Name string
}
func parseAnnotations(list schema.Annotation_List) *annotations {
ann := new(annotations)
for i, n := 0, list.Len(); i < n; i++ {
a := list.At(i)
val, _ := a.Value()
text, _ := val.Text()
switch a.Id() {
case capnp.Doc:
ann.Doc = text
case capnp.Package:
ann.Package = text
case capnp.Import:
ann.Import = text
case capnp.Tag:
ann.TagType = customTag
ann.CustomTag = text
case capnp.Notag:
ann.TagType = noTag
case capnp.Name:
ann.Name = text
}
}
return ann
}
// Tag returns the string value that an enumerant value called name should have.
// An empty string indicates that this enumerant value has no tag.
func (ann *annotations) Tag(name string) string {
switch ann.TagType {
case noTag:
return ""
case customTag:
return ann.CustomTag
case defaultTag:
fallthrough
default:
return name
}
}
// Rename returns the overridden name from the annotations or the given name
// if no annotation was found.
func (ann *annotations) Rename(given string) string {
if ann.Name == "" {
return given
}
return ann.Name
}
type nodeMap map[uint64]*node
func buildNodeMap(req schema.CodeGeneratorRequest) (nodeMap, error) {
rnodes, err := req.Nodes()
if err != nil {
return nil, err
}
nodes := make(nodeMap, rnodes.Len())
var allfiles []*node
for i := 0; i < rnodes.Len(); i++ {
ni := rnodes.At(i)
n := &node{Node: ni}
nodes[n.Id()] = n
if n.Which() == schema.Node_Which_file {
allfiles = append(allfiles, n)
}
}
for _, f := range allfiles {
fann, err := f.Annotations()
if err != nil {
return nil, fmt.Errorf("reading annotations for %v: %v", f, err)
}
ann := parseAnnotations(fann)
f.pkg = ann.Package
f.imp = ann.Import
nnodes, _ := f.NestedNodes()
for i := 0; i < nnodes.Len(); i++ {
nn := nnodes.At(i)
if ni := nodes[nn.Id()]; ni != nil {
nname, _ := nn.Name()
if err := resolveName(nodes, ni, "", nname, f); err != nil {
return nil, err
}
}
}
}
return nodes, nil
}
// resolveName is called as part of building up a node map to populate the name field of n.
func resolveName(nodes nodeMap, n *node, base, name string, file *node) error {
na, err := n.Annotations()
if err != nil {
return fmt.Errorf("reading annotations for %s: %v", n, err)
}
name = parseAnnotations(na).Rename(name)
if base == "" {
n.Name = strings.Title(name)
} else {
n.Name = base + "_" + name
}
n.pkg = file.pkg
n.imp = file.imp
file.nodes = append(file.nodes, n)
nnodes, err := n.NestedNodes()
if err != nil {
return fmt.Errorf("listing nested nodes for %s: %v", n, err)
}
for i := 0; i < nnodes.Len(); i++ {
nn := nnodes.At(i)
ni := nodes[nn.Id()]
if ni == nil {
continue
}
nname, err := nn.Name()
if err != nil {
return fmt.Errorf("reading name of nested node %d in %s: %v", i+1, n, err)
}
if err := resolveName(nodes, ni, n.Name, nname, file); err != nil {
return err
}
}
switch n.Which() {
case schema.Node_Which_structNode:
fields, _ := n.StructNode().Fields()
for i := 0; i < fields.Len(); i++ {
f := fields.At(i)
if f.Which() != schema.Field_Which_group {
continue
}
fa, _ := f.Annotations()
fname, _ := f.Name()
grp := nodes[f.Group().TypeId()]
if grp == nil {
return fmt.Errorf("could not find type information for group %s in %s", fname, n)
}
fname = parseAnnotations(fa).Rename(fname)
if err := resolveName(nodes, grp, n.Name, fname, file); err != nil {
return err
}
}
case schema.Node_Which_interface:
m, _ := n.Interface().Methods()
methodResolve := func(id uint64, mname string, base string, name string) error {
x := nodes[id]
if x == nil {
return fmt.Errorf("could not find type %#x for %s.%s", id, n, mname)
}
if x.ScopeId() != 0 {
return nil
}
return resolveName(nodes, x, base, name, file)
}
for i := 0; i < m.Len(); i++ {
mm := m.At(i)
mname, _ := mm.Name()
mann, _ := mm.Annotations()
base := n.Name + "_" + parseAnnotations(mann).Rename(mname)
if err := methodResolve(mm.ParamStructType(), mname, base, "Params"); err != nil {
return err
}
if err := methodResolve(mm.ResultStructType(), mname, base, "Results"); err != nil {
return err
}
}
}
return nil
}
func (nm nodeMap) mustFind(id uint64) (*node, error) {
n := nm[id]
if n == nil {
return nil, fmt.Errorf("could not find node %#x in schema", id)
}
return n, nil
}