1package structs 2 3import ( 4 "errors" 5 "fmt" 6 "reflect" 7) 8 9var ( 10 errNotExported = errors.New("field is not exported") 11 errNotSettable = errors.New("field is not settable") 12) 13 14// Field represents a single struct field that encapsulates high level 15// functions around the field. 16type Field struct { 17 value reflect.Value 18 field reflect.StructField 19 defaultTag string 20} 21 22// Tag returns the value associated with key in the tag string. If there is no 23// such key in the tag, Tag returns the empty string. 24func (f *Field) Tag(key string) string { 25 return f.field.Tag.Get(key) 26} 27 28// Value returns the underlying value of the field. It panics if the field 29// is not exported. 30func (f *Field) Value() interface{} { 31 return f.value.Interface() 32} 33 34// IsEmbedded returns true if the given field is an anonymous field (embedded) 35func (f *Field) IsEmbedded() bool { 36 return f.field.Anonymous 37} 38 39// IsExported returns true if the given field is exported. 40func (f *Field) IsExported() bool { 41 return f.field.PkgPath == "" 42} 43 44// IsZero returns true if the given field is not initialized (has a zero value). 45// It panics if the field is not exported. 46func (f *Field) IsZero() bool { 47 zero := reflect.Zero(f.value.Type()).Interface() 48 current := f.Value() 49 50 return reflect.DeepEqual(current, zero) 51} 52 53// Name returns the name of the given field 54func (f *Field) Name() string { 55 return f.field.Name 56} 57 58// Kind returns the fields kind, such as "string", "map", "bool", etc .. 59func (f *Field) Kind() reflect.Kind { 60 return f.value.Kind() 61} 62 63// Set sets the field to given value v. It returns an error if the field is not 64// settable (not addressable or not exported) or if the given value's type 65// doesn't match the fields type. 66func (f *Field) Set(val interface{}) error { 67 // we can't set unexported fields, so be sure this field is exported 68 if !f.IsExported() { 69 return errNotExported 70 } 71 72 // do we get here? not sure... 73 if !f.value.CanSet() { 74 return errNotSettable 75 } 76 77 given := reflect.ValueOf(val) 78 79 if f.value.Kind() != given.Kind() { 80 return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind()) 81 } 82 83 f.value.Set(given) 84 return nil 85} 86 87// Zero sets the field to its zero value. It returns an error if the field is not 88// settable (not addressable or not exported). 89func (f *Field) Zero() error { 90 zero := reflect.Zero(f.value.Type()).Interface() 91 return f.Set(zero) 92} 93 94// Fields returns a slice of Fields. This is particular handy to get the fields 95// of a nested struct . A struct tag with the content of "-" ignores the 96// checking of that particular field. Example: 97// 98// // Field is ignored by this package. 99// Field *http.Request `structs:"-"` 100// 101// It panics if field is not exported or if field's kind is not struct 102func (f *Field) Fields() []*Field { 103 return getFields(f.value, f.defaultTag) 104} 105 106// Field returns the field from a nested struct. It panics if the nested struct 107// is not exported or if the field was not found. 108func (f *Field) Field(name string) *Field { 109 field, ok := f.FieldOk(name) 110 if !ok { 111 panic("field not found") 112 } 113 114 return field 115} 116 117// FieldOk returns the field from a nested struct. The boolean returns whether 118// the field was found (true) or not (false). 119func (f *Field) FieldOk(name string) (*Field, bool) { 120 value := &f.value 121 // value must be settable so we need to make sure it holds the address of the 122 // variable and not a copy, so we can pass the pointer to strctVal instead of a 123 // copy (which is not assigned to any variable, hence not settable). 124 // see "https://blog.golang.org/laws-of-reflection#TOC_8." 125 if f.value.Kind() != reflect.Ptr { 126 a := f.value.Addr() 127 value = &a 128 } 129 v := strctVal(value.Interface()) 130 t := v.Type() 131 132 field, ok := t.FieldByName(name) 133 if !ok { 134 return nil, false 135 } 136 137 return &Field{ 138 field: field, 139 value: v.FieldByName(name), 140 }, true 141} 142