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