Takahiro Suzuki | d7bf820 | 2020-12-17 20:21:59 +0900 | [diff] [blame] | 1 | package ndr |
| 2 | |
| 3 | import ( |
| 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"` |
| 14 | type Union interface { |
| 15 | SwitchFunc(t interface{}) string |
| 16 | } |
| 17 | |
| 18 | // Union related constants such as struct tag values |
| 19 | const ( |
| 20 | unionSelectionFuncName = "SwitchFunc" |
| 21 | TagEncapsulated = "encapsulated" |
| 22 | TagUnionTag = "unionTag" |
| 23 | TagUnionField = "unionField" |
| 24 | ) |
| 25 | |
| 26 | func (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 |
| 42 | func 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 | } |