goyave.dev/goyave/v4@v4.4.11/util/reflectutil/reflectutil.go (about)

     1  package reflectutil
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	"goyave.dev/goyave/v4/util/sliceutil"
     8  )
     9  
    10  // Only extracts the requested field from the given map[string] or structure and
    11  // returns a map[string]interface{} containing only those values.
    12  //
    13  // For example:
    14  //
    15  //	 type Model struct {
    16  //	   Field string
    17  //	   Num   int
    18  //	   Slice []float64
    19  //	 }
    20  //	 model := Model{
    21  //		  Field: "value",
    22  //		  Num:   42,
    23  //		  Slice: []float64{3, 6, 9},
    24  //	 }
    25  //	 res := reflectutil.Only(model, "Field", "Slice")
    26  //
    27  // Result:
    28  //
    29  //	 map[string]interface{}{
    30  //		  "Field": "value",
    31  //		  "Slice": []float64{3, 6, 9},
    32  //	 }
    33  //
    34  // In case of conflicting fields (if a promoted field has the same name as a parent's
    35  // struct field), the higher level field is kept.
    36  func Only(data interface{}, fields ...string) map[string]interface{} {
    37  	result := make(map[string]interface{}, len(fields))
    38  	t := reflect.TypeOf(data)
    39  	value := reflect.ValueOf(data)
    40  	if t.Kind() == reflect.Ptr {
    41  		t = t.Elem()
    42  		value = value.Elem()
    43  	}
    44  
    45  	if !value.IsValid() {
    46  		return result
    47  	}
    48  
    49  	switch t.Kind() {
    50  	case reflect.Map:
    51  		if t.Key().Kind() != reflect.String {
    52  			panic(fmt.Errorf("reflectutil.Only only supports map[string] and structures, %s given", t.String()))
    53  		}
    54  		for _, k := range value.MapKeys() {
    55  			name := k.String()
    56  			if sliceutil.ContainsStr(fields, name) {
    57  				result[name] = value.MapIndex(k).Interface()
    58  			}
    59  		}
    60  	case reflect.Struct:
    61  		for i := 0; i < t.NumField(); i++ {
    62  			field := value.Field(i)
    63  			strctType := t.Field(i)
    64  			fieldType := strctType.Type
    65  			if fieldType.Kind() == reflect.Ptr {
    66  				fieldType = fieldType.Elem()
    67  			}
    68  			name := strctType.Name
    69  			if fieldType.Kind() == reflect.Struct && strctType.Anonymous {
    70  				for k, v := range Only(field.Interface(), fields...) {
    71  					// Check if fields are conflicting
    72  					// Highest level fields have priority
    73  					if _, ok := result[k]; !ok {
    74  						result[k] = v
    75  					}
    76  				}
    77  			} else if sliceutil.ContainsStr(fields, name) {
    78  				result[name] = value.Field(i).Interface()
    79  			}
    80  		}
    81  	default:
    82  		panic(fmt.Errorf("reflectutil.Only only supports map[string] and structures, %s given", t.Kind()))
    83  	}
    84  
    85  	return result
    86  }