mirror of
https://github.com/minio/minio.git
synced 2025-02-05 10:48:07 -05:00
61175ef091
- over the course of a project history every maintainer needs to update its dependency packages, the problem essentially with godep is manipulating GOPATH - this manipulation leads to static objects created at different locations which end up conflicting with the overall functionality of golang. This also leads to broken builds. There is no easier way out of this other than asking developers to do 'godep restore' all the time. Which perhaps as a practice doesn't sound like a clean solution. On the other hand 'godep restore' has its own set of problems. - govendor is a right tool but a stop gap tool until we wait for golangs official 1.5 version which fixes this vendoring issue once and for all. - govendor provides consistency in terms of how import paths should be handled unlike manipulation GOPATH. This has advantages - no more compiled objects being referenced in GOPATH and build time GOPATH manging which leads to conflicts. - proper import paths referencing the exact package a project is dependent on. govendor is simple and provides the minimal necessary tooling to achieve this. For now this is the right solution.
450 lines
12 KiB
Go
450 lines
12 KiB
Go
// Package structs contains various utilities functions to work with structs.
|
|
package structs
|
|
|
|
import "reflect"
|
|
|
|
var (
|
|
// DefaultTagName is the default tag name for struct fields which provides
|
|
// a more granular to tweak certain structs. Lookup the necessary functions
|
|
// for more info.
|
|
DefaultTagName = "structs" // struct's field default tag name
|
|
)
|
|
|
|
// Struct encapsulates a struct type to provide several high level functions
|
|
// around the struct.
|
|
type Struct struct {
|
|
raw interface{}
|
|
value reflect.Value
|
|
TagName string
|
|
}
|
|
|
|
// New returns a new *Struct with the struct s. It panics if the s's kind is
|
|
// not struct.
|
|
func New(s interface{}) *Struct {
|
|
return &Struct{
|
|
raw: s,
|
|
value: strctVal(s),
|
|
TagName: DefaultTagName,
|
|
}
|
|
}
|
|
|
|
// Map converts the given struct to a map[string]interface{}, where the keys
|
|
// of the map are the field names and the values of the map the associated
|
|
// values of the fields. The default key string is the struct field name but
|
|
// can be changed in the struct field's tag value. The "structs" key in the
|
|
// struct's field tag value is the key name. Example:
|
|
//
|
|
// // Field appears in map as key "myName".
|
|
// Name string `structs:"myName"`
|
|
//
|
|
// A tag value with the content of "-" ignores that particular field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// A tag value with the option of "omitnested" stops iterating further if the type
|
|
// is a struct. Example:
|
|
//
|
|
// // Field is not processed further by this package.
|
|
// Field time.Time `structs:"myName,omitnested"`
|
|
// Field *http.Request `structs:",omitnested"`
|
|
//
|
|
// A tag value with the option of "omitempty" ignores that particular field if
|
|
// the field value is empty. Example:
|
|
//
|
|
// // Field appears in map as key "myName", but the field is
|
|
// // skipped if empty.
|
|
// Field string `structs:"myName,omitempty"`
|
|
//
|
|
// // Field appears in map as key "Field" (the default), but
|
|
// // the field is skipped if empty.
|
|
// Field string `structs:",omitempty"`
|
|
//
|
|
// Note that only exported fields of a struct can be accessed, non exported
|
|
// fields will be neglected.
|
|
func (s *Struct) Map() map[string]interface{} {
|
|
out := make(map[string]interface{})
|
|
|
|
fields := s.structFields()
|
|
|
|
for _, field := range fields {
|
|
name := field.Name
|
|
val := s.value.FieldByName(name)
|
|
|
|
var finalVal interface{}
|
|
|
|
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
if tagName != "" {
|
|
name = tagName
|
|
}
|
|
|
|
// if the value is a zero value and the field is marked as omitempty do
|
|
// not include
|
|
if tagOpts.Has("omitempty") {
|
|
zero := reflect.Zero(val.Type()).Interface()
|
|
current := val.Interface()
|
|
|
|
if reflect.DeepEqual(current, zero) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
// look out for embedded structs, and convert them to a
|
|
// map[string]interface{} too
|
|
n := New(val.Interface())
|
|
n.TagName = s.TagName
|
|
finalVal = n.Map()
|
|
} else {
|
|
finalVal = val.Interface()
|
|
}
|
|
|
|
out[name] = finalVal
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
// Values converts the given s struct's field values to a []interface{}. A
|
|
// struct tag with the content of "-" ignores the that particular field.
|
|
// Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field int `structs:"-"`
|
|
//
|
|
// A value with the option of "omitnested" stops iterating further if the type
|
|
// is a struct. Example:
|
|
//
|
|
// // Fields is not processed further by this package.
|
|
// Field time.Time `structs:",omitnested"`
|
|
// Field *http.Request `structs:",omitnested"`
|
|
//
|
|
// A tag value with the option of "omitempty" ignores that particular field and
|
|
// is not added to the values if the field value is empty. Example:
|
|
//
|
|
// // Field is skipped if empty
|
|
// Field string `structs:",omitempty"`
|
|
//
|
|
// Note that only exported fields of a struct can be accessed, non exported
|
|
// fields will be neglected.
|
|
func (s *Struct) Values() []interface{} {
|
|
fields := s.structFields()
|
|
|
|
var t []interface{}
|
|
|
|
for _, field := range fields {
|
|
val := s.value.FieldByName(field.Name)
|
|
|
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
|
|
// if the value is a zero value and the field is marked as omitempty do
|
|
// not include
|
|
if tagOpts.Has("omitempty") {
|
|
zero := reflect.Zero(val.Type()).Interface()
|
|
current := val.Interface()
|
|
|
|
if reflect.DeepEqual(current, zero) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
// look out for embedded structs, and convert them to a
|
|
// []interface{} to be added to the final values slice
|
|
for _, embeddedVal := range Values(val.Interface()) {
|
|
t = append(t, embeddedVal)
|
|
}
|
|
} else {
|
|
t = append(t, val.Interface())
|
|
}
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
// Fields returns a slice of Fields. A struct tag with the content of "-"
|
|
// ignores the checking of that particular field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// It panics if s's kind is not struct.
|
|
func (s *Struct) Fields() []*Field {
|
|
return getFields(s.value, s.TagName)
|
|
}
|
|
|
|
// Names returns a slice of field names. A struct tag with the content of "-"
|
|
// ignores the checking of that particular field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// It panics if s's kind is not struct.
|
|
func (s *Struct) Names() []string {
|
|
fields := getFields(s.value, s.TagName)
|
|
|
|
names := make([]string, len(fields))
|
|
|
|
for i, field := range fields {
|
|
names[i] = field.Name()
|
|
}
|
|
|
|
return names
|
|
}
|
|
|
|
func getFields(v reflect.Value, tagName string) []*Field {
|
|
if v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
t := v.Type()
|
|
|
|
var fields []*Field
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
field := t.Field(i)
|
|
|
|
if tag := field.Tag.Get(tagName); tag == "-" {
|
|
continue
|
|
}
|
|
|
|
f := &Field{
|
|
field: field,
|
|
value: v.FieldByName(field.Name),
|
|
}
|
|
|
|
fields = append(fields, f)
|
|
|
|
}
|
|
|
|
return fields
|
|
}
|
|
|
|
// Field returns a new Field struct that provides several high level functions
|
|
// around a single struct field entity. It panics if the field is not found.
|
|
func (s *Struct) Field(name string) *Field {
|
|
f, ok := s.FieldOk(name)
|
|
if !ok {
|
|
panic("field not found")
|
|
}
|
|
|
|
return f
|
|
}
|
|
|
|
// Field returns a new Field struct that provides several high level functions
|
|
// around a single struct field entity. The boolean returns true if the field
|
|
// was found.
|
|
func (s *Struct) FieldOk(name string) (*Field, bool) {
|
|
t := s.value.Type()
|
|
|
|
field, ok := t.FieldByName(name)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
return &Field{
|
|
field: field,
|
|
value: s.value.FieldByName(name),
|
|
defaultTag: s.TagName,
|
|
}, true
|
|
}
|
|
|
|
// IsZero returns true if all fields in a struct is a zero value (not
|
|
// initialized) A struct tag with the content of "-" ignores the checking of
|
|
// that particular field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// A value with the option of "omitnested" stops iterating further if the type
|
|
// is a struct. Example:
|
|
//
|
|
// // Field is not processed further by this package.
|
|
// Field time.Time `structs:"myName,omitnested"`
|
|
// Field *http.Request `structs:",omitnested"`
|
|
//
|
|
// Note that only exported fields of a struct can be accessed, non exported
|
|
// fields will be neglected. It panics if s's kind is not struct.
|
|
func (s *Struct) IsZero() bool {
|
|
fields := s.structFields()
|
|
|
|
for _, field := range fields {
|
|
val := s.value.FieldByName(field.Name)
|
|
|
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
|
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
ok := IsZero(val.Interface())
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// zero value of the given field, such as "" for string, 0 for int
|
|
zero := reflect.Zero(val.Type()).Interface()
|
|
|
|
// current value of the given field
|
|
current := val.Interface()
|
|
|
|
if !reflect.DeepEqual(current, zero) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// HasZero returns true if a field in a struct is not initialized (zero value).
|
|
// A struct tag with the content of "-" ignores the checking of that particular
|
|
// field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// A value with the option of "omitnested" stops iterating further if the type
|
|
// is a struct. Example:
|
|
//
|
|
// // Field is not processed further by this package.
|
|
// Field time.Time `structs:"myName,omitnested"`
|
|
// Field *http.Request `structs:",omitnested"`
|
|
//
|
|
// Note that only exported fields of a struct can be accessed, non exported
|
|
// fields will be neglected. It panics if s's kind is not struct.
|
|
func (s *Struct) HasZero() bool {
|
|
fields := s.structFields()
|
|
|
|
for _, field := range fields {
|
|
val := s.value.FieldByName(field.Name)
|
|
|
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
|
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
ok := HasZero(val.Interface())
|
|
if ok {
|
|
return true
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// zero value of the given field, such as "" for string, 0 for int
|
|
zero := reflect.Zero(val.Type()).Interface()
|
|
|
|
// current value of the given field
|
|
current := val.Interface()
|
|
|
|
if reflect.DeepEqual(current, zero) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Name returns the structs's type name within its package. For more info refer
|
|
// to Name() function.
|
|
func (s *Struct) Name() string {
|
|
return s.value.Type().Name()
|
|
}
|
|
|
|
// structFields returns the exported struct fields for a given s struct. This
|
|
// is a convenient helper method to avoid duplicate code in some of the
|
|
// functions.
|
|
func (s *Struct) structFields() []reflect.StructField {
|
|
t := s.value.Type()
|
|
|
|
var f []reflect.StructField
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
field := t.Field(i)
|
|
// we can't access the value of unexported fields
|
|
if field.PkgPath != "" {
|
|
continue
|
|
}
|
|
|
|
// don't check if it's omitted
|
|
if tag := field.Tag.Get(s.TagName); tag == "-" {
|
|
continue
|
|
}
|
|
|
|
f = append(f, field)
|
|
}
|
|
|
|
return f
|
|
}
|
|
|
|
func strctVal(s interface{}) reflect.Value {
|
|
v := reflect.ValueOf(s)
|
|
|
|
// if pointer get the underlying element≤
|
|
if v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
if v.Kind() != reflect.Struct {
|
|
panic("not struct")
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
// Map converts the given struct to a map[string]interface{}. For more info
|
|
// refer to Struct types Map() method. It panics if s's kind is not struct.
|
|
func Map(s interface{}) map[string]interface{} {
|
|
return New(s).Map()
|
|
}
|
|
|
|
// Values converts the given struct to a []interface{}. For more info refer to
|
|
// Struct types Values() method. It panics if s's kind is not struct.
|
|
func Values(s interface{}) []interface{} {
|
|
return New(s).Values()
|
|
}
|
|
|
|
// Fields returns a slice of *Field. For more info refer to Struct types
|
|
// Fields() method. It panics if s's kind is not struct.
|
|
func Fields(s interface{}) []*Field {
|
|
return New(s).Fields()
|
|
}
|
|
|
|
// Names returns a slice of field names. For more info refer to Struct types
|
|
// Names() method. It panics if s's kind is not struct.
|
|
func Names(s interface{}) []string {
|
|
return New(s).Names()
|
|
}
|
|
|
|
// IsZero returns true if all fields is equal to a zero value. For more info
|
|
// refer to Struct types IsZero() method. It panics if s's kind is not struct.
|
|
func IsZero(s interface{}) bool {
|
|
return New(s).IsZero()
|
|
}
|
|
|
|
// HasZero returns true if any field is equal to a zero value. For more info
|
|
// refer to Struct types HasZero() method. It panics if s's kind is not struct.
|
|
func HasZero(s interface{}) bool {
|
|
return New(s).HasZero()
|
|
}
|
|
|
|
// IsStruct returns true if the given variable is a struct or a pointer to
|
|
// struct.
|
|
func IsStruct(s interface{}) bool {
|
|
v := reflect.ValueOf(s)
|
|
if v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
// uninitialized zero value of a struct
|
|
if v.Kind() == reflect.Invalid {
|
|
return false
|
|
}
|
|
|
|
return v.Kind() == reflect.Struct
|
|
}
|
|
|
|
// Name returns the structs's type name within its package. It returns an
|
|
// empty string for unnamed types. It panics if s's kind is not struct.
|
|
func Name(s interface{}) string {
|
|
return New(s).Name()
|
|
}
|