/
choice.go
88 lines (78 loc) · 3.24 KB
/
choice.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ytypes
import (
"fmt"
"reflect"
"github.com/openconfig/goyang/pkg/yang"
"github.com/openconfig/ygot/util"
"github.com/openconfig/ygot/ygot"
)
// Refer to: https://tools.ietf.org/html/rfc6020#section-7.9.
// validateChoice validates a choice struct in the structValue data tree against
// the given schema. It ensures that fields from only one case are selected from
// the available set. Since a case may contain choice elements that are not
// named in the data tree, the function recurses until it reaches a named
// element in such cases. It returns all the field names that were selected in
// the data tree from the Choice schema.
func validateChoice(schema *yang.Entry, structValue ygot.GoStruct) (selected []string, errors []error) {
util.DbgPrint("validateChoice with value %s, schema name %s\n", util.ValueStrDebug(structValue), schema.Name)
// Validate that multiple cases are not selected. Since choice is always
// inside a container, there's no need to validate each individual field
// since that is part of container validation.
var selectedCases []string
for _, caseSchema := range schema.Dir {
sel, errs := IsCaseSelected(caseSchema, structValue)
selected = append(selected, sel...)
errors = util.AppendErrs(errors, errs)
if len(sel) > 0 {
selectedCases = append(selectedCases, caseSchema.Name)
}
}
if len(selectedCases) > 1 {
errors = util.AppendErr(errors, fmt.Errorf("multiple cases %v selected for choice %s", selectedCases, schema.Name))
}
return
}
// IsCaseSelected reports whether a case with the given schema has been selected
// in the given value struct. The top level of the struct is checked, and any
// choices present in the schema are recursively followed to determine whether
// any case is selected for that choice schema subtree. It returns a slice with
// the names of all fields in the case that were selected.
func IsCaseSelected(schema *yang.Entry, value interface{}) (selected []string, errors []error) {
v := reflect.ValueOf(value).Elem()
for i := 0; i < v.NumField(); i++ {
if !util.IsValueNilOrDefault(v.Field(i).Interface()) {
fieldType := v.Type().Field(i)
cs, err := util.ChildSchema(schema, fieldType)
if err != nil {
errors = util.AppendErr(errors, err)
continue
}
if cs != nil {
// Since the field is non-nil and matching schema is found for
// the field under this case, this means that this case is
// selected.
selected = append(selected, fieldType.Name)
}
}
}
for _, elemSchema := range schema.Dir {
// If element is a choice, recurse down to the next named element.
if elemSchema.IsChoice() {
return validateChoice(elemSchema, value.(ygot.GoStruct))
}
}
return
}