From 11a0cabe5fb9824520a1bfcb9b0ee28a06150291 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 7 Jan 2015 11:39:12 -0800 Subject: [PATCH] Add unbind to pull data from a struct. --- storer.go | 46 +++++++++++++++++++++++++++++++++++++++++++++- storer_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/storer.go b/storer.go index 2943379..660a8d6 100644 --- a/storer.go +++ b/storer.go @@ -5,6 +5,7 @@ import ( "fmt" "reflect" "time" + "unicode" ) // DataType represents the various types that clients must be able to store. @@ -16,6 +17,8 @@ const ( DateTime ) +var dateTimeType = reflect.TypeOf(time.Time{}) + func (d DataType) String() string { switch d { case Integer: @@ -97,7 +100,7 @@ func (a Attributes) Bind(strct interface{}) error { } field.SetString(v.Value.(string)) case DateTime: - timeType := reflect.TypeOf(time.Time{}) + timeType := dateTimeType if fieldType != timeType { return fmt.Errorf("Bind: Field %s's type should be %s but was %s", k, timeType.String(), fieldType) } @@ -108,6 +111,47 @@ func (a Attributes) Bind(strct interface{}) error { return nil } +// Unbind is the opposite of Bind, taking a struct in and producing a list of attributes. +func Unbind(intf interface{}) Attributes { + structValue := reflect.ValueOf(intf) + if structValue.Kind() == reflect.Ptr { + structValue = structValue.Elem() + } + + structType := structValue.Type() + attr := make(Attributes) + for i := 0; i < structValue.NumField(); i++ { + field := structValue.Field(i) + + name := structType.Field(i).Name + if unicode.IsLower(rune(name[0])) { + continue // Unexported + } + + switch field.Kind() { + case reflect.Struct: + if field.Type() == dateTimeType { + attr[name] = Attribute{ + Type: DateTime, + Value: field.Interface(), + } + } + case reflect.Int: + attr[name] = Attribute{ + Type: Integer, + Value: field.Interface(), + } + case reflect.String: + attr[name] = Attribute{ + Type: String, + Value: field.Interface(), + } + } + } + + return attr +} + // UserNotFound should be returned from Get when the record is not found. var UserNotFound = errors.New("User Not Found") diff --git a/storer_test.go b/storer_test.go index 6d19206..ea122c9 100644 --- a/storer_test.go +++ b/storer_test.go @@ -104,3 +104,51 @@ func TestAttributes_BindTypeFail(t *testing.T) { } } + +func TestAttributes_Unbind(t *testing.T) { + s1 := struct { + Integer int + String string + Time time.Time + + SomethingElse1 int32 + SomethingElse2 *Config + + unexported int + }{5, "string", time.Now(), 5, nil, 5} + + attr := Unbind(s1) + if len(attr) != 3 { + t.Error("Expected three fields, got:", len(attr)) + } + + if v, ok := attr["Integer"]; !ok { + t.Error("Could not find Integer entry.") + } else if v.Type != Integer { + t.Error("Described type is wrong:", v.Type) + } else if i, ok := v.Value.(int); !ok { + t.Errorf("Underlying type is wrong: %T", v) + } else if s1.Integer != i { + t.Error("Underlying value is wrong:", i) + } + + if v, ok := attr["String"]; !ok { + t.Error("Could not find String entry.") + } else if v.Type != String { + t.Error("Described type is wrong:", v.Type) + } else if s, ok := v.Value.(string); !ok { + t.Errorf("Underlying type is wrong: %T", v) + } else if s1.String != s { + t.Error("Underlying value is wrong:", s) + } + + if v, ok := attr["Time"]; !ok { + t.Error("Could not find Time entry.") + } else if v.Type != DateTime { + t.Error("Described type is wrong:", v.Type) + } else if i, ok := v.Value.(time.Time); !ok { + t.Errorf("Underlying type is wrong: %T", v) + } else if s1.Time != i { + t.Error("Underlying value is wrong:", i) + } +}