union.go 2.2 KB

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