diff --git a/pkg/yang/entry.go b/pkg/yang/entry.go index 5d35f9b..256cefa 100644 --- a/pkg/yang/entry.go +++ b/pkg/yang/entry.go @@ -93,7 +93,8 @@ type Entry struct { // is a module only. Identities []*Identity `json:",omitempty"` - Augments []*Entry `json:"-"` // Augments associated with this entry + Augments []*Entry `json:"-"` // Augments associated with this entry + AugmentedBy []*Entry `json:"-"` // Augments added to this entry from others // Extra maps all the unsupported fields to their values Extra map[string][]interface{} `json:"-"` @@ -776,6 +777,7 @@ func (e *Entry) Augment(addErrors bool) (processed, skipped int) { // are merged into another entry. processed++ ae.merge(nil, a.Namespace(), a) + ae.AugmentedBy = append(ae.AugmentedBy, a) } e.Augments = sa return processed, skipped @@ -1117,3 +1119,40 @@ func (e *Entry) DefaultValue() string { } return "" } + +// getOrderedChildren lists a nodes child fields and recursively expands +// imported groups in depth first order. +func getOrderedChildren(e Node, seen map[string]bool) []string { + res := []string{} + for _, stmt := range e.Statement().SubStatements() { + // If it is a uses statement, and we can resolve the group recurse into it + if stmt.Kind() == (&Uses{}).Kind() { + if grp := FindGrouping(e, stmt.NName(), seen); grp != nil { + res = append(res, getOrderedChildren(grp, seen)...) + continue + } + } + + res = append(res, stmt.NName()) + } + return res +} + +// GetOrderedChildren returns the order of child elements in this entry from +// the original yang module. Fields from augments may not be retured in +// deterministed order, but will always be last. +func (e *Entry) GetOrderedChildren() []string { + seen := map[string]bool{} + return getOrderedChildren(e.Node, seen) +} + +// GetOrderedAugments returns the order of augments on an entry. +// TODO: is the order of these actually deterministic? Would depend on import order. +func (e *Entry) GetOrderedAugments() []string { + seen := map[string]bool{} + res := []string{} + for _, aug := range e.AugmentedBy { + res = append(res, getOrderedChildren(aug.Node, seen)...) + } + return res +} diff --git a/pkg/yang/entry_test.go b/pkg/yang/entry_test.go index 0cb6004..70c1148 100644 --- a/pkg/yang/entry_test.go +++ b/pkg/yang/entry_test.go @@ -1412,3 +1412,94 @@ func TestEntryTypes(t *testing.T) { } } } + +func TestOrderedChildren(t *testing.T) { + getdir := func(e *Entry, elements ...string) (*Entry, error) { + for _, elem := range elements { + next := e.Dir[elem] + if next == nil { + return nil, fmt.Errorf("%s missing directory %q", e.Path(), elem) + } + e = next + } + return e, nil + } + + modtext := ` +module sequence { + namespace "urn:sequence"; + prefix "sequence"; + + grouping testGroup1 { + leaf foo2 { type string; } + leaf bar2 { type string; } + } + + grouping testGroup2 { + leaf foo1 { type string; } + uses testGroup1; + leaf bar1 { type string; } + } + + container sequence { + leaf seq1 { + type uint32; + } + uses testGroup2; + leaf seq2 { + type uint32; + } + } + + augment "/sequence:sequence" { + leaf aug1 { + type string; + } + leaf aug2 { + type string; + } + } + +} +` + + ms := NewModules() + if err := ms.Parse(modtext, "sequence.yang"); err != nil { + t.Fatal(err) + } + + errs := ms.Process() + if len(errs) > 0 { + t.Fatal(errs) + } + + for i, tc := range []struct { + want []string + wantAug []string + path []string + }{ + { + want: []string{"seq1", "foo1", "foo2", "bar2", "bar1", "seq2"}, + wantAug: []string{"aug1", "aug2"}, + path: []string{"sequence"}, + }, + } { + tname := strings.Join(tc.path, "/") + + mod, err := ms.FindModuleByPrefix("sequence") + if err != nil { + t.Fatalf("[%d_%s] module not found: %v", i, tname, err) + } + defaults := ToEntry(mod) + dir, err := getdir(defaults, tc.path...) + if err != nil { + t.Fatalf("[%d_%s] could not retrieve path: %v", i, tname, err) + } + if got := dir.GetOrderedChildren(); !reflect.DeepEqual(tc.want, got) { + t.Errorf("[%d_%s] want list %+v, got %+v", i, tname, tc.want, got) + } + if got := dir.GetOrderedAugments(); !reflect.DeepEqual(tc.wantAug, got) { + t.Errorf("[%d_%s] want list %+v, got %+v", i, tname, tc.wantAug, got) + } + } +}