Skip to content

Commit d9bb850

Browse files
authored
[Go]: Resolve conflict when a multi-keyed list contains a container named "key" (#551)
* [Go]: Resolve conflict when a multi-keyed list contains a container named "key" Do this by keeping the current naming, but change to "%s_%s_key" instead of "%s_%s_YANGListKey" as the back up name if a conflict like this occurs. This should always work since child non-leaves are named using camel cases, so I'm making it error out if the backup is already taken. Tested: - Added a new integration test for this case. * update design doc
1 parent 5ba0876 commit d9bb850

File tree

5 files changed

+251
-0
lines changed

5 files changed

+251
-0
lines changed

docs/design.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ type C_Bar struct {
285285

286286
Such that the `Foo` field is a map, keyed on the type of the key leaf (`fookey`). For lists with multiple keys, a specific key `struct` is generated (`C_Bar_Key` in the above example), with fields that correspond to the key fields of the YANG list.
287287

288+
If there is already a Go structure named `C_Bar_Key` due to the [camel case rules](#output-go-structures-and-their-fields), then the back-up name of `C_Bar_YANGListKey` will be used instead.
289+
288290
Each YANG list that exists within a container has a helper-method generated for it. For a list named `foo`, the parent container (`C`) has a `NewFoo(fookey string)` method generated, taking a key value as an argument, and returning a new member of the map within the `foo` list.
289291

290292
##### Note on using binary as a list key type
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
module openconfig-multikey-list-name-conflict {
2+
namespace "urn:ocmklnc";
3+
prefix "oc";
4+
5+
description
6+
"A simple test module that is used to verify name conflict resolution
7+
between a multi-key struct and a container for Go code generation";
8+
9+
grouping multi-key-config {
10+
leaf key1 { type uint32; }
11+
leaf key2 { type uint64; }
12+
}
13+
14+
grouping lists-top {
15+
container model {
16+
container a {
17+
list multi-key {
18+
key "key1 key2";
19+
20+
leaf key1 {
21+
type leafref {
22+
path "../config/key1";
23+
}
24+
}
25+
26+
leaf key2 {
27+
type leafref {
28+
path "../config/key2";
29+
}
30+
}
31+
32+
container config {
33+
uses multi-key-config;
34+
}
35+
36+
container state {
37+
config false;
38+
uses multi-key-config;
39+
// This container name is used to test name conflict resolution
40+
// with the multi-key struct for Go code generation.
41+
container key {
42+
leaf key3 { type uint8; }
43+
}
44+
}
45+
}
46+
}
47+
}
48+
}
49+
50+
uses lists-top;
51+
52+
}

ygen/codegen_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,21 @@ func TestSimpleStructs(t *testing.T) {
546546
},
547547
},
548548
wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-withlist-opstate.formatted-txt"),
549+
}, {
550+
name: "OpenConfig schema test - multi-keyed list key struct name conflict and associated method (rename, new)",
551+
inFiles: []string{filepath.Join(datapath, "openconfig-multikey-list-name-conflict.yang")},
552+
inConfig: GeneratorConfig{
553+
TransformationOptions: TransformationOpts{
554+
CompressBehaviour: genutil.PreferIntendedConfig,
555+
ShortenEnumLeafNames: true,
556+
UseDefiningModuleForTypedefEnumNames: true,
557+
},
558+
GoOptions: GoOpts{
559+
GenerateRenameMethod: true,
560+
GenerateSimpleUnions: true,
561+
},
562+
},
563+
wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-multikey-list-name-conflict.formatted-txt"),
549564
}, {
550565
name: "simple openconfig test, with a list that has an enumeration key",
551566
inFiles: []string{filepath.Join(datapath, "openconfig-list-enum-key.yang")},

ygen/gogen.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2008,6 +2008,13 @@ func yangListFieldToGoType(listField *yang.Entry, listFieldName string, parent *
20082008
// generatedGoMultiKeyListStruct struct, which is then expanded by a template to the struct
20092009
// definition.
20102010
listKeyStructName = fmt.Sprintf("%s_%s_Key", parent.Name, listFieldName)
2011+
if gogen.definedGlobals[listKeyStructName] {
2012+
listKeyStructName = fmt.Sprintf("%s_%s_YANGListKey", parent.Name, listFieldName)
2013+
if gogen.definedGlobals[listKeyStructName] {
2014+
return "", nil, nil, fmt.Errorf("unexpected generated list key name conflict for %s", listField.Path())
2015+
}
2016+
gogen.definedGlobals[listKeyStructName] = true
2017+
}
20112018
multiListKey = &generatedGoMultiKeyListStruct{
20122019
KeyStructName: listKeyStructName,
20132020
ParentPath: util.SlicePathToString(parent.Path),
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
Package ocstructs is a generated package which contains definitions
3+
of structs which represent a YANG schema. The generated schema can be
4+
compressed by a series of transformations (compression was true
5+
in this case).
6+
7+
This package was generated by codegen-tests
8+
using the following YANG input files:
9+
- ../testdata/modules/openconfig-multikey-list-name-conflict.yang
10+
Imported modules were sourced from:
11+
*/
12+
package ocstructs
13+
14+
import (
15+
"encoding/json"
16+
"fmt"
17+
"reflect"
18+
19+
"github.com/openconfig/ygot/ygot"
20+
)
21+
22+
// Binary is a type that is used for fields that have a YANG type of
23+
// binary. It is used such that binary fields can be distinguished from
24+
// leaf-lists of uint8s (which are mapped to []uint8, equivalent to
25+
// []byte in reflection).
26+
type Binary []byte
27+
28+
// YANGEmpty is a type that is used for fields that have a YANG type of
29+
// empty. It is used such that empty fields can be distinguished from boolean fields
30+
// in the generated code.
31+
type YANGEmpty bool
32+
33+
// UnionInt8 is an int8 type assignable to unions of which it is a subtype.
34+
type UnionInt8 int8
35+
36+
// UnionInt16 is an int16 type assignable to unions of which it is a subtype.
37+
type UnionInt16 int16
38+
39+
// UnionInt32 is an int32 type assignable to unions of which it is a subtype.
40+
type UnionInt32 int32
41+
42+
// UnionInt64 is an int64 type assignable to unions of which it is a subtype.
43+
type UnionInt64 int64
44+
45+
// UnionUint8 is a uint8 type assignable to unions of which it is a subtype.
46+
type UnionUint8 uint8
47+
48+
// UnionUint16 is a uint16 type assignable to unions of which it is a subtype.
49+
type UnionUint16 uint16
50+
51+
// UnionUint32 is a uint32 type assignable to unions of which it is a subtype.
52+
type UnionUint32 uint32
53+
54+
// UnionUint64 is a uint64 type assignable to unions of which it is a subtype.
55+
type UnionUint64 uint64
56+
57+
// UnionFloat64 is a float64 type assignable to unions of which it is a subtype.
58+
type UnionFloat64 float64
59+
60+
// UnionString is a string type assignable to unions of which it is a subtype.
61+
type UnionString string
62+
63+
// UnionBool is a bool type assignable to unions of which it is a subtype.
64+
type UnionBool bool
65+
66+
// UnionUnsupported is an interface{} wrapper type for unsupported types. It is
67+
// assignable to unions of which it is a subtype.
68+
type UnionUnsupported struct {
69+
Value interface{}
70+
}
71+
72+
// Model represents the /openconfig-multikey-list-name-conflict/model YANG schema element.
73+
type Model struct {
74+
MultiKey map[Model_MultiKey_YANGListKey]*Model_MultiKey `path:"a/multi-key" module:"openconfig-multikey-list-name-conflict"`
75+
}
76+
77+
// IsYANGGoStruct ensures that Model implements the yang.GoStruct
78+
// interface. This allows functions that need to handle this struct to
79+
// identify it as being generated by ygen.
80+
func (*Model) IsYANGGoStruct() {}
81+
82+
// Model_MultiKey_YANGListKey represents the key for list MultiKey of element /openconfig-multikey-list-name-conflict/model.
83+
type Model_MultiKey_YANGListKey struct {
84+
Key1 uint32 `path:"key1"`
85+
Key2 uint64 `path:"key2"`
86+
}
87+
88+
// NewMultiKey creates a new entry in the MultiKey list of the
89+
// Model struct. The keys of the list are populated from the input
90+
// arguments.
91+
func (t *Model) NewMultiKey(Key1 uint32, Key2 uint64) (*Model_MultiKey, error){
92+
93+
// Initialise the list within the receiver struct if it has not already been
94+
// created.
95+
if t.MultiKey == nil {
96+
t.MultiKey = make(map[Model_MultiKey_YANGListKey]*Model_MultiKey)
97+
}
98+
99+
key := Model_MultiKey_YANGListKey{
100+
Key1: Key1,
101+
Key2: Key2,
102+
}
103+
104+
// Ensure that this key has not already been used in the
105+
// list. Keyed YANG lists do not allow duplicate keys to
106+
// be created.
107+
if _, ok := t.MultiKey[key]; ok {
108+
return nil, fmt.Errorf("duplicate key %v for list MultiKey", key)
109+
}
110+
111+
t.MultiKey[key] = &Model_MultiKey{
112+
Key1: &Key1,
113+
Key2: &Key2,
114+
}
115+
116+
return t.MultiKey[key], nil
117+
}
118+
119+
// RenameMultiKey renames an entry in the list MultiKey within
120+
// the Model struct. The entry with key oldK is renamed to newK updating
121+
// the key within the value.
122+
func (t *Model) RenameMultiKey(oldK, newK Model_MultiKey_YANGListKey) error {
123+
if _, ok := t.MultiKey[newK]; ok {
124+
return fmt.Errorf("key %v already exists in MultiKey", newK)
125+
}
126+
127+
e, ok := t.MultiKey[oldK]
128+
if !ok {
129+
return fmt.Errorf("key %v not found in MultiKey", oldK)
130+
}
131+
e.Key1 = &newK.Key1
132+
e.Key2 = &newK.Key2
133+
134+
t.MultiKey[newK] = e
135+
delete(t.MultiKey, oldK)
136+
return nil
137+
}
138+
139+
// Model_MultiKey represents the /openconfig-multikey-list-name-conflict/model/a/multi-key YANG schema element.
140+
type Model_MultiKey struct {
141+
Key *Model_MultiKey_Key `path:"state/key" module:"openconfig-multikey-list-name-conflict"`
142+
Key1 *uint32 `path:"config/key1|key1" module:"openconfig-multikey-list-name-conflict"`
143+
Key2 *uint64 `path:"config/key2|key2" module:"openconfig-multikey-list-name-conflict"`
144+
}
145+
146+
// IsYANGGoStruct ensures that Model_MultiKey implements the yang.GoStruct
147+
// interface. This allows functions that need to handle this struct to
148+
// identify it as being generated by ygen.
149+
func (*Model_MultiKey) IsYANGGoStruct() {}
150+
151+
// ΛListKeyMap returns the keys of the Model_MultiKey struct, which is a YANG list entry.
152+
func (t *Model_MultiKey) ΛListKeyMap() (map[string]interface{}, error) {
153+
if t.Key1 == nil {
154+
return nil, fmt.Errorf("nil value for key Key1")
155+
}
156+
157+
if t.Key2 == nil {
158+
return nil, fmt.Errorf("nil value for key Key2")
159+
}
160+
161+
return map[string]interface{}{
162+
"key1": *t.Key1,
163+
"key2": *t.Key2,
164+
}, nil
165+
}
166+
167+
// Model_MultiKey_Key represents the /openconfig-multikey-list-name-conflict/model/a/multi-key/state/key YANG schema element.
168+
type Model_MultiKey_Key struct {
169+
Key3 *uint8 `path:"key3" module:"openconfig-multikey-list-name-conflict"`
170+
}
171+
172+
// IsYANGGoStruct ensures that Model_MultiKey_Key implements the yang.GoStruct
173+
// interface. This allows functions that need to handle this struct to
174+
// identify it as being generated by ygen.
175+
func (*Model_MultiKey_Key) IsYANGGoStruct() {}

0 commit comments

Comments
 (0)