func (pf *protofile) printHeader(w io.Writer, e *yang.Entry) { syntax := "proto3" if proto2 { syntax = "proto2" } fmt.Fprintf(w, `syntax = %q; // Automatically generated by goyang https://github.com/openconfig/goyang // compiled %s`, syntax, time.Now().UTC().Format(time.RFC3339)) fmt.Fprintf(w, ` // do not delete the next line %s%s`, versionPrefix, protoVersion) fmt.Fprintf(w, ` // module %q `, e.Name) if v := e.Extra["revision"]; len(v) > 0 { for _, rev := range v[0].([]*yang.Revision) { fmt.Fprintf(w, "// revision %q\n", rev.Name) } } if v := e.Extra["namespace"]; len(v) > 0 { fmt.Fprintf(w, "// namespace %q\n", v[0].(*yang.Value).Name) } fmt.Fprintln(w) if !protoNoComments && e.Description != "" { fmt.Fprintln(indent.NewWriter(w, "// "), e.Description) } fmt.Fprintf(w, "package %s;\n", pf.fieldName(e.Name)) }
// Print prints e to w in human readable form. func (e *Entry) Print(w io.Writer) { if e.Description != "" { fmt.Fprintln(w) fmt.Fprintln(indent.NewWriter(w, "// "), e.Description) } if e.ReadOnly() { fmt.Fprintf(w, "RO: ") } else { fmt.Fprintf(w, "rw: ") } if e.Type != nil { fmt.Fprintf(w, "%s ", e.Type.Name) } switch { case e.Dir == nil && e.ListAttr != nil: fmt.Fprintf(w, "[]%s\n", e.Name) return case e.Dir == nil: fmt.Fprintf(w, "%s\n", e.Name) return case e.ListAttr != nil: fmt.Fprintf(w, "[%s]%s {\n", e.Key, e.Name) //} default: fmt.Fprintf(w, "%s {\n", e.Name) //} } var names []string for k := range e.Dir { names = append(names, k) } sort.Strings(names) for _, k := range names { e.Dir[k].Print(indent.NewWriter(w, " ")) } // { to match the brace below to keep brace matching working fmt.Fprintln(w, "}") }
// FormatNode writes e, formatted almost like a protobuf message, to w. func FormatNode(w io.Writer, e *yang.Entry) { fmt.Fprintln(w) if e.Description != "" { fmt.Fprintln(indent.NewWriter(w, "// "), e.Description) } fmt.Fprintf(w, "message %s {\n", fixName(e.Name)) var names []string for k := range e.Dir { names = append(names, k) } sort.Strings(names) for x, k := range names { se := e.Dir[k] if se.Description != "" { fmt.Fprintln(indent.NewWriter(w, " // "), se.Description) } if se.IsList { fmt.Fprint(w, " repeated ") } else { fmt.Fprint(w, " optional ") } if len(se.Dir) == 0 && se.Type != nil { // TODO(borman): this is probably an empty container. kind := "UNKNOWN TYPE" if se.Type != nil { kind = kind2proto[se.Type.Kind] } fmt.Fprintf(w, "%s %s = %d; // %s\n", kind, fixName(k), x+1, yang.Source(se.Node)) continue } fmt.Fprintf(w, "%s %s = %d; // %s\n", fixName(se.Name), fixName(k), x+1, yang.Source(se.Node)) } // { to match the brace below to keep brace matching working fmt.Fprintln(w, "}") }
// printType prints type t in a moderately human readable format to w. func printType(w io.Writer, t *yang.YangType, verbose bool) { if verbose && t.Base != nil { base := yang.Source(t.Base) if base == "unknown" { base = "unnamed type" } fmt.Fprintf(w, "%s: ", base) } fmt.Fprintf(w, "%s", t.Root.Name) if t.Kind.String() != t.Root.Name { fmt.Fprintf(w, "(%s)", t.Kind) } if t.Units != "" { fmt.Fprintf(w, " units=%s", t.Units) } if t.Default != "" { fmt.Fprintf(w, " default=%q", t.Default) } if t.FractionDigits != 0 { fmt.Fprintf(w, " fraction-digits=%d", t.FractionDigits) } if len(t.Length) > 0 { fmt.Fprintf(w, " length=%s", t.Length) } if t.Kind == yang.YinstanceIdentifier && !t.OptionalInstance { fmt.Fprintf(w, " required") } if t.Kind == yang.Yleafref && t.Path != "" { fmt.Fprintf(w, " path=%q", t.Path) } if len(t.Pattern) > 0 { fmt.Fprintf(w, " pattern=%s", strings.Join(t.Pattern, "|")) } b := yang.BaseTypedefs[t.Kind.String()].YangType if len(t.Range) > 0 && !t.Range.Equal(b.Range) { fmt.Fprintf(w, " range=%s", t.Range) } if len(t.Type) > 0 { fmt.Fprintf(w, "union{\n") for _, t := range t.Type { printType(indent.NewWriter(w, " "), t, verbose) } fmt.Fprintf(w, "}") } fmt.Fprintf(w, ";\n") }
// Write writes e, formatted, and all of its children, to w. func Write(w io.Writer, e *yang.Entry) { if e.Description != "" { fmt.Fprintln(w) fmt.Fprintln(indent.NewWriter(w, "// "), e.Description) } if len(e.Exts) > 0 { fmt.Fprintf(w, "extensions: {\n") for _, ext := range e.Exts { if n := ext.NName(); n != "" { fmt.Fprintf(w, " %s %s;\n", ext.Kind(), n) } else { fmt.Fprintf(w, " %s;\n", ext.Kind()) } } fmt.Fprintln(w, "}") } switch { case e.RPC != nil: fmt.Fprintf(w, "RPC: ") case e.ReadOnly(): fmt.Fprintf(w, "RO: ") default: fmt.Fprintf(w, "rw: ") } if e.Type != nil { fmt.Fprintf(w, "%s ", getTypeName(e)) } name := e.Name if e.Prefix != nil { name = e.Prefix.Name + ":" + name } switch { case e.Dir == nil && e.ListAttr != nil: fmt.Fprintf(w, "[]%s\n", name) return case e.Dir == nil: fmt.Fprintf(w, "%s\n", name) return case e.ListAttr != nil: fmt.Fprintf(w, "[%s]%s {\n", e.Key, name) //} default: fmt.Fprintf(w, "%s {\n", name) //} } if r := e.RPC; r != nil { if r.Input != nil { Write(indent.NewWriter(w, " "), r.Input) } if r.Output != nil { Write(indent.NewWriter(w, " "), r.Output) } } var names []string for k := range e.Dir { names = append(names, k) } sort.Strings(names) for _, k := range names { Write(indent.NewWriter(w, " "), e.Dir[k]) } // { to match the brace below to keep brace matching working fmt.Fprintln(w, "}") }
// FormatNode writes e, formatted almost like a protobuf message, to w. func FormatNode(w io.Writer, e *yang.Entry) { var names []string for k, se := range e.Dir { if se.RPC != nil { names = append(names, k) } } needEmpty := false if len(names) > 0 { sort.Strings(names) fmt.Fprintf(w, "service %s {\n", fixName(e.Name)) for _, k := range names { rpc := e.Dir[k].RPC k = fixName(k) iName := "Empty" oName := "Empty" if rpc.Input != nil { iName = k + "Request" rpc.Input.Name = iName if isStream(rpc.Input) { iName = "stream " + iName } } if rpc.Output != nil { oName = k + "Response" rpc.Output.Name = oName if isStream(rpc.Output) { oName = "stream " + oName } } needEmpty = needEmpty || rpc.Input == nil || rpc.Output == nil fmt.Fprintf(w, " rpc %s (%s) returns (%s);\n", k, iName, oName) } fmt.Fprintln(w, "}") for _, k := range names { rpc := e.Dir[k].RPC if rpc.Input != nil { FormatNode(w, rpc.Input) } if rpc.Output != nil { FormatNode(w, rpc.Output) } } } if needEmpty { fmt.Fprintln(w, "\nmessage Empty { }") } names = nil for k, se := range e.Dir { if se.RPC == nil { names = append(names, k) } } if len(names) == 0 { return } fmt.Fprintln(w) if e.Description != "" { fmt.Fprintln(indent.NewWriter(w, "// "), e.Description) } fmt.Fprintf(w, "message %s {\n", fixName(e.Name)) sort.Strings(names) for x, k := range names { se := e.Dir[k] if se.Description != "" { fmt.Fprintln(indent.NewWriter(w, " // "), se.Description) } if se.ListAttr != nil { fmt.Fprint(w, " repeated ") } else { fmt.Fprint(w, " optional ") } if len(se.Dir) == 0 && se.Type != nil { // TODO(borman): this is probably an empty container. kind := "UNKNOWN TYPE" if se.Type != nil { kind = kind2proto[se.Type.Kind] } fmt.Fprintf(w, "%s %s = %d; // %s\n", kind, fixName(k), x+1, yang.Source(se.Node)) continue } fmt.Fprintf(w, "%s %s = %d; // %s\n", fixName(se.Name), fixName(k), x+1, yang.Source(se.Node)) } // { to match the brace below to keep brace matching working fmt.Fprintln(w, "}") }
// FindNode finds the node referenced by path relative to n. If path does not // reference a node then nil is returned (i.e. path not found). The path looks // similar to an XPath but curently has no wildcarding. For example: // "/if:interfaces/if:interface" and "../config". func FindNode(n Node, path string) (Node, error) { if path == "" { return n, nil } // / is not a valid path, it needs a module name if path == "/" { return nil, fmt.Errorf("invalid path %q", path) } // Paths do not end in /'s if path[len(path)-1] == '/' { return nil, fmt.Errorf("invalid path %q", path) } parts := strings.Split(path, "/") // An absolute path has a leading component of "". // We need to discover which module they are part of // based on our imports. if parts[0] == "" { parts = parts[1:] // TODO(borman): merge this with FindModuleByPrefix? n = RootNode(n) // The base is always a module mod := n.(*Module) prefix, _ := getPrefix(parts[0]) if mod.Kind() == "submodule" { m := mod.modules.Modules[mod.BelongsTo.Name] if m == nil { return nil, fmt.Errorf("%s: unknown module %s", m.Name, mod.BelongsTo.Name) } if prefix == "" || prefix == mod.BelongsTo.Prefix.Name { mod = m goto processing } mod = m } if prefix == "" || prefix == mod.Prefix.Name { goto processing } for _, i := range mod.Import { if prefix == i.Prefix.Name { n = i.Module goto processing } } // We didn't find a matching prefix. return nil, fmt.Errorf("unknown prefix: %q", prefix) processing: // At this point, n should be pointing to the Module node // of module we are rooted in } for _, part := range parts { // If we encounter an RPC node in our search then we // return the magic isRPCNode Node which just contains // an error that it is an RPC node. isRPCNode is a singleton // and can be checked against. if n.Kind() == "rpc" { return isRPCNode, nil } if part == ".." { Loop: for { n = n.ParentNode() if n == nil { return nil, fmt.Errorf(".. with no parent") } // choice, leaf, and case nodes // are "invisible" when doing ".." // up the tree. switch n.Kind() { case "choice", "leaf", "case": default: break Loop } } continue } // For now just strip off any prefix // TODO(borman): fix this _, spart := getPrefix(part) n = ChildNode(n, spart) if n == nil { return nil, fmt.Errorf("%s: no such element", part) } } return n, nil } // ChildNode finds n's child node named name. It returns nil if the node // could not be found. ChildNode looks at every direct Node pointer in // n as well as every node in all slices of Node pointers. Names must // be non-ambiguous, otherwise ChildNode has a non-deterministic result. func ChildNode(n Node, name string) Node { v := reflect.ValueOf(n).Elem() t := v.Type() nf := t.NumField() Loop: for i := 0; i < nf; i++ { ft := t.Field(i) yang := ft.Tag.Get("yang") if yang == "" { continue } parts := strings.Split(yang, ",") for _, p := range parts[1:] { if p == "nomerge" { continue Loop } } f := v.Field(i) if !f.IsValid() || f.IsNil() { continue } check := func(n Node) Node { if n.NName() == name { return n } return nil } if parts[0] == "uses" { check = func(n Node) Node { uname := n.NName() // unrooted uses are rooted at root if !strings.HasPrefix(uname, "/") { uname = "/" + uname } if n, _ = FindNode(n, uname); n != nil { return ChildNode(n, name) } return nil } } switch ft.Type.Kind() { case reflect.Ptr: if n = check(f.Interface().(Node)); n != nil { return n } case reflect.Slice: sl := f.Len() for i := 0; i < sl; i++ { n = f.Index(i).Interface().(Node) if n = check(n); n != nil { return n } } } } return nil } // PrintNode prints node n to w, recursively. // TODO(borman): display more information func PrintNode(w io.Writer, n Node) { v := reflect.ValueOf(n).Elem() t := v.Type() nf := t.NumField() fmt.Fprintf(w, "%s%s [%s]\n", n.NName(), n.Kind()) Loop: for i := 0; i < nf; i++ { ft := t.Field(i) yang := ft.Tag.Get("yang") if yang == "" { continue } parts := strings.Split(yang, ",") for _, p := range parts[1:] { if p == "nomerge" { continue Loop } } // Skip uppercase elements. if parts[0][0] >= 'A' && parts[0][0] <= 'Z' { continue } f := v.Field(i) if !f.IsValid() || f.IsNil() { continue } switch ft.Type.Kind() { case reflect.Ptr: n = f.Interface().(Node) if v, ok := n.(*Value); ok { fmt.Fprintf(w, "%s%s = %s\n", ft.Name, v.Name) } else { PrintNode(indent.NewWriter(w, " "), n) } case reflect.Slice: sl := f.Len() for i := 0; i < sl; i++ { n = f.Index(i).Interface().(Node) if v, ok := n.(*Value); ok { fmt.Fprintf(w, "%s%s[%d] = %s\n", ft.Name, i, v.Name) } else { PrintNode(indent.NewWriter(w, " "), n) } } } } }
// printNode writes e, formatted almost like a protobuf message, to w. func (pf *protofile) printNode(w io.Writer, e *yang.Entry, nest bool) { nodes := children(e) if !protoNoComments && e.Description != "" { fmt.Fprintln(indent.NewWriter(w, "// "), e.Description) } messageName := pf.fullName(e) mi := pf.messages[messageName] if mi == nil { mi = &messageInfo{ fields: map[string]int{}, } pf.messages[messageName] = mi } fmt.Fprintf(w, "message %s {", pf.messageName(e)) // matching brace } if protoWithSource { fmt.Fprintf(w, " // %s", yang.Source(e.Node)) } fmt.Fprintln(w) for i, se := range nodes { k := se.Name if !protoNoComments && se.Description != "" { fmt.Fprintln(indent.NewWriter(w, " // "), se.Description) } if nest && (len(se.Dir) > 0 || se.Type == nil) { pf.printNode(indent.NewWriter(w, " "), se, true) } prefix := " " if se.ListAttr != nil { prefix = " repeated " } else if proto2 { prefix = " optional " } name := pf.fieldName(k) printed := false var kind string if len(se.Dir) > 0 || se.Type == nil { kind = pf.messageName(se) } else if se.Type.Kind == yang.Ybits { values := dedup(se.Type.Bit.Values()) asComment := false switch { case len(values) > 0 && values[len(values)-1] > 63: if i != 0 { fmt.Fprintln(w) } fmt.Fprintf(w, " // *WARNING* bitfield %s has more than 64 positions\n", name) kind = "uint64" asComment = true case len(values) > 0 && values[len(values)-1] > 31: if i != 0 { fmt.Fprintln(w) } fmt.Fprintf(w, " // bitfield %s to large for enum\n", name) kind = "uint64" asComment = true default: kind = pf.fixName(se.Name) } if !asComment { fmt.Fprintf(w, " enum %s {\n", kind) fmt.Fprintf(w, " %s_FIELD_NOT_SET = 0;\n", kind) } else { fmt.Fprintf(w, " // Values:\n") } names := map[int64][]string{} for n, v := range se.Type.Bit.NameMap() { names[v] = append(names[v], n) } for _, v := range values { ns := names[v] sort.Strings(ns) if asComment { for _, n := range ns { fmt.Fprintf(w, " // %s = 1 << %d\n", n, v) } } else { n := strings.ToUpper(pf.fieldName(ns[0])) fmt.Fprintf(w, " %s_%s = %d;\n", kind, n, 1<<uint(v)) for _, n := range ns[1:] { n = strings.ToUpper(pf.fieldName(n)) fmt.Fprintf(w, " // %s = %d; (DUPLICATE VALUE)\n", n, 1<<uint(v)) } } } if !asComment { fmt.Fprintf(w, " };\n") } } else if se.Type.Kind == yang.Ydecimal64 { kind = "Decimal64" pf.hasDecimal64 = true } else if se.Type.Kind == yang.Yenum { kind = pf.fixName(se.Name) fmt.Fprintf(w, " enum %s {", kind) if protoWithSource { fmt.Fprintf(w, " // %s", yang.Source(se.Node)) } fmt.Fprintln(w) for i, n := range se.Type.Enum.Names() { fmt.Fprintf(w, " %s_%s = %d;\n", kind, strings.ToUpper(pf.fieldName(n)), i) } fmt.Fprintf(w, " };\n") } else if se.Type.Kind == yang.Yunion { types := pf.unionTypes(se.Type, map[string]bool{}) switch len(types) { case 0: fmt.Fprintf(w, " // *WARNING* union %s has no types\n", se.Name) printed = true case 1: kind = types[0] default: iw := w kind = pf.fixName(se.Name) if se.ListAttr != nil { fmt.Fprintf(w, " message %s {\n", kind) iw = indent.NewWriter(w, " ") } fmt.Fprintf(iw, " oneof %s {", kind) // matching brace } if protoWithSource { fmt.Fprintf(w, " // %s", yang.Source(se.Node)) } fmt.Fprintln(w) for _, tkind := range types { fmt.Fprintf(iw, " %s %s_%s = %d;\n", tkind, kind, tkind, mi.tag(name, tkind, false)) } // { to match the brace below to keep brace matching working fmt.Fprintf(iw, " }\n") if se.ListAttr != nil { fmt.Fprintf(w, " }\n") } else { printed = true } } } else { kind = kind2proto[se.Type.Kind] } if !printed { fmt.Fprintf(w, "%s%s %s = %d;", prefix, kind, name, mi.tag(name, kind, se.ListAttr != nil)) if protoWithSource { fmt.Fprintf(w, " // %s", yang.Source(se.Node)) } fmt.Fprintln(w) } } // { to match the brace below to keep brace matching working fmt.Fprintln(w, "}") }