structure: more improvements
This commit is contained in:
parent
f614213600
commit
1c91005c39
114
README.md
114
README.md
@ -18,49 +18,63 @@ Lets define and declare a struct
|
||||
|
||||
```go
|
||||
type Server struct {
|
||||
Name string
|
||||
ID int32
|
||||
Name string `json:"name,omitempty"`
|
||||
ID int
|
||||
Enabled bool
|
||||
users []string // not exported
|
||||
http.Server // embedded
|
||||
}
|
||||
|
||||
s := &Server{
|
||||
server := &Server{
|
||||
Name: "gopher",
|
||||
ID: 123456,
|
||||
Enabled: true,
|
||||
}
|
||||
```
|
||||
|
||||
### Struct methods
|
||||
|
||||
Let's create a new `Struct` type.
|
||||
|
||||
```go
|
||||
// Create a new struct type:
|
||||
s := structure.New(server)
|
||||
|
||||
// Convert a struct to a map[string]interface{}
|
||||
// => {"Name":"gopher", "ID":123456, "Enabled":true}
|
||||
m := structure.Map(s)
|
||||
m := s.Map()
|
||||
|
||||
// Convert the values of a struct to a []interface{}
|
||||
// => [123456, "gopher", true]
|
||||
v := structure.Values(s)
|
||||
|
||||
// Convert the fields of a struct to a []string.
|
||||
// => ["Name", "ID", "Enabled"]
|
||||
f := structure.Fields(s)
|
||||
|
||||
// Return the struct name
|
||||
// => "Server"
|
||||
n := structure.Name(s)
|
||||
|
||||
// Check if field name exists
|
||||
// => true
|
||||
h := structure.Has(s, "Enabled")
|
||||
// => ["gopher", 123456, true]
|
||||
v := s.Values()
|
||||
|
||||
// Check if any field of a struct is initialized or not.
|
||||
if structure.HasZero(s) {
|
||||
if s.HasZero() {
|
||||
fmt.Println("s has a zero value field")
|
||||
}
|
||||
|
||||
// Check if all fields of a struct is initialized or not.
|
||||
if structure.IsZero(s) {
|
||||
if s.IsZero() {
|
||||
fmt.Println("all fields of s is zero value")
|
||||
}
|
||||
|
||||
// Return the struct name
|
||||
// => "Server"
|
||||
n := s.Name()
|
||||
```
|
||||
|
||||
Most of the struct methods are available as global functions without the need
|
||||
for a `New()` constructor:
|
||||
|
||||
```go
|
||||
m := structure.Map(s)
|
||||
v := structure.Values(s)
|
||||
f := structure.Fields(s)
|
||||
n := structure.Name(s)
|
||||
|
||||
hasZero := structure.HasZero(s)
|
||||
isZero := structure.IsZero(s)
|
||||
|
||||
// Check if it's a struct or a pointer to struct
|
||||
if structure.IsStruct(s) {
|
||||
fmt.Println("s is a struct")
|
||||
@ -68,6 +82,66 @@ if structure.IsStruct(s) {
|
||||
|
||||
```
|
||||
|
||||
### 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 := structure.New(server)
|
||||
|
||||
// Get the Field struct for the "Name" field
|
||||
name := s.Field("Name")
|
||||
|
||||
// Get the underlying value, value => "gopher"
|
||||
value := name.Value().(string)
|
||||
|
||||
// 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)
|
||||
```
|
||||
|
||||
We can also get a slice of Fields from the Struct type to iterate over all
|
||||
fields. This is handy if you whish to examine all fields:
|
||||
|
||||
```go
|
||||
// Convert the fields of a struct to a []*Field
|
||||
fields := s.Fields()
|
||||
|
||||
for _, f := range 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.Value())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
|
||||
5
field.go
5
field.go
@ -56,8 +56,9 @@ func (f *Field) Field(name string) *Field {
|
||||
return field
|
||||
}
|
||||
|
||||
// Field returns the field from a nested struct. It panics if the nested struct
|
||||
// is not exported or if the field was not found.
|
||||
// Field returns the field from a nested struct. The boolean returns true if
|
||||
// the field was not found. It panics if the nested struct is not exported or
|
||||
// if the field was not found.
|
||||
func (f *Field) FieldOk(name string) (*Field, bool) {
|
||||
v := strctVal(f.value.Interface())
|
||||
t := v.Type()
|
||||
|
||||
46
structure.go
46
structure.go
@ -120,43 +120,35 @@ func (s *Struct) Values() []interface{} {
|
||||
return t
|
||||
}
|
||||
|
||||
// Fields returns a slice of field names. A struct tag with the content of "-"
|
||||
// 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 `structure:"-"`
|
||||
//
|
||||
// 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 `structure:"myName,omitnested"`
|
||||
// Field *http.Request `structure:",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) Fields() []string {
|
||||
fields := s.structFields()
|
||||
// It panics if s's kind is not struct.
|
||||
func (s *Struct) Fields() []*Field {
|
||||
t := s.value.Type()
|
||||
|
||||
var keys []string
|
||||
var fields []*Field
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(DefaultTagName))
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
// look out for embedded structs, and convert them to a
|
||||
// []string to be added to the final values slice
|
||||
for _, embeddedVal := range Fields(val.Interface()) {
|
||||
keys = append(keys, embeddedVal)
|
||||
}
|
||||
if tag := field.Tag.Get(DefaultTagName); tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
keys = append(keys, field.Name)
|
||||
f := &Field{
|
||||
field: field,
|
||||
value: s.value.FieldByName(field.Name),
|
||||
}
|
||||
|
||||
return keys
|
||||
fields = append(fields, f)
|
||||
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// Field returns a new Field struct that provides several high level functions
|
||||
@ -340,9 +332,9 @@ func Values(s interface{}) []interface{} {
|
||||
return New(s).Values()
|
||||
}
|
||||
|
||||
// Fields returns a slice of field names. For more info refer to Struct types
|
||||
// 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{}) []string {
|
||||
func Fields(s interface{}) []*Field {
|
||||
return New(s).Fields()
|
||||
}
|
||||
|
||||
|
||||
@ -148,11 +148,16 @@ func ExampleFields() {
|
||||
Number: 1234567,
|
||||
}
|
||||
|
||||
m := Fields(s)
|
||||
fields := Fields(s)
|
||||
|
||||
for i, field := range fields {
|
||||
fmt.Printf("[%d] %+v\n", i, field.Name())
|
||||
}
|
||||
|
||||
fmt.Printf("Fields: %+v\n", m)
|
||||
// Output:
|
||||
// Fields: [Name LastAccessed Number]
|
||||
// [0] Name
|
||||
// [1] LastAccessed
|
||||
// [2] Number
|
||||
}
|
||||
|
||||
func ExampleFields_nested() {
|
||||
@ -162,7 +167,7 @@ func ExampleFields_nested() {
|
||||
}
|
||||
|
||||
type Access struct {
|
||||
Person Person `structure:",omitnested"`
|
||||
Person Person
|
||||
HasPermission bool
|
||||
LastAccessed time.Time
|
||||
}
|
||||
@ -173,13 +178,17 @@ func ExampleFields_nested() {
|
||||
HasPermission: true,
|
||||
}
|
||||
|
||||
// Let's get all fields from the struct s. Note that we don't include the
|
||||
// fields from the Person field anymore due to "omitnested" tag option.
|
||||
m := Fields(s)
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Fields: %+v\n", m)
|
||||
// Output:
|
||||
// Fields: [Person HasPermission LastAccessed]
|
||||
// Access.Person.Name: fatih
|
||||
}
|
||||
|
||||
func ExampleIsZero() {
|
||||
|
||||
@ -399,7 +399,7 @@ func TestFields(t *testing.T) {
|
||||
|
||||
inSlice := func(val string) bool {
|
||||
for _, v := range s {
|
||||
if reflect.DeepEqual(v, val) {
|
||||
if reflect.DeepEqual(v.Name(), val) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -416,27 +416,27 @@ func TestFields(t *testing.T) {
|
||||
func TestFields_OmitNested(t *testing.T) {
|
||||
type A struct {
|
||||
Name string
|
||||
Value string
|
||||
Number int
|
||||
Enabled bool
|
||||
}
|
||||
a := A{Name: "example"}
|
||||
|
||||
type B struct {
|
||||
A A `structure:",omitnested"`
|
||||
A A
|
||||
C int
|
||||
Value string `structure:"-"`
|
||||
Number int
|
||||
}
|
||||
b := &B{A: a, C: 123}
|
||||
|
||||
s := Fields(b)
|
||||
|
||||
if len(s) != 2 {
|
||||
if len(s) != 3 {
|
||||
t.Errorf("Fields should omit nested struct. Expecting 2 got: %d", len(s))
|
||||
}
|
||||
|
||||
inSlice := func(val interface{}) bool {
|
||||
for _, v := range s {
|
||||
if reflect.DeepEqual(v, val) {
|
||||
if reflect.DeepEqual(v.Name(), val) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -450,36 +450,6 @@ func TestFields_OmitNested(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFields_Nested(t *testing.T) {
|
||||
type A struct {
|
||||
Name string
|
||||
}
|
||||
a := A{Name: "example"}
|
||||
|
||||
type B struct {
|
||||
A A
|
||||
C int
|
||||
}
|
||||
b := &B{A: a, C: 123}
|
||||
|
||||
s := Fields(b)
|
||||
|
||||
inSlice := func(val interface{}) bool {
|
||||
for _, v := range s {
|
||||
if reflect.DeepEqual(v, val) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for _, val := range []interface{}{"Name", "A", "C"} {
|
||||
if !inSlice(val) {
|
||||
t.Errorf("Fields should have the value %v", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFields_Anonymous(t *testing.T) {
|
||||
type A struct {
|
||||
Name string
|
||||
@ -497,14 +467,14 @@ func TestFields_Anonymous(t *testing.T) {
|
||||
|
||||
inSlice := func(val interface{}) bool {
|
||||
for _, v := range s {
|
||||
if reflect.DeepEqual(v, val) {
|
||||
if reflect.DeepEqual(v.Name(), val) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for _, val := range []interface{}{"Name", "A", "C"} {
|
||||
for _, val := range []interface{}{"A", "C"} {
|
||||
if !inSlice(val) {
|
||||
t.Errorf("Fields should have the value %v", val)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user