Skip to content

Instantly share code, notes, and snippets.

@oslyak
Last active November 19, 2022 20:11
Show Gist options
  • Save oslyak/081e6462570ce2af417fddd7db97c082 to your computer and use it in GitHub Desktop.
Save oslyak/081e6462570ce2af417fddd7db97c082 to your computer and use it in GitHub Desktop.
reflect.go Get and Set struct field by Name
package utils
import (
"errors"
"fmt"
"reflect"
)
var (
ErrReflectStructPointerExpected = errors.New("pointer to struct expected")
ErrReflectFieldDoesNotExist = errors.New("provided field does not exists")
ErrReflectPrivateField = errors.New("can not set private field")
ErrReflectGenerictType = errors.New("generic type does not match type of provided field")
)
// Set struct field by fieldName to value.
// structPtr - should be poiter ro struct.
func SetField(structPtr interface{}, fieldName string, value interface{}) error {
if reflect.TypeOf(structPtr).Kind() != reflect.Ptr {
return ErrReflectStructPointerExpected
}
if reflect.TypeOf(structPtr).Elem().Kind() != reflect.Struct {
return ErrReflectStructPointerExpected
}
pointerToStruct := reflect.ValueOf(structPtr)
strct := pointerToStruct.Elem()
field := strct.FieldByName(fieldName) // type: reflect.Value
if !field.IsValid() {
return fmt.Errorf(`%w: %s`, ErrReflectFieldDoesNotExist, fieldName)
}
if !field.CanSet() {
return fmt.Errorf(`%w: %s`, ErrReflectPrivateField, fieldName)
}
field.Set(reflect.ValueOf(value))
return nil
}
// Get struct field by fieldName to value.
// structPtr - should be poiter ro struct.
// Generic tyoe - should be type of struct field.
func GetField[T any](structPtr interface{}, fieldName string) (T, error) {
var result T
if reflect.TypeOf(structPtr).Kind() != reflect.Ptr {
return result, ErrReflectStructPointerExpected
}
if reflect.TypeOf(structPtr).Elem().Kind() != reflect.Struct {
return result, ErrReflectStructPointerExpected
}
pointerToStruct := reflect.ValueOf(structPtr)
strct := pointerToStruct.Elem()
field := strct.FieldByName(fieldName) // type: reflect.Value
if !field.IsValid() {
return result, fmt.Errorf(`%w: %s`, ErrReflectFieldDoesNotExist, fieldName)
}
if !field.CanSet() {
return result, fmt.Errorf(`%w: %s`, ErrReflectPrivateField, fieldName)
}
result, ok := field.Interface().(T)
if !ok {
return result, ErrReflectGenerictType
}
return result, nil
}
// Get string with Generic T type name.
func GetTypeName[T any]() string {
var obj T
return reflect.TypeOf(obj).String()
}
package utils_test
import (
"testing"
"newone/pkg/utils"
"github.com/stretchr/testify/require"
)
type userType struct {
ID int64
FullName string
Login string
Age int
private string
}
func TestSetField(t *testing.T) {
var (
err error
user userType
expect userType
)
user = userType{ID: 73, FullName: "Super Man", Login: "superman", Age: 43, private: "secret data"}
expect = userType{ID: 42, FullName: "Clark Joseph Kent", Login: "c.kent", Age: 36, private: "another private data"}
err = utils.SetField(user, `Age`, expect.Age)
require.Error(t, err)
require.ErrorIs(t, err, utils.ErrReflectStructPointerExpected)
err = utils.SetField(new(string), `Age`, expect.Age)
require.Error(t, err)
require.ErrorIs(t, err, utils.ErrReflectStructPointerExpected)
err = utils.SetField(&user, `InvalidField`, expect.Age)
require.Error(t, err)
require.ErrorIs(t, err, utils.ErrReflectFieldDoesNotExist)
err = utils.SetField(&user, `private`, expect.private)
require.Error(t, err)
require.ErrorIs(t, err, utils.ErrReflectPrivateField)
err = utils.SetField(&user, `FullName`, expect.FullName)
require.NoError(t, err)
require.Equal(t, expect.FullName, user.FullName)
err = utils.SetField(&user, `Login`, expect.Login)
require.NoError(t, err)
require.Equal(t, expect.Login, user.Login)
err = utils.SetField(&user, `Age`, expect.Age)
require.NoError(t, err)
require.Equal(t, expect.Age, user.Age)
err = utils.SetField(&user, `ID`, expect.ID)
require.NoError(t, err)
require.Equal(t, expect.ID, user.ID)
}
func TestGetField(t *testing.T) {
var (
err error
user userType
value any
)
user = userType{ID: 42, FullName: "Clark Joseph Kent", Login: "c.kent", Age: 36, private: "another private data"}
value, err = utils.GetField[string](user, `Age`)
require.Empty(t, value)
require.Error(t, err)
require.ErrorIs(t, err, utils.ErrReflectStructPointerExpected)
value, err = utils.GetField[string](new(string), `Age`)
require.Empty(t, value)
require.Error(t, err)
require.ErrorIs(t, err, utils.ErrReflectStructPointerExpected)
value, err = utils.GetField[string](&user, `InvalidField`)
require.Empty(t, value)
require.Error(t, err)
require.ErrorIs(t, err, utils.ErrReflectFieldDoesNotExist)
value, err = utils.GetField[string](&user, `private`)
require.Empty(t, value)
require.Error(t, err)
require.ErrorIs(t, err, utils.ErrReflectPrivateField)
value, err = utils.GetField[string](&user, `Age`)
require.Empty(t, value)
require.Error(t, err)
require.ErrorIs(t, err, utils.ErrReflectGenerictType)
value, err = utils.GetField[string](&user, `FullName`)
require.NoError(t, err)
require.Equal(t, value, user.FullName)
value, err = utils.GetField[string](&user, `Login`)
require.NoError(t, err)
require.Equal(t, value, user.Login)
value, err = utils.GetField[int](&user, `Age`)
require.NoError(t, err)
require.Equal(t, value, user.Age)
value, err = utils.GetField[int64](&user, `ID`)
require.NoError(t, err)
require.Equal(t, value, user.ID)
}
func TestGetTypeName(t *testing.T) {
var typeName string
typeName = utils.GetTypeName[string]()
require.Equal(t, `string`, typeName)
typeName = utils.GetTypeName[uintptr]()
require.Equal(t, `uintptr`, typeName)
typeName = utils.GetTypeName[int]()
require.Equal(t, `int`, typeName)
typeName = utils.GetTypeName[int8]()
require.Equal(t, `int8`, typeName)
typeName = utils.GetTypeName[int16]()
require.Equal(t, `int16`, typeName)
typeName = utils.GetTypeName[int32]()
require.Equal(t, `int32`, typeName)
typeName = utils.GetTypeName[int64]()
require.Equal(t, `int64`, typeName)
typeName = utils.GetTypeName[userType]()
require.Equal(t, `utils_test.userType`, typeName)
typeName = utils.GetTypeName[*userType]()
require.Equal(t, `*utils_test.userType`, typeName)
typeName = utils.GetTypeName[[]userType]()
require.Equal(t, `[]utils_test.userType`, typeName)
typeName = utils.GetTypeName[[]*userType]()
require.Equal(t, `[]*utils_test.userType`, typeName)
typeName = utils.GetTypeName[struct{}]()
require.Equal(t, `struct {}`, typeName)
typeName = utils.GetTypeName[*struct{}]()
require.Equal(t, `*struct {}`, typeName)
typeName = utils.GetTypeName[[]struct{}]()
require.Equal(t, `[]struct {}`, typeName)
typeName = utils.GetTypeName[[]*struct{}]()
require.Equal(t, `[]*struct {}`, typeName)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment