mirror of https://github.com/matrix-org/go-neb.git
No known key found for this signature in database
GPG Key ID: E5B89311FAB91B9F
9 changed files with 3190 additions and 0 deletions
-
21vendor/src/github.com/fatih/structs/LICENSE
-
163vendor/src/github.com/fatih/structs/README.md
-
141vendor/src/github.com/fatih/structs/field.go
-
397vendor/src/github.com/fatih/structs/field_test.go
-
586vendor/src/github.com/fatih/structs/structs.go
-
351vendor/src/github.com/fatih/structs/structs_example_test.go
-
1453vendor/src/github.com/fatih/structs/structs_test.go
-
32vendor/src/github.com/fatih/structs/tags.go
-
46vendor/src/github.com/fatih/structs/tags_test.go
@ -0,0 +1,21 @@ |
|||||
|
The MIT License (MIT) |
||||
|
|
||||
|
Copyright (c) 2014 Fatih Arslan |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
||||
@ -0,0 +1,163 @@ |
|||||
|
# Structs [](http://godoc.org/github.com/fatih/structs) [](https://travis-ci.org/fatih/structs) [](https://coveralls.io/r/fatih/structs) |
||||
|
|
||||
|
Structs contains various utilities to work with Go (Golang) structs. It was |
||||
|
initially used by me to convert a struct into a `map[string]interface{}`. With |
||||
|
time I've added other utilities for structs. It's basically a high level |
||||
|
package based on primitives from the reflect package. Feel free to add new |
||||
|
functions or improve the existing code. |
||||
|
|
||||
|
## Install |
||||
|
|
||||
|
```bash |
||||
|
go get github.com/fatih/structs |
||||
|
``` |
||||
|
|
||||
|
## Usage and Examples |
||||
|
|
||||
|
Just like the standard lib `strings`, `bytes` and co packages, `structs` has |
||||
|
many global functions to manipulate or organize your struct data. Lets define |
||||
|
and declare a struct: |
||||
|
|
||||
|
```go |
||||
|
type Server struct { |
||||
|
Name string `json:"name,omitempty"` |
||||
|
ID int |
||||
|
Enabled bool |
||||
|
users []string // not exported |
||||
|
http.Server // embedded |
||||
|
} |
||||
|
|
||||
|
server := &Server{ |
||||
|
Name: "gopher", |
||||
|
ID: 123456, |
||||
|
Enabled: true, |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
```go |
||||
|
// Convert a struct to a map[string]interface{} |
||||
|
// => {"Name":"gopher", "ID":123456, "Enabled":true} |
||||
|
m := structs.Map(server) |
||||
|
|
||||
|
// Convert the values of a struct to a []interface{} |
||||
|
// => ["gopher", 123456, true] |
||||
|
v := structs.Values(server) |
||||
|
|
||||
|
// Convert the names of a struct to a []string |
||||
|
// (see "Names methods" for more info about fields) |
||||
|
n := structs.Names(server) |
||||
|
|
||||
|
// Convert the values of a struct to a []*Field |
||||
|
// (see "Field methods" for more info about fields) |
||||
|
f := structs.Fields(server) |
||||
|
|
||||
|
// Return the struct name => "Server" |
||||
|
n := structs.Name(server) |
||||
|
|
||||
|
// Check if any field of a struct is initialized or not. |
||||
|
h := structs.HasZero(server) |
||||
|
|
||||
|
// Check if all fields of a struct is initialized or not. |
||||
|
z := structs.IsZero(server) |
||||
|
|
||||
|
// Check if server is a struct or a pointer to struct |
||||
|
i := structs.IsStruct(server) |
||||
|
``` |
||||
|
|
||||
|
### Struct methods |
||||
|
|
||||
|
The structs functions can be also used as independent methods by creating a new |
||||
|
`*structs.Struct`. This is handy if you want to have more control over the |
||||
|
structs (such as retrieving a single Field). |
||||
|
|
||||
|
```go |
||||
|
// Create a new struct type: |
||||
|
s := structs.New(server) |
||||
|
|
||||
|
m := s.Map() // Get a map[string]interface{} |
||||
|
v := s.Values() // Get a []interface{} |
||||
|
f := s.Fields() // Get a []*Field |
||||
|
n := s.Names() // Get a []string |
||||
|
f := s.Field(name) // Get a *Field based on the given field name |
||||
|
f, ok := s.FieldOk(name) // Get a *Field based on the given field name |
||||
|
n := s.Name() // Get the struct name |
||||
|
h := s.HasZero() // Check if any field is initialized |
||||
|
z := s.IsZero() // Check if all fields are initialized |
||||
|
``` |
||||
|
|
||||
|
### Field methods |
||||
|
|
||||
|
We can easily examine a single Field for more detail. Below you can see how we |
||||
|
get and interact with various field methods: |
||||
|
|
||||
|
|
||||
|
```go |
||||
|
s := structs.New(server) |
||||
|
|
||||
|
// Get the Field struct for the "Name" field |
||||
|
name := s.Field("Name") |
||||
|
|
||||
|
// Get the underlying value, value => "gopher" |
||||
|
value := name.Value().(string) |
||||
|
|
||||
|
// Set the field's value |
||||
|
name.Set("another gopher") |
||||
|
|
||||
|
// Get the field's kind, kind => "string" |
||||
|
name.Kind() |
||||
|
|
||||
|
// Check if the field is exported or not |
||||
|
if name.IsExported() { |
||||
|
fmt.Println("Name field is exported") |
||||
|
} |
||||
|
|
||||
|
// Check if the value is a zero value, such as "" for string, 0 for int |
||||
|
if !name.IsZero() { |
||||
|
fmt.Println("Name is initialized") |
||||
|
} |
||||
|
|
||||
|
// Check if the field is an anonymous (embedded) field |
||||
|
if !name.IsEmbedded() { |
||||
|
fmt.Println("Name is not an embedded field") |
||||
|
} |
||||
|
|
||||
|
// Get the Field's tag value for tag name "json", tag value => "name,omitempty" |
||||
|
tagValue := name.Tag("json") |
||||
|
``` |
||||
|
|
||||
|
Nested structs are supported too: |
||||
|
|
||||
|
```go |
||||
|
addrField := s.Field("Server").Field("Addr") |
||||
|
|
||||
|
// Get the value for addr |
||||
|
a := addrField.Value().(string) |
||||
|
|
||||
|
// Or get all fields |
||||
|
httpServer := s.Field("Server").Fields() |
||||
|
``` |
||||
|
|
||||
|
We can also get a slice of Fields from the Struct type to iterate over all |
||||
|
fields. This is handy if you wish to examine all fields: |
||||
|
|
||||
|
```go |
||||
|
s := structs.New(server) |
||||
|
|
||||
|
for _, f := range s.Fields() { |
||||
|
fmt.Printf("field name: %+v\n", f.Name()) |
||||
|
|
||||
|
if f.IsExported() { |
||||
|
fmt.Printf("value : %+v\n", f.Value()) |
||||
|
fmt.Printf("is zero : %+v\n", f.IsZero()) |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Credits |
||||
|
|
||||
|
* [Fatih Arslan](https://github.com/fatih) |
||||
|
* [Cihangir Savas](https://github.com/cihangir) |
||||
|
|
||||
|
## License |
||||
|
|
||||
|
The MIT License (MIT) - see LICENSE.md for more details |
||||
@ -0,0 +1,141 @@ |
|||||
|
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 |
||||
|
} |
||||
@ -0,0 +1,397 @@ |
|||||
|
package structs |
||||
|
|
||||
|
import ( |
||||
|
"reflect" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
// A test struct that defines all cases
|
||||
|
type Foo struct { |
||||
|
A string |
||||
|
B int `structs:"y"` |
||||
|
C bool `json:"c"` |
||||
|
d string // not exported
|
||||
|
E *Baz |
||||
|
x string `xml:"x"` // not exported, with tag
|
||||
|
Y []string |
||||
|
Z map[string]interface{} |
||||
|
*Bar // embedded
|
||||
|
} |
||||
|
|
||||
|
type Baz struct { |
||||
|
A string |
||||
|
B int |
||||
|
} |
||||
|
|
||||
|
type Bar struct { |
||||
|
E string |
||||
|
F int |
||||
|
g []string |
||||
|
} |
||||
|
|
||||
|
func newStruct() *Struct { |
||||
|
b := &Bar{ |
||||
|
E: "example", |
||||
|
F: 2, |
||||
|
g: []string{"zeynep", "fatih"}, |
||||
|
} |
||||
|
|
||||
|
// B and x is not initialized for testing
|
||||
|
f := &Foo{ |
||||
|
A: "gopher", |
||||
|
C: true, |
||||
|
d: "small", |
||||
|
E: nil, |
||||
|
Y: []string{"example"}, |
||||
|
Z: nil, |
||||
|
} |
||||
|
f.Bar = b |
||||
|
|
||||
|
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: %t want: %t", 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) |
||||
|
} |
||||
|
|
||||
|
// let's set a pointer to struct
|
||||
|
b := &Bar{ |
||||
|
E: "gopher", |
||||
|
F: 2, |
||||
|
} |
||||
|
|
||||
|
f = s.Field("Bar") |
||||
|
err = f.Set(b) |
||||
|
if err != nil { |
||||
|
t.Error(err) |
||||
|
} |
||||
|
|
||||
|
baz := &Baz{ |
||||
|
A: "helloWorld", |
||||
|
B: 42, |
||||
|
} |
||||
|
|
||||
|
f = s.Field("E") |
||||
|
err = f.Set(baz) |
||||
|
if err != nil { |
||||
|
t.Error(err) |
||||
|
} |
||||
|
|
||||
|
ba := s.Field("E").Value().(*Baz) |
||||
|
|
||||
|
if ba.A != "helloWorld" { |
||||
|
t.Errorf("could not set baz. Got: %s Want: helloWorld", ba.A) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField_NotSettable(t *testing.T) { |
||||
|
a := map[int]Baz{ |
||||
|
4: Baz{ |
||||
|
A: "value", |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
s := New(a[4]) |
||||
|
|
||||
|
if err := s.Field("A").Set("newValue"); err != errNotSettable { |
||||
|
t.Errorf("Trying to set non-settable field should error with %q. Got %q instead.", errNotSettable, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField_Zero(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
f := s.Field("A") |
||||
|
err := f.Zero() |
||||
|
if err != nil { |
||||
|
t.Error(err) |
||||
|
} |
||||
|
|
||||
|
if f.Value().(string) != "" { |
||||
|
t.Errorf("Zeroed value is wrong: %s want: %s", f.Value().(string), "") |
||||
|
} |
||||
|
|
||||
|
f = s.Field("Y") |
||||
|
err = f.Zero() |
||||
|
if err != nil { |
||||
|
t.Error(err) |
||||
|
} |
||||
|
|
||||
|
sliceLen := len(f.Value().([]string)) |
||||
|
if sliceLen != 0 { |
||||
|
t.Errorf("Zeroed values slice length is wrong: %d, want: %d", sliceLen, 0) |
||||
|
} |
||||
|
|
||||
|
f = s.Field("C") |
||||
|
err = f.Zero() |
||||
|
if err != nil { |
||||
|
t.Error(err) |
||||
|
} |
||||
|
|
||||
|
if f.Value().(bool) { |
||||
|
t.Errorf("Zeroed value is wrong: %t want: %t", f.Value().(bool), false) |
||||
|
} |
||||
|
|
||||
|
// let's access an unexported field, which should give an error
|
||||
|
f = s.Field("d") |
||||
|
err = f.Zero() |
||||
|
if err != errNotExported { |
||||
|
t.Error(err) |
||||
|
} |
||||
|
|
||||
|
f = s.Field("Bar") |
||||
|
err = f.Zero() |
||||
|
if err != nil { |
||||
|
t.Error(err) |
||||
|
} |
||||
|
|
||||
|
f = s.Field("E") |
||||
|
err = f.Zero() |
||||
|
if err != nil { |
||||
|
t.Error(err) |
||||
|
} |
||||
|
|
||||
|
v := s.Field("E").value |
||||
|
if !v.IsNil() { |
||||
|
t.Errorf("could not set baz. Got: %s Want: <nil>", v.Interface()) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
defer func() { |
||||
|
err := recover() |
||||
|
if err == nil { |
||||
|
t.Error("Retrieveing a non existing field from the struct should panic") |
||||
|
} |
||||
|
}() |
||||
|
|
||||
|
_ = s.Field("no-field") |
||||
|
} |
||||
|
|
||||
|
func TestField_Kind(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
f := s.Field("A") |
||||
|
if f.Kind() != reflect.String { |
||||
|
t.Errorf("Field A has wrong kind: %s want: %s", f.Kind(), reflect.String) |
||||
|
} |
||||
|
|
||||
|
f = s.Field("B") |
||||
|
if f.Kind() != reflect.Int { |
||||
|
t.Errorf("Field B has wrong kind: %s want: %s", f.Kind(), reflect.Int) |
||||
|
} |
||||
|
|
||||
|
// unexported
|
||||
|
f = s.Field("d") |
||||
|
if f.Kind() != reflect.String { |
||||
|
t.Errorf("Field d has wrong kind: %s want: %s", f.Kind(), reflect.String) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField_Tag(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
v := s.Field("B").Tag("json") |
||||
|
if v != "" { |
||||
|
t.Errorf("Field's tag value of a non existing tag should return empty, got: %s", v) |
||||
|
} |
||||
|
|
||||
|
v = s.Field("C").Tag("json") |
||||
|
if v != "c" { |
||||
|
t.Errorf("Field's tag value of the existing field C should return 'c', got: %s", v) |
||||
|
} |
||||
|
|
||||
|
v = s.Field("d").Tag("json") |
||||
|
if v != "" { |
||||
|
t.Errorf("Field's tag value of a non exported field should return empty, got: %s", v) |
||||
|
} |
||||
|
|
||||
|
v = s.Field("x").Tag("xml") |
||||
|
if v != "x" { |
||||
|
t.Errorf("Field's tag value of a non exported field with a tag should return 'x', got: %s", v) |
||||
|
} |
||||
|
|
||||
|
v = s.Field("A").Tag("json") |
||||
|
if v != "" { |
||||
|
t.Errorf("Field's tag value of a existing field without a tag should return empty, got: %s", v) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField_Value(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
v := s.Field("A").Value() |
||||
|
val, ok := v.(string) |
||||
|
if !ok { |
||||
|
t.Errorf("Field's value of a A should be string") |
||||
|
} |
||||
|
|
||||
|
if val != "gopher" { |
||||
|
t.Errorf("Field's value of a existing tag should return 'gopher', got: %s", val) |
||||
|
} |
||||
|
|
||||
|
defer func() { |
||||
|
err := recover() |
||||
|
if err == nil { |
||||
|
t.Error("Value of a non exported field from the field should panic") |
||||
|
} |
||||
|
}() |
||||
|
|
||||
|
// should panic
|
||||
|
_ = s.Field("d").Value() |
||||
|
} |
||||
|
|
||||
|
func TestField_IsEmbedded(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
if !s.Field("Bar").IsEmbedded() { |
||||
|
t.Errorf("Fields 'Bar' field is an embedded field") |
||||
|
} |
||||
|
|
||||
|
if s.Field("d").IsEmbedded() { |
||||
|
t.Errorf("Fields 'd' field is not an embedded field") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField_IsExported(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
if !s.Field("Bar").IsExported() { |
||||
|
t.Errorf("Fields 'Bar' field is an exported field") |
||||
|
} |
||||
|
|
||||
|
if !s.Field("A").IsExported() { |
||||
|
t.Errorf("Fields 'A' field is an exported field") |
||||
|
} |
||||
|
|
||||
|
if s.Field("d").IsExported() { |
||||
|
t.Errorf("Fields 'd' field is not an exported field") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField_IsZero(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
if s.Field("A").IsZero() { |
||||
|
t.Errorf("Fields 'A' field is an initialized field") |
||||
|
} |
||||
|
|
||||
|
if !s.Field("B").IsZero() { |
||||
|
t.Errorf("Fields 'B' field is not an initialized field") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField_Name(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
if s.Field("A").Name() != "A" { |
||||
|
t.Errorf("Fields 'A' field should have the name 'A'") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField_Field(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
e := s.Field("Bar").Field("E") |
||||
|
|
||||
|
val, ok := e.Value().(string) |
||||
|
if !ok { |
||||
|
t.Error("The value of the field 'e' inside 'Bar' struct should be string") |
||||
|
} |
||||
|
|
||||
|
if val != "example" { |
||||
|
t.Errorf("The value of 'e' should be 'example, got: %s", val) |
||||
|
} |
||||
|
|
||||
|
defer func() { |
||||
|
err := recover() |
||||
|
if err == nil { |
||||
|
t.Error("Field of a non existing nested struct should panic") |
||||
|
} |
||||
|
}() |
||||
|
|
||||
|
_ = s.Field("Bar").Field("e") |
||||
|
} |
||||
|
|
||||
|
func TestField_Fields(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
fields := s.Field("Bar").Fields() |
||||
|
|
||||
|
if len(fields) != 3 { |
||||
|
t.Errorf("We expect 3 fields in embedded struct, was: %d", len(fields)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestField_FieldOk(t *testing.T) { |
||||
|
s := newStruct() |
||||
|
|
||||
|
b, ok := s.FieldOk("Bar") |
||||
|
if !ok { |
||||
|
t.Error("The field 'Bar' should exists.") |
||||
|
} |
||||
|
|
||||
|
e, ok := b.FieldOk("E") |
||||
|
if !ok { |
||||
|
t.Error("The field 'E' should exists.") |
||||
|
} |
||||
|
|
||||
|
val, ok := e.Value().(string) |
||||
|
if !ok { |
||||
|
t.Error("The value of the field 'e' inside 'Bar' struct should be string") |
||||
|
} |
||||
|
|
||||
|
if val != "example" { |
||||
|
t.Errorf("The value of 'e' should be 'example, got: %s", val) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,586 @@ |
|||||
|
// Package structs contains various utilities functions to work with structs.
|
||||
|
package structs |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
|
||||
|
"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 content of "string" uses the stringer to get the value. Example:
|
||||
|
//
|
||||
|
// // The value will be output of Animal's String() func.
|
||||
|
// // Map will panic if Animal does not implement String().
|
||||
|
// Field *Animal `structs:"field,string"`
|
||||
|
//
|
||||
|
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
|
||||
|
// in the output map. Example:
|
||||
|
//
|
||||
|
// // The FieldStruct's fields will be flattened into the output map.
|
||||
|
// FieldStruct time.Time `structs:",flatten"`
|
||||
|
//
|
||||
|
// 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{}) |
||||
|
s.FillMap(out) |
||||
|
return out |
||||
|
} |
||||
|
|
||||
|
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||
|
// given map.
|
||||
|
func (s *Struct) FillMap(out map[string]interface{}) { |
||||
|
if out == nil { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
fields := s.structFields() |
||||
|
|
||||
|
for _, field := range fields { |
||||
|
name := field.Name |
||||
|
val := s.value.FieldByName(name) |
||||
|
isSubStruct := false |
||||
|
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 !tagOpts.Has("omitnested") { |
||||
|
finalVal = s.nested(val) |
||||
|
|
||||
|
v := reflect.ValueOf(val.Interface()) |
||||
|
if v.Kind() == reflect.Ptr { |
||||
|
v = v.Elem() |
||||
|
} |
||||
|
|
||||
|
switch v.Kind() { |
||||
|
case reflect.Map, reflect.Struct: |
||||
|
isSubStruct = true |
||||
|
} |
||||
|
} else { |
||||
|
finalVal = val.Interface() |
||||
|
} |
||||
|
|
||||
|
if tagOpts.Has("string") { |
||||
|
s, ok := val.Interface().(fmt.Stringer) |
||||
|
if ok { |
||||
|
out[name] = s.String() |
||||
|
} |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
if isSubStruct && (tagOpts.Has("flatten")) { |
||||
|
for k := range finalVal.(map[string]interface{}) { |
||||
|
out[k] = finalVal.(map[string]interface{})[k] |
||||
|
} |
||||
|
} else { |
||||
|
out[name] = finalVal |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 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 tagOpts.Has("string") { |
||||
|
s, ok := val.Interface().(fmt.Stringer) |
||||
|
if ok { |
||||
|
t = append(t, s.String()) |
||||
|
} |
||||
|
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 |
||||
|
} |
||||
|
|
||||
|
// FieldOk 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≤
|
||||
|
for 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() |
||||
|
} |
||||
|
|
||||
|
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||
|
// given map.
|
||||
|
func FillMap(s interface{}, out map[string]interface{}) { |
||||
|
New(s).FillMap(out) |
||||
|
} |
||||
|
|
||||
|
// 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() |
||||
|
} |
||||
|
|
||||
|
// nested retrieves recursively all types for the given value and returns the
|
||||
|
// nested value.
|
||||
|
func (s *Struct) nested(val reflect.Value) interface{} { |
||||
|
var finalVal interface{} |
||||
|
|
||||
|
v := reflect.ValueOf(val.Interface()) |
||||
|
if v.Kind() == reflect.Ptr { |
||||
|
v = v.Elem() |
||||
|
} |
||||
|
|
||||
|
switch v.Kind() { |
||||
|
case reflect.Struct: |
||||
|
n := New(val.Interface()) |
||||
|
n.TagName = s.TagName |
||||
|
m := n.Map() |
||||
|
|
||||
|
// do not add the converted value if there are no exported fields, ie:
|
||||
|
// time.Time
|
||||
|
if len(m) == 0 { |
||||
|
finalVal = val.Interface() |
||||
|
} else { |
||||
|
finalVal = m |
||||
|
} |
||||
|
case reflect.Map: |
||||
|
// get the element type of the map
|
||||
|
mapElem := val.Type() |
||||
|
switch val.Type().Kind() { |
||||
|
case reflect.Ptr, reflect.Array, reflect.Map, |
||||
|
reflect.Slice, reflect.Chan: |
||||
|
mapElem = val.Type().Elem() |
||||
|
if mapElem.Kind() == reflect.Ptr { |
||||
|
mapElem = mapElem.Elem() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// only iterate over struct types, ie: map[string]StructType,
|
||||
|
// map[string][]StructType,
|
||||
|
if mapElem.Kind() == reflect.Struct || |
||||
|
(mapElem.Kind() == reflect.Slice && |
||||
|
mapElem.Elem().Kind() == reflect.Struct) { |
||||
|
m := make(map[string]interface{}, val.Len()) |
||||
|
for _, k := range val.MapKeys() { |
||||
|
m[k.String()] = s.nested(val.MapIndex(k)) |
||||
|
} |
||||
|
finalVal = m |
||||
|
break |
||||
|
} |
||||
|
|
||||
|
// TODO(arslan): should this be optional?
|
||||
|
finalVal = val.Interface() |
||||
|
case reflect.Slice, reflect.Array: |
||||
|
if val.Type().Kind() == reflect.Interface { |
||||
|
finalVal = val.Interface() |
||||
|
break |
||||
|
} |
||||
|
|
||||
|
// TODO(arslan): should this be optional?
|
||||
|
// do not iterate of non struct types, just pass the value. Ie: []int,
|
||||
|
// []string, co... We only iterate further if it's a struct.
|
||||
|
// i.e []foo or []*foo
|
||||
|
if val.Type().Elem().Kind() != reflect.Struct && |
||||
|
!(val.Type().Elem().Kind() == reflect.Ptr && |
||||
|
val.Type().Elem().Elem().Kind() == reflect.Struct) { |
||||
|
finalVal = val.Interface() |
||||
|
break |
||||
|
} |
||||
|
|
||||
|
slices := make([]interface{}, val.Len(), val.Len()) |
||||
|
for x := 0; x < val.Len(); x++ { |
||||
|
slices[x] = s.nested(val.Index(x)) |
||||
|
} |
||||
|
finalVal = slices |
||||
|
default: |
||||
|
finalVal = val.Interface() |
||||
|
} |
||||
|
|
||||
|
return finalVal |
||||
|
} |
||||
@ -0,0 +1,351 @@ |
|||||
|
package structs |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
func ExampleNew() { |
||||
|
type Server struct { |
||||
|
Name string |
||||
|
ID int32 |
||||
|
Enabled bool |
||||
|
} |
||||
|
|
||||
|
server := &Server{ |
||||
|
Name: "Arslan", |
||||
|
ID: 123456, |
||||
|
Enabled: true, |
||||
|
} |
||||
|
|
||||
|
s := New(server) |
||||
|
|
||||
|
fmt.Printf("Name : %v\n", s.Name()) |
||||
|
fmt.Printf("Values : %v\n", s.Values()) |
||||
|
fmt.Printf("Value of ID : %v\n", s.Field("ID").Value()) |
||||
|
// Output:
|
||||
|
// Name : Server
|
||||
|
// Values : [Arslan 123456 true]
|
||||
|
// Value of ID : 123456
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
func ExampleMap() { |
||||
|
type Server struct { |
||||
|
Name string |
||||
|
ID int32 |
||||
|
Enabled bool |
||||
|
} |
||||
|
|
||||
|
s := &Server{ |
||||
|
Name: "Arslan", |
||||
|
ID: 123456, |
||||
|
Enabled: true, |
||||
|
} |
||||
|
|
||||
|
m := Map(s) |
||||
|
|
||||
|
fmt.Printf("%#v\n", m["Name"]) |
||||
|
fmt.Printf("%#v\n", m["ID"]) |
||||
|
fmt.Printf("%#v\n", m["Enabled"]) |
||||
|
// Output:
|
||||
|
// "Arslan"
|
||||
|
// 123456
|
||||
|
// true
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
func ExampleMap_tags() { |
||||
|
// Custom tags can change the map keys instead of using the fields name
|
||||
|
type Server struct { |
||||
|
Name string `structs:"server_name"` |
||||
|
ID int32 `structs:"server_id"` |
||||
|
Enabled bool `structs:"enabled"` |
||||
|
} |
||||
|
|
||||
|
s := &Server{ |
||||
|
Name: "Zeynep", |
||||
|
ID: 789012, |
||||
|
} |
||||
|
|
||||
|
m := Map(s) |
||||
|
|
||||
|
// access them by the custom tags defined above
|
||||
|
fmt.Printf("%#v\n", m["server_name"]) |
||||
|
fmt.Printf("%#v\n", m["server_id"]) |
||||
|
fmt.Printf("%#v\n", m["enabled"]) |
||||
|
// Output:
|
||||
|
// "Zeynep"
|
||||
|
// 789012
|
||||
|
// false
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
func ExampleMap_omitNested() { |
||||
|
// By default field with struct types are processed too. We can stop
|
||||
|
// processing them via "omitnested" tag option.
|
||||
|
type Server struct { |
||||
|
Name string `structs:"server_name"` |
||||
|
ID int32 `structs:"server_id"` |
||||
|
Time time.Time `structs:"time,omitnested"` // do not convert to map[string]interface{}
|
||||
|
} |
||||
|
|
||||
|
const shortForm = "2006-Jan-02" |
||||
|
t, _ := time.Parse("2006-Jan-02", "2013-Feb-03") |
||||
|
|
||||
|
s := &Server{ |
||||
|
Name: "Zeynep", |
||||
|
ID: 789012, |
||||
|
Time: t, |
||||
|
} |
||||
|
|
||||
|
m := Map(s) |
||||
|
|
||||
|
// access them by the custom tags defined above
|
||||
|
fmt.Printf("%v\n", m["server_name"]) |
||||
|
fmt.Printf("%v\n", m["server_id"]) |
||||
|
fmt.Printf("%v\n", m["time"].(time.Time)) |
||||
|
// Output:
|
||||
|
// Zeynep
|
||||
|
// 789012
|
||||
|
// 2013-02-03 00:00:00 +0000 UTC
|
||||
|
} |
||||
|
|
||||
|
func ExampleMap_omitEmpty() { |
||||
|
// By default field with struct types of zero values are processed too. We
|
||||
|
// can stop processing them via "omitempty" tag option.
|
||||
|
type Server struct { |
||||
|
Name string `structs:",omitempty"` |
||||
|
ID int32 `structs:"server_id,omitempty"` |
||||
|
Location string |
||||
|
} |
||||
|
|
||||
|
// Only add location
|
||||
|
s := &Server{ |
||||
|
Location: "Tokyo", |
||||
|
} |
||||
|
|
||||
|
m := Map(s) |
||||
|
|
||||
|
// map contains only the Location field
|
||||
|
fmt.Printf("%v\n", m) |
||||
|
// Output:
|
||||
|
// map[Location:Tokyo]
|
||||
|
} |
||||
|
|
||||
|
func ExampleValues() { |
||||
|
type Server struct { |
||||
|
Name string |
||||
|
ID int32 |
||||
|
Enabled bool |
||||
|
} |
||||
|
|
||||
|
s := &Server{ |
||||
|
Name: "Fatih", |
||||
|
ID: 135790, |
||||
|
Enabled: false, |
||||
|
} |
||||
|
|
||||
|
m := Values(s) |
||||
|
|
||||
|
fmt.Printf("Values: %+v\n", m) |
||||
|
// Output:
|
||||
|
// Values: [Fatih 135790 false]
|
||||
|
} |
||||
|
|
||||
|
func ExampleValues_omitEmpty() { |
||||
|
// By default field with struct types of zero values are processed too. We
|
||||
|
// can stop processing them via "omitempty" tag option.
|
||||
|
type Server struct { |
||||
|
Name string `structs:",omitempty"` |
||||
|
ID int32 `structs:"server_id,omitempty"` |
||||
|
Location string |
||||
|
} |
||||
|
|
||||
|
// Only add location
|
||||
|
s := &Server{ |
||||
|
Location: "Ankara", |
||||
|
} |
||||
|
|
||||
|
m := Values(s) |
||||
|
|
||||
|
// values contains only the Location field
|
||||
|
fmt.Printf("Values: %+v\n", m) |
||||
|
// Output:
|
||||
|
// Values: [Ankara]
|
||||
|
} |
||||
|
|
||||
|
func ExampleValues_tags() { |
||||
|
type Location struct { |
||||
|
City string |
||||
|
Country string |
||||
|
} |
||||
|
|
||||
|
type Server struct { |
||||
|
Name string |
||||
|
ID int32 |
||||
|
Enabled bool |
||||
|
Location Location `structs:"-"` // values from location are not included anymore
|
||||
|
} |
||||
|
|
||||
|
s := &Server{ |
||||
|
Name: "Fatih", |
||||
|
ID: 135790, |
||||
|
Enabled: false, |
||||
|
Location: Location{City: "Ankara", Country: "Turkey"}, |
||||
|
} |
||||
|
|
||||
|
// Let get all values from the struct s. Note that we don't include values
|
||||
|
// from the Location field
|
||||
|
m := Values(s) |
||||
|
|
||||
|
fmt.Printf("Values: %+v\n", m) |
||||
|
// Output:
|
||||
|
// Values: [Fatih 135790 false]
|
||||
|
} |
||||
|
|
||||
|
func ExampleFields() { |
||||
|
type Access struct { |
||||
|
Name string |
||||
|
LastAccessed time.Time |
||||
|
Number int |
||||
|
} |
||||
|
|
||||
|
s := &Access{ |
||||
|
Name: "Fatih", |
||||
|
LastAccessed: time.Now(), |
||||
|
Number: 1234567, |
||||
|
} |
||||
|
|
||||
|
fields := Fields(s) |
||||
|
|
||||
|
for i, field := range fields { |
||||
|
fmt.Printf("[%d] %+v\n", i, field.Name()) |
||||
|
} |
||||
|
|
||||
|
// Output:
|
||||
|
// [0] Name
|
||||
|
// [1] LastAccessed
|
||||
|
// [2] Number
|
||||
|
} |
||||
|
|
||||
|
func ExampleFields_nested() { |
||||
|
type Person struct { |
||||
|
Name string |
||||
|
Number int |
||||
|
} |
||||
|
|
||||
|
type Access struct { |
||||
|
Person Person |
||||
|
HasPermission bool |
||||
|
LastAccessed time.Time |
||||
|
} |
||||
|
|
||||
|
s := &Access{ |
||||
|
Person: Person{Name: "fatih", Number: 1234567}, |
||||
|
LastAccessed: time.Now(), |
||||
|
HasPermission: true, |
||||
|
} |
||||
|
|
||||
|
// Let's get all fields from the struct s.
|
||||
|
fields := Fields(s) |
||||
|
|
||||
|
for _, field := range fields { |
||||
|
if field.Name() == "Person" { |
||||
|
fmt.Printf("Access.Person.Name: %+v\n", field.Field("Name").Value()) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Output:
|
||||
|
// Access.Person.Name: fatih
|
||||
|
} |
||||
|
|
||||
|
func ExampleField() { |
||||
|
type Person struct { |
||||
|
Name string |
||||
|
Number int |
||||
|
} |
||||
|
|
||||
|
type Access struct { |
||||
|
Person Person |
||||
|
HasPermission bool |
||||
|
LastAccessed time.Time |
||||
|
} |
||||
|
|
||||
|
access := &Access{ |
||||
|
Person: Person{Name: "fatih", Number: 1234567}, |
||||
|
LastAccessed: time.Now(), |
||||
|
HasPermission: true, |
||||
|
} |
||||
|
|
||||
|
// Create a new Struct type
|
||||
|
s := New(access) |
||||
|
|
||||
|
// Get the Field type for "Person" field
|
||||
|
p := s.Field("Person") |
||||
|
|
||||
|
// Get the underlying "Name field" and print the value of it
|
||||
|
name := p.Field("Name") |
||||
|
|
||||
|
fmt.Printf("Value of Person.Access.Name: %+v\n", name.Value()) |
||||
|
|
||||
|
// Output:
|
||||
|
// Value of Person.Access.Name: fatih
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
func ExampleIsZero() { |
||||
|
type Server struct { |
||||
|
Name string |
||||
|
ID int32 |
||||
|
Enabled bool |
||||
|
} |
||||
|
|
||||
|
// Nothing is initalized
|
||||
|
a := &Server{} |
||||
|
isZeroA := IsZero(a) |
||||
|
|
||||
|
// Name and Enabled is initialized, but not ID
|
||||
|
b := &Server{ |
||||
|
Name: "Golang", |
||||
|
Enabled: true, |
||||
|
} |
||||
|
isZeroB := IsZero(b) |
||||
|
|
||||
|
fmt.Printf("%#v\n", isZeroA) |
||||
|
fmt.Printf("%#v\n", isZeroB) |
||||
|
// Output:
|
||||
|
// true
|
||||
|
// false
|
||||
|
} |
||||
|
|
||||
|
func ExampleHasZero() { |
||||
|
// Let's define an Access struct. Note that the "Enabled" field is not
|
||||
|
// going to be checked because we added the "structs" tag to the field.
|
||||
|
type Access struct { |
||||
|
Name string |
||||
|
LastAccessed time.Time |
||||
|
Number int |
||||
|
Enabled bool `structs:"-"` |
||||
|
} |
||||
|
|
||||
|
// Name and Number is not initialized.
|
||||
|
a := &Access{ |
||||
|
LastAccessed: time.Now(), |
||||
|
} |
||||
|
hasZeroA := HasZero(a) |
||||
|
|
||||
|
// Name and Number is initialized.
|
||||
|
b := &Access{ |
||||
|
Name: "Fatih", |
||||
|
LastAccessed: time.Now(), |
||||
|
Number: 12345, |
||||
|
} |
||||
|
hasZeroB := HasZero(b) |
||||
|
|
||||
|
fmt.Printf("%#v\n", hasZeroA) |
||||
|
fmt.Printf("%#v\n", hasZeroB) |
||||
|
// Output:
|
||||
|
// true
|
||||
|
// false
|
||||
|
} |
||||
1453
vendor/src/github.com/fatih/structs/structs_test.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,32 @@ |
|||||
|
package structs |
||||
|
|
||||
|
import "strings" |
||||
|
|
||||
|
// tagOptions contains a slice of tag options
|
||||
|
type tagOptions []string |
||||
|
|
||||
|
// Has returns true if the given option is available in tagOptions
|
||||
|
func (t tagOptions) Has(opt string) bool { |
||||
|
for _, tagOpt := range t { |
||||
|
if tagOpt == opt { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
// parseTag splits a struct field's tag into its name and a list of options
|
||||
|
// which comes after a name. A tag is in the form of: "name,option1,option2".
|
||||
|
// The name can be neglectected.
|
||||
|
func parseTag(tag string) (string, tagOptions) { |
||||
|
// tag is one of followings:
|
||||
|
// ""
|
||||
|
// "name"
|
||||
|
// "name,opt"
|
||||
|
// "name,opt,opt2"
|
||||
|
// ",opt"
|
||||
|
|
||||
|
res := strings.Split(tag, ",") |
||||
|
return res[0], res[1:] |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
package structs |
||||
|
|
||||
|
import "testing" |
||||
|
|
||||
|
func TestParseTag_Name(t *testing.T) { |
||||
|
tags := []struct { |
||||
|
tag string |
||||
|
has bool |
||||
|
}{ |
||||
|
{"", false}, |
||||
|
{"name", true}, |
||||
|
{"name,opt", true}, |
||||
|
{"name , opt, opt2", false}, // has a single whitespace
|
||||
|
{", opt, opt2", false}, |
||||
|
} |
||||
|
|
||||
|
for _, tag := range tags { |
||||
|
name, _ := parseTag(tag.tag) |
||||
|
|
||||
|
if (name != "name") && tag.has { |
||||
|
t.Errorf("Parse tag should return name: %#v", tag) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestParseTag_Opts(t *testing.T) { |
||||
|
tags := []struct { |
||||
|
opts string |
||||
|
has bool |
||||
|
}{ |
||||
|
{"name", false}, |
||||
|
{"name,opt", true}, |
||||
|
{"name , opt, opt2", false}, // has a single whitespace
|
||||
|
{",opt, opt2", true}, |
||||
|
{", opt3, opt4", false}, |
||||
|
} |
||||
|
|
||||
|
// search for "opt"
|
||||
|
for _, tag := range tags { |
||||
|
_, opts := parseTag(tag.opts) |
||||
|
|
||||
|
if opts.Has("opt") != tag.has { |
||||
|
t.Errorf("Tag opts should have opt: %#v", tag) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue