Skip to content

Commit

Permalink
add FromMap constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
zekroTJA committed Mar 25, 2023
1 parent a3ee3ae commit 78c8426
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 14 deletions.
8 changes: 7 additions & 1 deletion errors.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package timedmap

import "errors"
import (
"errors"
)

var (
// ErrKeyNotFound is returned when a key was
// requested which is not present in the map.
ErrKeyNotFound = errors.New("key not found")

// ErrValueNoMap is returned when a value passed
// expected was of another type.
ErrValueNoMap = errors.New("value is not of type map")
)
67 changes: 54 additions & 13 deletions timedmap.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package timedmap

import (
"reflect"
"sync"
"time"
)
Expand Down Expand Up @@ -53,23 +54,39 @@ type element struct {
// can also be used to re-define the specification of
// the cleanup loop when already running if you want to.
func New(cleanupTickTime time.Duration, tickerChan ...<-chan time.Time) *TimedMap {
tm := &TimedMap{
container: make(map[keyWrap]*element),
cleanerStopChan: make(chan bool),
elementPool: &sync.Pool{
New: func() interface{} {
return new(element)
},
},
return newTimedMap(make(map[keyWrap]*element), cleanupTickTime, tickerChan)
}

func FromMap(
m interface{},
expiration time.Duration,
cleanupTickTime time.Duration,
tickerChan ...<-chan time.Time,
) (*TimedMap, error) {
mv := reflect.ValueOf(m)
if mv.Kind() != reflect.Map {
return nil, ErrValueNoMap
}

if len(tickerChan) > 0 {
tm.StartCleanerExternal(tickerChan[0])
} else if cleanupTickTime > 0 {
tm.StartCleanerInternal(cleanupTickTime)
exp := time.Now().Add(expiration)
container := make(map[keyWrap]*element)

iter := mv.MapRange()
for iter.Next() {
key := iter.Key()
val := iter.Value()
kw := keyWrap{
sec: 0,
key: key.Interface(),
}
el := &element{
value: val.Interface(),
expires: exp,
}
container[kw] = el
}

return tm
return newTimedMap(container, cleanupTickTime, tickerChan), nil
}

// Section returns a sectioned subset of
Expand Down Expand Up @@ -383,3 +400,27 @@ func (tm *TimedMap) getSnapshot(sec int) (m map[interface{}]interface{}) {

return
}

func newTimedMap(
container map[keyWrap]*element,
cleanupTickTime time.Duration,
tickerChan []<-chan time.Time,
) *TimedMap {
tm := &TimedMap{
container: container,
cleanerStopChan: make(chan bool),
elementPool: &sync.Pool{
New: func() interface{} {
return new(element)
},
},
}

if len(tickerChan) > 0 {
tm.StartCleanerExternal(tickerChan[0])
} else if cleanupTickTime > 0 {
tm.StartCleanerInternal(cleanupTickTime)
}

return tm
}
59 changes: 59 additions & 0 deletions timedmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,65 @@ func TestNew(t *testing.T) {
assert.True(t, tm.cleanerRunning)
}

func TestFromMap(t *testing.T) {
t.Run("map-string-string", func(t *testing.T) {
tm, err := FromMap(
map[string]string{"foo": "bar", "bazz": "fuzz"},
200*time.Millisecond, 10*time.Millisecond)
assert.Nil(t, err)

assert.EqualValues(t, "bar", tm.GetValue("foo"))
assert.EqualValues(t, "fuzz", tm.GetValue("bazz"))

time.Sleep(500 * time.Millisecond)

assert.False(t, tm.Contains("foo"))
assert.False(t, tm.Contains("bazz"))
})

t.Run("map-int-interface", func(t *testing.T) {
tm, err := FromMap(
map[int]interface{}{1: "foo", 2: 3.456},
200*time.Millisecond, 10*time.Millisecond)
assert.Nil(t, err)

assert.EqualValues(t, "foo", tm.GetValue(1))
assert.EqualValues(t, 3.456, tm.GetValue(2))

time.Sleep(500 * time.Millisecond)

assert.False(t, tm.Contains(1))
assert.False(t, tm.Contains(2))
})

t.Run("map-interface-interface", func(t *testing.T) {
tm, err := FromMap(
map[interface{}]interface{}{1: "foo", "a": 3.456},
200*time.Millisecond, 10*time.Millisecond)
assert.Nil(t, err)

assert.EqualValues(t, "foo", tm.GetValue(1))
assert.EqualValues(t, 3.456, tm.GetValue("a"))

time.Sleep(500 * time.Millisecond)

assert.False(t, tm.Contains(1))
assert.False(t, tm.Contains("a"))
})

t.Run("non-map", func(t *testing.T) {
_, err := FromMap(
"this is not a map",
200*time.Millisecond, 10*time.Millisecond)
assert.ErrorIs(t, err, ErrValueNoMap)

_, err = FromMap(
nil,
200*time.Millisecond, 10*time.Millisecond)
assert.ErrorIs(t, err, ErrValueNoMap)
})
}

func TestFlush(t *testing.T) {
tm := New(dCleanupTick)

Expand Down

0 comments on commit 78c8426

Please sign in to comment.