diff --git a/README.md b/README.md index 2a954fb..944ced5 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,7 @@ s := &Server{ Convert to a `map[string]interface{}` ```go -m, err := structure.ToMap(s) -if err != nil { - panic(err) -} +m := structure.ToMap(s) // prints: map[string]interface {}{"Name":"Arslan", "ID":123456, "Enabled":true} fmt.Printf("%#v", m) @@ -46,10 +43,7 @@ Convert to a `[]interface{}`. Slice values are **sorted** by default according to the field names. ```go -m, err := structure.ToSlice(s) -if err != nil { - panic(err) -} +m := structure.ToSlice(s) // prints: []interface {}{true, 123456, "Arslan"} fmt.Printf("%#v", m) diff --git a/structure.go b/structure.go index df5720c..0fb99e3 100644 --- a/structure.go +++ b/structure.go @@ -2,19 +2,16 @@ package structure import ( - "errors" "reflect" "sort" ) -// ErrNotStruct is returned when the passed value is not a struct -var ErrNotStruct = errors.New("not struct") - -// ToMap converts a struct to a map[string]interface{}. The default map key -// string is the struct fieldname but this can be changed by defining a -// "structure" tag key if needed. Note that only exported fields of a struct -// can be accessed, non exported fields will be neglected. -func ToMap(s interface{}) (map[string]interface{}, error) { +// ToMap converts the given s struct to a map[string]interface{}. The default +// map key names are the struct fieldnames but this can be changed by defining +// a "structure" tag key if needed. 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 ToMap(s interface{}) map[string]interface{} { out := make(map[string]interface{}) t := reflect.TypeOf(s) @@ -27,7 +24,7 @@ func ToMap(s interface{}) (map[string]interface{}, error) { } if t.Kind() != reflect.Struct { - return nil, ErrNotStruct + panic("not struct") } for i := 0; i < t.NumField(); i++ { @@ -48,17 +45,15 @@ func ToMap(s interface{}) (map[string]interface{}, error) { out[name] = v.Field(i).Interface() } - return out, nil + return out } -// ToSlice converts a struct's field values to a []interface{}. Values are -// inserted and sorted according to the field names. Note that only exported -// fields of a struct can be accessed, non exported fields will be neglected. -func ToSlice(s interface{}) ([]interface{}, error) { - m, err := ToMap(s) - if err != nil { - return nil, err - } +// ToSlice converts the given s struct's field values to a []interface{}. +// Values are inserted and sorted according to the field names. 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 ToSlice(s interface{}) []interface{} { + m := ToMap(s) keys := make([]string, len(m)) count := 0 @@ -75,10 +70,26 @@ func ToSlice(s interface{}) ([]interface{}, error) { t[i] = m[key] } - return t, nil + return t } +// Fields returns a sorted slice of field names. +func Fields(s interface{}) []string { + m := ToMap(s) + + keys := make([]string, len(m)) + count := 0 + for k := range m { + keys[count] = k + count++ + } + + sort.Strings(keys) + + return keys +} + // IsStruct returns true if the given variable is a struct or a pointer to // struct. func IsStruct(s interface{}) bool { diff --git a/structure_example_test.go b/structure_example_test.go index eba2a26..4653b98 100644 --- a/structure_example_test.go +++ b/structure_example_test.go @@ -15,10 +15,7 @@ func ExampleToMap() { Enabled: true, } - m, err := ToMap(s) - if err != nil { - panic(err) - } + m := ToMap(s) fmt.Printf("%#v\n", m["Name"]) fmt.Printf("%#v\n", m["ID"]) @@ -43,10 +40,7 @@ func ExampleToMap_tags() { ID: 789012, } - m, err := ToMap(s) - if err != nil { - panic(err) - } + m := ToMap(s) // access them by the custom tags defined above fmt.Printf("%#v\n", m["server_name"]) @@ -72,10 +66,7 @@ func ExampleToSlice() { Enabled: false, } - m, err := ToSlice(s) - if err != nil { - panic(err) - } + m := ToSlice(s) // note that the output is sorted according to the field names fmt.Printf("%#v\n", m) diff --git a/structure_test.go b/structure_test.go index 25b624c..8067b4b 100644 --- a/structure_test.go +++ b/structure_test.go @@ -8,11 +8,15 @@ import ( func TestToMapNonStruct(t *testing.T) { foo := []string{"foo"} - _, err := ToMap(foo) - if err == nil { - t.Error("ToMap shouldn't accept non struct types") - } + defer func() { + err := recover() + if err == nil { + t.Error("Passing a non struct into ToMap should panic") + } + }() + // this should panic. We are going to recover and and test it + _ = ToMap(foo) } func TestToMap(t *testing.T) { @@ -26,10 +30,7 @@ func TestToMap(t *testing.T) { C: true, } - a, err := ToMap(T) - if err != nil { - t.Error(err) - } + a := ToMap(T) if typ := reflect.TypeOf(a).Kind(); typ != reflect.Map { t.Errorf("ToMap should return a map type, got: %v", typ) @@ -69,19 +70,14 @@ func TestToMap_Tag(t *testing.T) { C: true, } - a, err := ToMap(T) - if err != nil { - t.Error(err) - } + a := ToMap(T) inMap := func(key interface{}) bool { for k := range a { if reflect.DeepEqual(k, key) { - return true } } - return false } @@ -117,10 +113,7 @@ func TestToSlice(t *testing.T) { C: true, } - s, err := ToSlice(T) - if err != nil { - t.Error(err) - } + s := ToSlice(T) inSlice := func(val interface{}) bool { for _, v := range s { @@ -128,7 +121,6 @@ func TestToSlice(t *testing.T) { return true } } - return false }