blob: 6a657fa6f660e997de598969147f751b30476e54 [file] [log] [blame]
Takahiro Suzuki241c10e2020-12-17 20:17:57 +09001package ndr
2
3import (
4 "errors"
5 "fmt"
6 "reflect"
7)
8
9// Union interface must be implemented by structs that will be unmarshaled into from the NDR byte stream union representation.
10// The union's discriminating tag will be passed to the SwitchFunc method.
11// The discriminating tag field must have the struct tag: `ndr:"unionTag"`
12// If the union is encapsulated the discriminating tag field must have the struct tag: `ndr:"encapsulated"`
13// The possible value fields that can be selected from must have the struct tag: `ndr:"unionField"`
14type Union interface {
15 SwitchFunc(t interface{}) string
16}
17
18// Union related constants such as struct tag values
19const (
20 unionSelectionFuncName = "SwitchFunc"
21 TagEncapsulated = "encapsulated"
22 TagUnionTag = "unionTag"
23 TagUnionField = "unionField"
24)
25
26func (dec *Decoder) isUnion(field reflect.Value, tag reflect.StructTag) (r reflect.Value) {
27 ndrTag := parseTags(tag)
28 if !ndrTag.HasValue(TagUnionTag) {
29 return
30 }
31 r = field
32 // For a non-encapsulated union, the discriminant is marshalled into the transmitted data stream twice: once as the
33 // field or parameter, which is referenced by the switch_is construct, in the procedure argument list; and once as
34 // the first part of the union representation.
35 if !ndrTag.HasValue(TagEncapsulated) {
36 dec.r.Discard(int(r.Type().Size()))
37 }
38 return
39}
40
41// unionSelectedField returns the field name of which of the union values to fill
42func unionSelectedField(union, discriminant reflect.Value) (string, error) {
43 if !union.Type().Implements(reflect.TypeOf(new(Union)).Elem()) {
44 return "", errors.New("struct does not implement union interface")
45 }
46 args := []reflect.Value{discriminant}
47 // Call the SelectFunc of the union struct to find the name of the field to fill with the value selected.
48 sf := union.MethodByName(unionSelectionFuncName)
49 if !sf.IsValid() {
50 return "", fmt.Errorf("could not find a selection function called %s in the unions struct representation", unionSelectionFuncName)
51 }
52 f := sf.Call(args)
53 if f[0].Kind() != reflect.String || f[0].String() == "" {
54 return "", fmt.Errorf("the union select function did not return a string for the name of the field to fill")
55 }
56 return f[0].String(), nil
57}