From abd60bc091505d0cabfce80be86c8a15b5100251 Mon Sep 17 00:00:00 2001 From: Rene Zbinden Date: Fri, 19 Jun 2020 09:30:46 +0200 Subject: [PATCH] return struct instead of interface in hash and etcd implementation --- etcd/config.go | 5 ++++- etcd/etcdv3_test.go | 12 ++++++------ hash/config.go | 15 +++++++++------ hash/config_test.go | 16 ++++++++-------- hash/hash.go | 34 +++++++++++++++++----------------- hash/hash_test.go | 14 +++++++------- hash/watch.go | 2 +- 7 files changed, 52 insertions(+), 46 deletions(-) diff --git a/etcd/config.go b/etcd/config.go index 7ebe1c7..efb06fc 100644 --- a/etcd/config.go +++ b/etcd/config.go @@ -20,9 +20,12 @@ const ( DfltSeparator = '/' ) +// verify that *Backend implements store.Backend +var _ store.Backend = &Backend{} + // New configures a new etcd backend. At least Withclientv3 or WithEndoints option // has to be used. -func New(opts ...Opt) (store.Backend, error) { +func New(opts ...Opt) (*Backend, error) { e := Backend{ separator: DfltSeparator, errHandler: func(err error) error { return err }, diff --git a/etcd/etcdv3_test.go b/etcd/etcdv3_test.go index 7c36eec..59aa57e 100644 --- a/etcd/etcdv3_test.go +++ b/etcd/etcdv3_test.go @@ -33,37 +33,37 @@ func TestKeyFunctions(t *testing.T) { t.Run("absolute key", func(t *testing.T) { b, err := New(WithClient(cli), WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, b.(*Backend).AbsKey(relkey), abskey) + assert.Equal(t, b.AbsKey(relkey), abskey) }) t.Run("absolute key from key with leading separator", func(t *testing.T) { b, err := New(WithClient(cli), WithPrefix(prefix)) assert.NoError(t, err) - assert.NotEqual(t, b.(*Backend).AbsKey("/"+relkey), abskey) + assert.NotEqual(t, b.AbsKey("/"+relkey), abskey) }) t.Run("relative key", func(t *testing.T) { b, err := New(WithClient(cli), WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, b.(*Backend).RelKey(abskey), relkey) + assert.Equal(t, b.RelKey(abskey), relkey) }) t.Run("join key", func(t *testing.T) { b, err := New(WithClient(cli), WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, b.(*Backend).JoinKey(middle, suffix), relkey) + assert.Equal(t, b.JoinKey(middle, suffix), relkey) }) t.Run("split key", func(t *testing.T) { b, err := New(WithClient(cli), WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, b.(*Backend).SplitKey(relkey), []string{middle, suffix}) + assert.Equal(t, b.SplitKey(relkey), []string{middle, suffix}) }) t.Run("split key", func(t *testing.T) { b, err := New(WithClient(cli), WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, b.(*Backend).KeyLeaf(relkey), suffix) + assert.Equal(t, b.KeyLeaf(relkey), suffix) }) } diff --git a/hash/config.go b/hash/config.go index 8f51aa5..66f5eda 100644 --- a/hash/config.go +++ b/hash/config.go @@ -14,9 +14,12 @@ const ( DfltSeparator = '/' ) +// verify that *Backend implements store.Backend +var _ store.Backend = &Backend{} + // New returns a new store and the function to stop the cache janitor -func New(opts ...Opt) (store.Backend, error) { - h := &Hash{ +func New(opts ...Opt) (*Backend, error) { + h := &Backend{ separator: DfltSeparator, data: make(map[string]entry), watchKey: make(map[string][]notifyCallbackFunc), @@ -33,12 +36,12 @@ func New(opts ...Opt) (store.Backend, error) { } // Opt is a functional option to configure backend -type Opt func(*Hash) error +type Opt func(*Backend) error // WithPrefix is an option to set the global prefix used for the backend // WithPrefix("global") results in keys prefixed "global" + separator func WithPrefix(p string) Opt { - return func(h *Hash) error { + return func(h *Backend) error { h.prefix = p if h.prefix != "" { @@ -51,7 +54,7 @@ func WithPrefix(p string) Opt { // WithSeparator is an option to overwrite the default separator for keys func WithSeparator(s rune) Opt { - return func(h *Hash) error { + return func(h *Backend) error { h.separator = s if h.prefix != "" { @@ -65,7 +68,7 @@ func WithSeparator(s rune) Opt { // WithTTL is an option to add a time to live for hash entries // the TTL will be the preset which can be overwritten for each Operation supporting WithTTL func WithTTL(ttl time.Duration) Opt { - return func(h *Hash) error { + return func(h *Backend) error { if ttl < 0 { return errors.New("ttl cannot be < 0") } diff --git a/hash/config_test.go b/hash/config_test.go index 0c897a8..dcc3683 100644 --- a/hash/config_test.go +++ b/hash/config_test.go @@ -13,26 +13,26 @@ func TestOptions(t *testing.T) { ) t.Run("WithPrefix", func(t *testing.T) { - h := &Hash{} + h := &Backend{} assert.NoError(t, WithPrefix(prefix)(h)) assert.EqualValues(t, h.prefix, prefix) }) t.Run("WithSeparator", func(t *testing.T) { s := '!' - h := &Hash{} + h := &Backend{} assert.NoError(t, WithSeparator(s)(h)) assert.EqualValues(t, h.separator, s) }) t.Run("WithTTL", func(t *testing.T) { - h := &Hash{} + h := &Backend{} assert.NoError(t, WithTTL(1)(h)) assert.EqualValues(t, h.TTL(), 1) }) t.Run("WithTTL invalid value", func(t *testing.T) { - h := &Hash{} + h := &Backend{} assert.Error(t, WithTTL(-1)(h)) assert.EqualValues(t, h.TTL(), 0) }) @@ -46,15 +46,15 @@ func TestOptions(t *testing.T) { t.Run("WithPrefix and default separator", func(t *testing.T) { h, err := New(WithPrefix(prefix)) assert.NoError(t, err) - assert.EqualValues(t, h.(*Hash).prefix, prefix) - assert.EqualValues(t, h.(*Hash).prefixReady2Use, fmt.Sprintf("%s/", prefix)) + assert.EqualValues(t, h.prefix, prefix) + assert.EqualValues(t, h.prefixReady2Use, fmt.Sprintf("%s/", prefix)) }) t.Run("WithPrefix and WithSeparator", func(t *testing.T) { s := '!' h, err := New(WithPrefix(prefix), WithSeparator(s)) assert.NoError(t, err) - assert.EqualValues(t, h.(*Hash).separator, s) - assert.EqualValues(t, h.(*Hash).prefixReady2Use, fmt.Sprintf("%s%c", prefix, s)) + assert.EqualValues(t, h.separator, s) + assert.EqualValues(t, h.prefixReady2Use, fmt.Sprintf("%s%c", prefix, s)) }) } diff --git a/hash/hash.go b/hash/hash.go index 693a5bb..bfccb94 100644 --- a/hash/hash.go +++ b/hash/hash.go @@ -20,8 +20,8 @@ type entry struct { TTL time.Duration } -// Hash table backend -type Hash struct { +// Backend table backend +type Backend struct { sync.RWMutex prefix string prefixReady2Use string @@ -36,7 +36,7 @@ type Hash struct { // AbsKey("a/b") with prefix "root" and separator '/' returns "root/a/b" // AbsKey does not validate the given key. // Given a faulty relative key returns a faulty absolute key. -func (h *Hash) AbsKey(k string) string { +func (h *Backend) AbsKey(k string) string { if h.prefix == "" { return k } @@ -48,7 +48,7 @@ func (h *Hash) AbsKey(k string) string { // RelKey("root/a/b") with prefix "root" and separator '/' returns "a/b" // RelKey does not validate the given key. // Given a faulty absolute key returns a faulty relativ key. -func (h *Hash) RelKey(k string) string { +func (h *Backend) RelKey(k string) string { if h.prefix == "" { return k } @@ -58,18 +58,18 @@ func (h *Hash) RelKey(k string) string { // JoinKey returns a formatted key // it joins the given elements with the correct delimiter -func (h *Hash) JoinKey(args ...string) string { +func (h *Backend) JoinKey(args ...string) string { return strings.Join(args, string(h.separator)) } // SplitKey returns the key elements // it splits the given key in its elements with the correct delimiter -func (h *Hash) SplitKey(key string) []string { +func (h *Backend) SplitKey(key string) []string { return strings.Split(strings.Trim(key, string(h.separator)), string(h.separator)) } // KeyLeaf returns the leave (last) element of a key -func (h *Hash) KeyLeaf(key string) string { +func (h *Backend) KeyLeaf(key string) string { elems := h.SplitKey(key) if len(elems) > 0 { return elems[len(elems)-1] @@ -79,12 +79,12 @@ func (h *Hash) KeyLeaf(key string) string { } // TTL returns the configured TTL -func (h *Hash) TTL() time.Duration { +func (h *Backend) TTL() time.Duration { return h.ttl } // register a watch key -func (h *Hash) register(key string, prefix bool, f notifyCallbackFunc) { +func (h *Backend) register(key string, prefix bool, f notifyCallbackFunc) { h.Lock() if prefix { if _, ok := h.watchKeyPrefix[key]; !ok { @@ -102,7 +102,7 @@ func (h *Hash) register(key string, prefix bool, f notifyCallbackFunc) { } // notify all registrants -func (h *Hash) notify(key string, msg changeNotification) { +func (h *Backend) notify(key string, msg changeNotification) { for _, f := range h.watchKey[key] { f(msg) } @@ -117,7 +117,7 @@ func (h *Hash) notify(key string, msg changeNotification) { } // exists checks if an entry is expired -func (h *Hash) exists(e entry) bool { +func (h *Backend) exists(e entry) bool { // entry has no TTL or an unexpired TTL if e.TTL == 0 || e.Updated.Add(e.TTL).After(time.Now()) { return true @@ -129,7 +129,7 @@ func (h *Hash) exists(e entry) bool { } // keys returns all keys with prefix -func (h *Hash) keys(prefix string) []string { +func (h *Backend) keys(prefix string) []string { keys := []string{} h.RLock() @@ -147,7 +147,7 @@ func (h *Hash) keys(prefix string) []string { // Get returns a list of store entries // WithPrefix, WithHandler are supported // WithContext will be ignored -func (h *Hash) Get(key string, ops ...store.GetOption) ([]store.Entry, error) { +func (h *Backend) Get(key string, ops ...store.GetOption) ([]store.Entry, error) { opts := &store.GetOptions{} for _, op := range ops { @@ -213,7 +213,7 @@ func (h *Hash) Get(key string, ops ...store.GetOption) ([]store.Entry, error) { // Put insert and/or update an entry in the store // WithTTL, WithInsert are supported // WithContext will be ignored -func (h *Hash) Put(e *store.Entry, ops ...store.PutOption) (bool, error) { +func (h *Backend) Put(e *store.Entry, ops ...store.PutOption) (bool, error) { opts := &store.PutOptions{} for _, op := range ops { @@ -260,7 +260,7 @@ func (h *Hash) Put(e *store.Entry, ops ...store.PutOption) (bool, error) { // Del an entry from the store // WithPrefix is supported // WithContext will be ignored -func (h *Hash) Del(key string, ops ...store.DelOption) (int64, error) { +func (h *Backend) Del(key string, ops ...store.DelOption) (int64, error) { var count int64 opts := &store.DelOptions{} @@ -288,7 +288,7 @@ func (h *Hash) Del(key string, ops ...store.DelOption) (int64, error) { return count, nil } -func (h *Hash) del(e store.Entry) { +func (h *Backend) del(e store.Entry) { absKey := h.AbsKey(e.Key) h.Lock() @@ -302,6 +302,6 @@ func (h *Hash) del(e store.Entry) { // Close exists to implement the store interface, there // is no connection to close. -func (h *Hash) Close() error { +func (h *Backend) Close() error { return nil } diff --git a/hash/hash_test.go b/hash/hash_test.go index 31c122e..45d0ddd 100644 --- a/hash/hash_test.go +++ b/hash/hash_test.go @@ -26,42 +26,42 @@ func TestKeyFunctions(t *testing.T) { t.Run("absolute key", func(t *testing.T) { h, err := hash.New(hash.WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, h.(*hash.Hash).AbsKey(relkey), abskey) + assert.Equal(t, h.AbsKey(relkey), abskey) assert.NoError(t, h.Close()) }) t.Run("absolute key from key with leading separator", func(t *testing.T) { h, err := hash.New(hash.WithPrefix(prefix)) assert.NoError(t, err) - assert.NotEqual(t, h.(*hash.Hash).AbsKey("/"+relkey), abskey) + assert.NotEqual(t, h.AbsKey("/"+relkey), abskey) assert.NoError(t, h.Close()) }) t.Run("relative key", func(t *testing.T) { h, err := hash.New(hash.WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, h.(*hash.Hash).RelKey(abskey), relkey) + assert.Equal(t, h.RelKey(abskey), relkey) assert.NoError(t, h.Close()) }) t.Run("join key", func(t *testing.T) { h, err := hash.New(hash.WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, h.(*hash.Hash).JoinKey(middle, suffix), relkey) + assert.Equal(t, h.JoinKey(middle, suffix), relkey) assert.NoError(t, h.Close()) }) t.Run("split key", func(t *testing.T) { h, err := hash.New(hash.WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, h.(*hash.Hash).SplitKey(relkey), []string{middle, suffix}) + assert.Equal(t, h.SplitKey(relkey), []string{middle, suffix}) assert.NoError(t, h.Close()) }) t.Run("split key", func(t *testing.T) { h, err := hash.New(hash.WithPrefix(prefix)) assert.NoError(t, err) - assert.Equal(t, h.(*hash.Hash).KeyLeaf(relkey), suffix) + assert.Equal(t, h.KeyLeaf(relkey), suffix) assert.NoError(t, h.Close()) }) } @@ -391,7 +391,7 @@ func TestMarshal(t *testing.T) { } } -func TestHashWithTTL(t *testing.T) { +func TestBackendWithTTL(t *testing.T) { for _, p := range []string{"", "root"} { opts := []hash.Opt{hash.WithTTL(10 * time.Millisecond)} if p != "" { diff --git a/hash/watch.go b/hash/watch.go index 7850bf3..0064706 100644 --- a/hash/watch.go +++ b/hash/watch.go @@ -9,7 +9,7 @@ import ( // Watch a key or prefix // WithPrefix, WithContext, WithNotifyCreated, WithErrorHandler are supported -func (h *Hash) Watch(key string, w store.Watcher, ops ...store.WatchOption) error { +func (h *Backend) Watch(key string, w store.Watcher, ops ...store.WatchOption) error { opts := &store.WatchOptions{} for _, op := range ops {