use stringer if string tag option is used

This commit is contained in:
Kunal Powar 2015-12-24 14:36:13 +05:30
parent dd04ebad3d
commit 89d5c9d448
2 changed files with 113 additions and 1 deletions

View File

@ -1,7 +1,11 @@
// Package structs contains various utilities functions to work with structs. // Package structs contains various utilities functions to work with structs.
package structs package structs
import "reflect" import (
"fmt"
"reflect"
)
var ( var (
// DefaultTagName is the default tag name for struct fields which provides // DefaultTagName is the default tag name for struct fields which provides
@ -42,6 +46,12 @@ func New(s interface{}) *Struct {
// // Field is ignored by this package. // // Field is ignored by this package.
// Field bool `structs:"-"` // 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 "omitnested" stops iterating further if the type // A tag value with the option of "omitnested" stops iterating further if the type
// is a struct. Example: // is a struct. Example:
// //
@ -104,6 +114,14 @@ func (s *Struct) Map() map[string]interface{} {
finalVal = val.Interface() finalVal = val.Interface()
} }
if tagOpts.Has("string") {
s, ok := val.Interface().(fmt.Stringer)
if ok {
out[name] = s.String()
}
continue
}
out[name] = finalVal out[name] = finalVal
} }
@ -153,6 +171,14 @@ func (s *Struct) Values() []interface{} {
} }
} }
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") { if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
// look out for embedded structs, and convert them to a // look out for embedded structs, and convert them to a
// []interface{} to be added to the final values slice // []interface{} to be added to the final values slice

View File

@ -910,3 +910,89 @@ func TestNestedNilPointer(t *testing.T) {
_ = Map(personWithDog) // Panics _ = Map(personWithDog) // Panics
_ = Map(personWithDogWithCollar) // Doesn't panic _ = Map(personWithDogWithCollar) // Doesn't panic
} }
type Person struct {
Name string
Age int
}
func (p *Person) String() string {
return fmt.Sprintf("%s(%d)", p.Name, p.Age)
}
func TestTagWithStringOption(t *testing.T) {
type Address struct {
Country string `json:"country"`
Person *Person `json:"person,string"`
}
person := &Person{
Name: "John",
Age: 23,
}
address := &Address{
Country: "EU",
Person: person,
}
defer func() {
err := recover()
if err != nil {
fmt.Printf("err %+v\n", err)
t.Error("Internal nil pointer should not panic")
}
}()
s := New(address)
s.TagName = "json"
m := s.Map()
if m["person"] != person.String() {
t.Errorf("Value for field person should be %s, got: %s", person.String(), m["person"])
}
vs := s.Values()
if vs[1] != person.String() {
t.Errorf("Value for 2nd field (person) should be %t, got: %t", person.String(), vs[1])
}
}
type Animal struct {
Name string
Age int
}
type Dog struct {
Animal *Animal `json:"animal,string"`
}
func TestNonStringerTagWithStringOption(t *testing.T) {
a := &Animal{
Name: "Fluff",
Age: 4,
}
d := &Dog{
Animal: a,
}
defer func() {
err := recover()
if err != nil {
fmt.Printf("err %+v\n", err)
t.Error("Internal nil pointer should not panic")
}
}()
s := New(d)
s.TagName = "json"
m := s.Map()
if _, exists := m["animal"]; exists {
t.Errorf("Value for field Animal should not exist")
}
}