structure: more improvements
This commit is contained in:
parent
f614213600
commit
1c91005c39
116
README.md
116
README.md
@ -18,49 +18,63 @@ Lets define and declare a struct
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Name string
|
Name string `json:"name,omitempty"`
|
||||||
ID int32
|
ID int
|
||||||
Enabled bool
|
Enabled bool
|
||||||
|
users []string // not exported
|
||||||
|
http.Server // embedded
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &Server{
|
server := &Server{
|
||||||
Name: "gopher",
|
Name: "gopher",
|
||||||
ID: 123456,
|
ID: 123456,
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Struct methods
|
||||||
|
|
||||||
|
Let's create a new `Struct` type.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
// Create a new struct type:
|
||||||
|
s := structure.New(server)
|
||||||
|
|
||||||
// Convert a struct to a map[string]interface{}
|
// Convert a struct to a map[string]interface{}
|
||||||
// => {"Name":"gopher", "ID":123456, "Enabled":true}
|
// => {"Name":"gopher", "ID":123456, "Enabled":true}
|
||||||
m := structure.Map(s)
|
m := s.Map()
|
||||||
|
|
||||||
// Convert the values of a struct to a []interface{}
|
// Convert the values of a struct to a []interface{}
|
||||||
// => [123456, "gopher", true]
|
// => ["gopher", 123456, true]
|
||||||
v := structure.Values(s)
|
v := s.Values()
|
||||||
|
|
||||||
// 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")
|
|
||||||
|
|
||||||
// Check if any field of a struct is initialized or not.
|
// 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")
|
fmt.Println("s has a zero value field")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all fields of a struct is initialized or not.
|
// 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")
|
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
|
// Check if it's a struct or a pointer to struct
|
||||||
if structure.IsStruct(s) {
|
if structure.IsStruct(s) {
|
||||||
fmt.Println("s is a struct")
|
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
|
## Credits
|
||||||
|
|
||||||
* [Fatih Arslan](https://github.com/fatih)
|
* [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
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field returns the field from a nested struct. It panics if the nested struct
|
// Field returns the field from a nested struct. The boolean returns true if
|
||||||
// is not exported or if the field was not found.
|
// 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) {
|
func (f *Field) FieldOk(name string) (*Field, bool) {
|
||||||
v := strctVal(f.value.Interface())
|
v := strctVal(f.value.Interface())
|
||||||
t := v.Type()
|
t := v.Type()
|
||||||
|
|||||||
46
structure.go
46
structure.go
@ -120,43 +120,35 @@ func (s *Struct) Values() []interface{} {
|
|||||||
return t
|
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:
|
// ignores the checking of that particular field. Example:
|
||||||
//
|
//
|
||||||
// // Field is ignored by this package.
|
// // Field is ignored by this package.
|
||||||
// Field bool `structure:"-"`
|
// Field bool `structure:"-"`
|
||||||
//
|
//
|
||||||
// A value with the option of "omitnested" stops iterating further if the type
|
// It panics if s's kind is not struct.
|
||||||
// is a struct. Example:
|
func (s *Struct) Fields() []*Field {
|
||||||
//
|
t := s.value.Type()
|
||||||
// // 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()
|
|
||||||
|
|
||||||
var keys []string
|
var fields []*Field
|
||||||
|
|
||||||
for _, field := range fields {
|
for i := 0; i < t.NumField(); i++ {
|
||||||
val := s.value.FieldByName(field.Name)
|
field := t.Field(i)
|
||||||
|
|
||||||
_, tagOpts := parseTag(field.Tag.Get(DefaultTagName))
|
if tag := field.Tag.Get(DefaultTagName); tag == "-" {
|
||||||
|
continue
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keys = append(keys, field.Name)
|
f := &Field{
|
||||||
|
field: field,
|
||||||
|
value: s.value.FieldByName(field.Name),
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = append(fields, f)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys
|
return fields
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field returns a new Field struct that provides several high level functions
|
// 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()
|
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.
|
// 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()
|
return New(s).Fields()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -148,11 +148,16 @@ func ExampleFields() {
|
|||||||
Number: 1234567,
|
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:
|
// Output:
|
||||||
// Fields: [Name LastAccessed Number]
|
// [0] Name
|
||||||
|
// [1] LastAccessed
|
||||||
|
// [2] Number
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleFields_nested() {
|
func ExampleFields_nested() {
|
||||||
@ -162,7 +167,7 @@ func ExampleFields_nested() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Access struct {
|
type Access struct {
|
||||||
Person Person `structure:",omitnested"`
|
Person Person
|
||||||
HasPermission bool
|
HasPermission bool
|
||||||
LastAccessed time.Time
|
LastAccessed time.Time
|
||||||
}
|
}
|
||||||
@ -173,13 +178,17 @@ func ExampleFields_nested() {
|
|||||||
HasPermission: true,
|
HasPermission: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's get all fields from the struct s. Note that we don't include the
|
// Let's get all fields from the struct s.
|
||||||
// fields from the Person field anymore due to "omitnested" tag option.
|
fields := Fields(s)
|
||||||
m := 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:
|
// Output:
|
||||||
// Fields: [Person HasPermission LastAccessed]
|
// Access.Person.Name: fatih
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleIsZero() {
|
func ExampleIsZero() {
|
||||||
|
|||||||
@ -399,7 +399,7 @@ func TestFields(t *testing.T) {
|
|||||||
|
|
||||||
inSlice := func(val string) bool {
|
inSlice := func(val string) bool {
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
if reflect.DeepEqual(v, val) {
|
if reflect.DeepEqual(v.Name(), val) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -416,27 +416,27 @@ func TestFields(t *testing.T) {
|
|||||||
func TestFields_OmitNested(t *testing.T) {
|
func TestFields_OmitNested(t *testing.T) {
|
||||||
type A struct {
|
type A struct {
|
||||||
Name string
|
Name string
|
||||||
Value string
|
|
||||||
Number int
|
|
||||||
Enabled bool
|
Enabled bool
|
||||||
}
|
}
|
||||||
a := A{Name: "example"}
|
a := A{Name: "example"}
|
||||||
|
|
||||||
type B struct {
|
type B struct {
|
||||||
A A `structure:",omitnested"`
|
A A
|
||||||
C int
|
C int
|
||||||
|
Value string `structure:"-"`
|
||||||
|
Number int
|
||||||
}
|
}
|
||||||
b := &B{A: a, C: 123}
|
b := &B{A: a, C: 123}
|
||||||
|
|
||||||
s := Fields(b)
|
s := Fields(b)
|
||||||
|
|
||||||
if len(s) != 2 {
|
if len(s) != 3 {
|
||||||
t.Errorf("Fields should omit nested struct. Expecting 2 got: %d", len(s))
|
t.Errorf("Fields should omit nested struct. Expecting 2 got: %d", len(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
inSlice := func(val interface{}) bool {
|
inSlice := func(val interface{}) bool {
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
if reflect.DeepEqual(v, val) {
|
if reflect.DeepEqual(v.Name(), val) {
|
||||||
return true
|
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) {
|
func TestFields_Anonymous(t *testing.T) {
|
||||||
type A struct {
|
type A struct {
|
||||||
Name string
|
Name string
|
||||||
@ -497,14 +467,14 @@ func TestFields_Anonymous(t *testing.T) {
|
|||||||
|
|
||||||
inSlice := func(val interface{}) bool {
|
inSlice := func(val interface{}) bool {
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
if reflect.DeepEqual(v, val) {
|
if reflect.DeepEqual(v.Name(), val) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, val := range []interface{}{"Name", "A", "C"} {
|
for _, val := range []interface{}{"A", "C"} {
|
||||||
if !inSlice(val) {
|
if !inSlice(val) {
|
||||||
t.Errorf("Fields should have the value %v", val)
|
t.Errorf("Fields should have the value %v", val)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user