mirror of https://github.com/matrix-org/go-neb.git
Browse Source
[CircleCI-Service] Apply changes that got requested by t3chguy
[CircleCI-Service] Apply changes that got requested by t3chguy
Signed-off-by: MTRNord <mtrnord1@gmail.com>pull/213/head
No known key found for this signature in database
GPG Key ID: E5B89311FAB91B9F
12 changed files with 42 additions and 3287 deletions
-
60src/github.com/matrix-org/go-neb/services/circleci/circleci.go
-
2src/github.com/matrix-org/go-neb/services/circleci/types.go
-
61src/github.com/matrix-org/go-neb/services/circleci/util.go
-
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
@ -1,61 +0,0 @@ |
|||||
package circleci |
|
||||
|
|
||||
import ( |
|
||||
"unicode" |
|
||||
"unicode/utf8" |
|
||||
) |
|
||||
|
|
||||
type buffer struct { |
|
||||
r []byte |
|
||||
runeBytes [utf8.UTFMax]byte |
|
||||
} |
|
||||
|
|
||||
func (b *buffer) write(r rune) { |
|
||||
if r < utf8.RuneSelf { |
|
||||
b.r = append(b.r, byte(r)) |
|
||||
return |
|
||||
} |
|
||||
n := utf8.EncodeRune(b.runeBytes[0:], r) |
|
||||
b.r = append(b.r, b.runeBytes[0:n]...) |
|
||||
} |
|
||||
|
|
||||
func (b *buffer) indent() { |
|
||||
if len(b.r) > 0 { |
|
||||
b.r = append(b.r, '_') |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func CamelCaseToUnderscore(s string) string { |
|
||||
b := buffer{ |
|
||||
r: make([]byte, 0, len(s)), |
|
||||
} |
|
||||
var m rune |
|
||||
var w bool |
|
||||
for _, ch := range s { |
|
||||
if unicode.IsUpper(ch) { |
|
||||
if m != 0 { |
|
||||
if !w { |
|
||||
b.indent() |
|
||||
w = true |
|
||||
} |
|
||||
b.write(m) |
|
||||
} |
|
||||
m = unicode.ToLower(ch) |
|
||||
} else { |
|
||||
if m != 0 { |
|
||||
b.indent() |
|
||||
b.write(m) |
|
||||
m = 0 |
|
||||
w = false |
|
||||
} |
|
||||
b.write(ch) |
|
||||
} |
|
||||
} |
|
||||
if m != 0 { |
|
||||
if !w { |
|
||||
b.indent() |
|
||||
} |
|
||||
b.write(m) |
|
||||
} |
|
||||
return string(b.r) |
|
||||
} |
|
||||
@ -1,21 +0,0 @@ |
|||||
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. |
|
||||
@ -1,163 +0,0 @@ |
|||||
# 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 |
|
||||
@ -1,141 +0,0 @@ |
|||||
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 |
|
||||
} |
|
||||
@ -1,397 +0,0 @@ |
|||||
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) |
|
||||
} |
|
||||
} |
|
||||
@ -1,586 +0,0 @@ |
|||||
// 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 |
|
||||
} |
|
||||
@ -1,351 +0,0 @@ |
|||||
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
@ -1,32 +0,0 @@ |
|||||
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:] |
|
||||
} |
|
||||
@ -1,46 +0,0 @@ |
|||||
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