| package structs |
| |
| 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 |
| // functions around the field. |
| type Field struct { |
| value reflect.Value |
| field reflect.StructField |
| defaultTag string |
| } |
| |
| // Tag returns the value associated with key in the tag string. If there is no |
| // such key in the tag, Tag returns the empty string. |
| func (f *Field) Tag(key string) string { |
| return f.field.Tag.Get(key) |
| } |
| |
| // Value returns the underlying value of the field. It panics if the field |
| // is not exported. |
| func (f *Field) Value() interface{} { |
| return f.value.Interface() |
| } |
| |
| // IsEmbedded returns true if the given field is an anonymous field (embedded) |
| func (f *Field) IsEmbedded() bool { |
| return f.field.Anonymous |
| } |
| |
| // IsExported returns true if the given field is exported. |
| func (f *Field) IsExported() bool { |
| return f.field.PkgPath == "" |
| } |
| |
| // IsZero returns true if the given field is not initialized (has a zero value). |
| // It panics if the field is not exported. |
| func (f *Field) IsZero() bool { |
| zero := reflect.Zero(f.value.Type()).Interface() |
| current := f.Value() |
| |
| return reflect.DeepEqual(current, zero) |
| } |
| |
| // Name returns the name of the given field |
| func (f *Field) Name() string { |
| return f.field.Name |
| } |
| |
| // Kind returns the fields kind, such as "string", "map", "bool", etc .. |
| func (f *Field) Kind() reflect.Kind { |
| return f.value.Kind() |
| } |
| |
| // Set sets the field to given value v. It returns an error if the field is not |
| // settable (not addressable or not exported) or if the given value's type |
| // doesn't match the fields type. |
| func (f *Field) Set(val interface{}) error { |
| // we can't set unexported fields, so be sure this field is exported |
| if !f.IsExported() { |
| return errNotExported |
| } |
| |
| // do we get here? not sure... |
| if !f.value.CanSet() { |
| return errNotSettable |
| } |
| |
| given := reflect.ValueOf(val) |
| |
| if f.value.Kind() != given.Kind() { |
| return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind()) |
| } |
| |
| f.value.Set(given) |
| return nil |
| } |
| |
| // Zero sets the field to its zero value. It returns an error if the field is not |
| // settable (not addressable or not exported). |
| func (f *Field) Zero() error { |
| zero := reflect.Zero(f.value.Type()).Interface() |
| return f.Set(zero) |
| } |
| |
| // Fields returns a slice of Fields. This is particular handy to get the fields |
| // of a nested struct . A struct tag with the content of "-" ignores the |
| // checking of that particular field. Example: |
| // |
| // // Field is ignored by this package. |
| // Field *http.Request `structs:"-"` |
| // |
| // It panics if field is not exported or if field's kind is not struct |
| func (f *Field) Fields() []*Field { |
| return getFields(f.value, f.defaultTag) |
| } |
| |
| // Field returns the field from a nested struct. It panics if the nested struct |
| // is not exported or if the field was not found. |
| func (f *Field) Field(name string) *Field { |
| field, ok := f.FieldOk(name) |
| if !ok { |
| panic("field not found") |
| } |
| |
| return field |
| } |
| |
| // FieldOk returns the field from a nested struct. The boolean returns whether |
| // the field was found (true) or not (false). |
| func (f *Field) FieldOk(name string) (*Field, bool) { |
| value := &f.value |
| // value must be settable so we need to make sure it holds the address of the |
| // variable and not a copy, so we can pass the pointer to strctVal instead of a |
| // copy (which is not assigned to any variable, hence not settable). |
| // see "https://blog.golang.org/laws-of-reflection#TOC_8." |
| if f.value.Kind() != reflect.Ptr { |
| a := f.value.Addr() |
| value = &a |
| } |
| v := strctVal(value.Interface()) |
| t := v.Type() |
| |
| field, ok := t.FieldByName(name) |
| if !ok { |
| return nil, false |
| } |
| |
| return &Field{ |
| field: field, |
| value: v.FieldByName(name), |
| }, true |
| } |