pseudounion.go 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. package pseudounion
  2. import "reflect"
  3. type Tag int
  4. var tagType = reflect.TypeOf(Tag(0))
  5. func Load(u interface{}) interface{} {
  6. var ru = reflect.ValueOf(u)
  7. if ru.Kind() != reflect.Struct {
  8. checkKind(ru, reflect.Pointer)
  9. ru = ru.Elem()
  10. checkKind(ru, reflect.Struct)
  11. }
  12. var index = int(assertTagType(ru.Field(0)).Int())
  13. if index <= 0 {
  14. panic("invalid pseudo-union index: zero or negative")
  15. }
  16. if index > ru.NumField() {
  17. panic("invalid pseudo-union index: too big")
  18. }
  19. return ru.Field(index).Interface()
  20. }
  21. func Store[T any] (v interface{}) T {
  22. var u T
  23. var ru = reflect.ValueOf(&u).Elem()
  24. if ru.Kind() != reflect.Struct {
  25. checkKind(ru, reflect.Pointer)
  26. ru.Set(reflect.New(ru.Type().Elem()))
  27. ru = ru.Elem()
  28. checkKind(ru, reflect.Struct)
  29. }
  30. var rv = reflect.ValueOf(v)
  31. for i := 1; i < ru.NumField(); i += 1 {
  32. if rv.Type().AssignableTo(ru.Type().Field(i).Type) {
  33. ru.Field(i).Set(rv)
  34. assertTagType(ru.Field(0)).SetInt(int64(i))
  35. return u
  36. }
  37. }
  38. panic("invalid pseudo-union value: none of fields assignable")
  39. }
  40. func checkKind(ru reflect.Value, expected reflect.Kind) {
  41. if ru.Kind() != expected {
  42. panic("invalid pseudo-union type: " + ru.Type().String())
  43. }
  44. }
  45. func assertTagType(rv reflect.Value) reflect.Value {
  46. if rv.Type() != tagType {
  47. panic("invalid pseudo-union")
  48. }
  49. return rv
  50. }