From e40d7a236cd3b0f8c6c772ff6c245dec48fd9dc1 Mon Sep 17 00:00:00 2001 From: Gregory Eremin Date: Sat, 7 Jul 2018 14:11:33 +0200 Subject: [PATCH] Named parameters helpers --- dbc/named.go | 66 ++++++++++++++++++++++++++++++++++++++ dbc/named_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 dbc/named.go create mode 100644 dbc/named_test.go diff --git a/dbc/named.go b/dbc/named.go new file mode 100644 index 0000000..c97650c --- /dev/null +++ b/dbc/named.go @@ -0,0 +1,66 @@ +package dbc + +import ( + "fmt" + "reflect" + + "github.com/localhots/gobelt/reflect2" +) + +type namedParams interface { + Get(name string) (val interface{}, ok bool) +} + +var ( + _ namedParams = namedParamsMap{} + _ namedParams = &namedParamsStruct{} +) + +func newNamedParams(val interface{}) (namedParams, error) { + switch tval := val.(type) { + case map[string]interface{}: + return newNamedParamsMap(tval) + default: + return newNamedParamsStruct(val) + } +} + +type namedParamsMap struct { + m map[string]interface{} +} + +func newNamedParamsMap(m map[string]interface{}) (namedParamsMap, error) { + return namedParamsMap{m}, nil +} + +func (p namedParamsMap) Get(name string) (val interface{}, ok bool) { + val, ok = p.m[name] + return +} + +type namedParamsStruct struct { + s reflect.Value + idx map[string]int +} + +func newNamedParamsStruct(s interface{}) (*namedParamsStruct, error) { + val := reflect.ValueOf(s) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if val.Kind() != reflect.Struct { + return nil, fmt.Errorf("Unsupported named parameters type: %T", s) + } + return &namedParamsStruct{ + s: val, + idx: reflect2.TagIndex(val.Type(), tagName), + }, nil +} + +func (p *namedParamsStruct) Get(name string) (val interface{}, ok bool) { + i, ok := p.idx[name] + if !ok { + return nil, false + } + return p.s.Field(i).Interface(), true +} diff --git a/dbc/named_test.go b/dbc/named_test.go new file mode 100644 index 0000000..048fa3b --- /dev/null +++ b/dbc/named_test.go @@ -0,0 +1,80 @@ +package dbc + +import "testing" + +func TestNamedParamsMap(t *testing.T) { + m, err := newNamedParamsMap(map[string]interface{}{ + "num": 1, + "str": "foo", + }) + if err != nil { + t.Fatalf("Failed to create named params map: %v", err) + } + testNamedParams(t, m) +} + +func TestNamedParamsStruct(t *testing.T) { + type dummy struct { + Num int `db:"num"` + Str string `db:"str"` + } + m, err := newNamedParamsStruct(&dummy{Num: 1, Str: "foo"}) + if err != nil { + t.Fatalf("Failed to create named params struct: %v", err) + } + testNamedParams(t, m) +} + +func testNamedParams(t *testing.T, p namedParams) { + t.Helper() + + v, ok := p.Get("num") + if !ok { + t.Error("num was not found") + } + if v != 1 { + t.Errorf("Expected num to equal 1, got %d", v) + } + + v, ok = p.Get("str") + if !ok { + t.Error("num was not found") + } + if v != "foo" { + t.Errorf("Expected num to equal 'foo', got %q", v) + } + + _, ok = p.Get("missing") + if ok { + t.Error("missing value reportedly found") + } +} + +func BenchmarkNamedParamsMap(b *testing.B) { + m, err := newNamedParamsMap(map[string]interface{}{"foo": 1}) + if err != nil { + b.Fatalf("Failed to create named params map: %v", err) + } + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + m.Get("foo") + } +} + +func BenchmarkNamedParamsStruct(b *testing.B) { + type dummy struct { + Foo int `db:"foo"` + } + m, err := newNamedParamsStruct(&dummy{Foo: 1}) + if err != nil { + b.Fatalf("Failed to create named params struct: %v", err) + } + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + m.Get("foo") + } +}