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  }