github.com/safing/portbase@v0.19.5/database/accessor/accessor-struct.go (about) 1 package accessor 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 ) 8 9 // StructAccessor is a json string with get functions. 10 type StructAccessor struct { 11 object reflect.Value 12 } 13 14 // NewStructAccessor adds the Accessor interface to a JSON string. 15 func NewStructAccessor(object interface{}) *StructAccessor { 16 return &StructAccessor{ 17 object: reflect.ValueOf(object).Elem(), 18 } 19 } 20 21 // Set sets the value identified by key. 22 func (sa *StructAccessor) Set(key string, value interface{}) error { 23 field := sa.object.FieldByName(key) 24 if !field.IsValid() { 25 return errors.New("struct field does not exist") 26 } 27 if !field.CanSet() { 28 return fmt.Errorf("field %s or struct is immutable", field.String()) 29 } 30 31 newVal := reflect.ValueOf(value) 32 33 // set directly if type matches 34 if newVal.Kind() == field.Kind() { 35 field.Set(newVal) 36 return nil 37 } 38 39 // handle special cases 40 switch field.Kind() { // nolint:exhaustive 41 42 // ints 43 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 44 var newInt int64 45 switch newVal.Kind() { // nolint:exhaustive 46 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 47 newInt = newVal.Int() 48 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 49 newInt = int64(newVal.Uint()) 50 default: 51 return fmt.Errorf("tried to set field %s (%s) to a %s value", key, field.Kind().String(), newVal.Kind().String()) 52 } 53 if field.OverflowInt(newInt) { 54 return fmt.Errorf("setting field %s (%s) to %d would overflow", key, field.Kind().String(), newInt) 55 } 56 field.SetInt(newInt) 57 58 // uints 59 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 60 var newUint uint64 61 switch newVal.Kind() { // nolint:exhaustive 62 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 63 newUint = uint64(newVal.Int()) 64 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 65 newUint = newVal.Uint() 66 default: 67 return fmt.Errorf("tried to set field %s (%s) to a %s value", key, field.Kind().String(), newVal.Kind().String()) 68 } 69 if field.OverflowUint(newUint) { 70 return fmt.Errorf("setting field %s (%s) to %d would overflow", key, field.Kind().String(), newUint) 71 } 72 field.SetUint(newUint) 73 74 // floats 75 case reflect.Float32, reflect.Float64: 76 switch newVal.Kind() { // nolint:exhaustive 77 case reflect.Float32, reflect.Float64: 78 field.SetFloat(newVal.Float()) 79 default: 80 return fmt.Errorf("tried to set field %s (%s) to a %s value", key, field.Kind().String(), newVal.Kind().String()) 81 } 82 default: 83 return fmt.Errorf("tried to set field %s (%s) to a %s value", key, field.Kind().String(), newVal.Kind().String()) 84 } 85 86 return nil 87 } 88 89 // Get returns the value found by the given json key and whether it could be successfully extracted. 90 func (sa *StructAccessor) Get(key string) (value interface{}, ok bool) { 91 field := sa.object.FieldByName(key) 92 if !field.IsValid() || !field.CanInterface() { 93 return nil, false 94 } 95 return field.Interface(), true 96 } 97 98 // GetString returns the string found by the given json key and whether it could be successfully extracted. 99 func (sa *StructAccessor) GetString(key string) (value string, ok bool) { 100 field := sa.object.FieldByName(key) 101 if !field.IsValid() || field.Kind() != reflect.String { 102 return "", false 103 } 104 return field.String(), true 105 } 106 107 // GetStringArray returns the []string found by the given json key and whether it could be successfully extracted. 108 func (sa *StructAccessor) GetStringArray(key string) (value []string, ok bool) { 109 field := sa.object.FieldByName(key) 110 if !field.IsValid() || field.Kind() != reflect.Slice || !field.CanInterface() { 111 return nil, false 112 } 113 v := field.Interface() 114 slice, ok := v.([]string) 115 if !ok { 116 return nil, false 117 } 118 return slice, true 119 } 120 121 // GetInt returns the int found by the given json key and whether it could be successfully extracted. 122 func (sa *StructAccessor) GetInt(key string) (value int64, ok bool) { 123 field := sa.object.FieldByName(key) 124 if !field.IsValid() { 125 return 0, false 126 } 127 switch field.Kind() { // nolint:exhaustive 128 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 129 return field.Int(), true 130 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 131 return int64(field.Uint()), true 132 default: 133 return 0, false 134 } 135 } 136 137 // GetFloat returns the float found by the given json key and whether it could be successfully extracted. 138 func (sa *StructAccessor) GetFloat(key string) (value float64, ok bool) { 139 field := sa.object.FieldByName(key) 140 if !field.IsValid() { 141 return 0, false 142 } 143 switch field.Kind() { // nolint:exhaustive 144 case reflect.Float32, reflect.Float64: 145 return field.Float(), true 146 default: 147 return 0, false 148 } 149 } 150 151 // GetBool returns the bool found by the given json key and whether it could be successfully extracted. 152 func (sa *StructAccessor) GetBool(key string) (value bool, ok bool) { 153 field := sa.object.FieldByName(key) 154 if !field.IsValid() || field.Kind() != reflect.Bool { 155 return false, false 156 } 157 return field.Bool(), true 158 } 159 160 // Exists returns the whether the given key exists. 161 func (sa *StructAccessor) Exists(key string) bool { 162 field := sa.object.FieldByName(key) 163 return field.IsValid() 164 } 165 166 // Type returns the accessor type as a string. 167 func (sa *StructAccessor) Type() string { 168 return "StructAccessor" 169 }