field: add Set() method for setting fields
This commit is contained in:
parent
747a05cbb4
commit
add65496af
@ -95,6 +95,9 @@ name := s.Field("Name")
|
|||||||
// Get the underlying value, value => "gopher"
|
// Get the underlying value, value => "gopher"
|
||||||
value := name.Value().(string)
|
value := name.Value().(string)
|
||||||
|
|
||||||
|
// Set the field's value
|
||||||
|
name.Set("another gopher")
|
||||||
|
|
||||||
// Check if the field is exported or not
|
// Check if the field is exported or not
|
||||||
if name.IsExported() {
|
if name.IsExported() {
|
||||||
fmt.Println("Name field is exported")
|
fmt.Println("Name field is exported")
|
||||||
|
|||||||
37
field.go
37
field.go
@ -1,6 +1,15 @@
|
|||||||
package structs
|
package structs
|
||||||
|
|
||||||
import "reflect"
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNotExported = errors.New("field is not exported")
|
||||||
|
errNotSettable = errors.New("field is not settable")
|
||||||
|
)
|
||||||
|
|
||||||
// Field represents a single struct field that encapsulates high level
|
// Field represents a single struct field that encapsulates high level
|
||||||
// functions around the field.
|
// functions around the field.
|
||||||
@ -45,6 +54,32 @@ func (f *Field) Name() string {
|
|||||||
return f.field.Name
|
return f.field.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set sets the field to given value v. It retuns an error if the field is not
|
||||||
|
// settable (not addresable or not exported) or if the given value's type
|
||||||
|
// doesn't match the fields type.
|
||||||
|
func (f *Field) Set(val interface{}) error {
|
||||||
|
// needed to make the field settable
|
||||||
|
v := reflect.Indirect(f.value)
|
||||||
|
|
||||||
|
if !f.IsExported() {
|
||||||
|
return errNotExported
|
||||||
|
}
|
||||||
|
|
||||||
|
// do we get here? not sure...
|
||||||
|
if !v.CanSet() {
|
||||||
|
return errNotSettable
|
||||||
|
}
|
||||||
|
|
||||||
|
given := reflect.ValueOf(val)
|
||||||
|
|
||||||
|
if v.Kind() != given.Kind() {
|
||||||
|
return fmt.Errorf("wrong kind: %s want: %s", given.Kind(), v.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set(given)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Field returns the field from a nested struct. It panics if the nested struct
|
// Field returns the field from a nested struct. It panics if the nested struct
|
||||||
// is not exported or if the field was not found.
|
// is not exported or if the field was not found.
|
||||||
func (f *Field) Field(name string) *Field {
|
func (f *Field) Field(name string) *Field {
|
||||||
|
|||||||
@ -9,7 +9,9 @@ type Foo struct {
|
|||||||
C bool `json:"c"`
|
C bool `json:"c"`
|
||||||
d string // not exported
|
d string // not exported
|
||||||
x string `xml:"x"` // not exported, with tag
|
x string `xml:"x"` // not exported, with tag
|
||||||
*Bar // embedded
|
Y []string
|
||||||
|
Z map[string]interface{}
|
||||||
|
*Bar // embedded
|
||||||
}
|
}
|
||||||
|
|
||||||
type Bar struct {
|
type Bar struct {
|
||||||
@ -30,12 +32,70 @@ func newStruct() *Struct {
|
|||||||
A: "gopher",
|
A: "gopher",
|
||||||
C: true,
|
C: true,
|
||||||
d: "small",
|
d: "small",
|
||||||
|
Y: []string{"example"},
|
||||||
|
Z: nil,
|
||||||
}
|
}
|
||||||
f.Bar = b
|
f.Bar = b
|
||||||
|
|
||||||
return New(f)
|
return New(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestField_Set(t *testing.T) {
|
||||||
|
s := newStruct()
|
||||||
|
|
||||||
|
f := s.Field("A")
|
||||||
|
err := f.Set("fatih")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Value().(string) != "fatih" {
|
||||||
|
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih")
|
||||||
|
}
|
||||||
|
|
||||||
|
f = s.Field("Y")
|
||||||
|
err = f.Set([]string{"override", "with", "this"})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceLen := len(f.Value().([]string))
|
||||||
|
if sliceLen != 3 {
|
||||||
|
t.Errorf("Setted values slice length is wrong: %d, want: %d", sliceLen, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
f = s.Field("C")
|
||||||
|
err = f.Set(false)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Value().(bool) {
|
||||||
|
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(bool), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's pass a different type
|
||||||
|
f = s.Field("A")
|
||||||
|
err = f.Set(123) // Field A is of type string, but we are going to pass an integer
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Setting a field's value with a different type than the field's type should return an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// old value should be still there :)
|
||||||
|
if f.Value().(string) != "fatih" {
|
||||||
|
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih")
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's access an unexported field, which should give an error
|
||||||
|
f = s.Field("d")
|
||||||
|
err = f.Set("large")
|
||||||
|
if err != errNotExported {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: let's access a non addresable field, which should give an error
|
||||||
|
}
|
||||||
|
|
||||||
func TestField(t *testing.T) {
|
func TestField(t *testing.T) {
|
||||||
s := newStruct()
|
s := newStruct()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user