diff --git a/README.md b/README.md index 2091738..a1b6690 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,14 @@ func main() { // Select values >= lowerBound and values <= upperBound. // Loop through the values, in reverse order: - if ch, ok := sm.BoundedIterCh(reversed, lowerBound, upperBound); ok { - for rec := range ch { - fmt.Printf("%+v\n", rec) - } - } else { - fmt.Println("No values found that were equal to or within the given bounds.") + iterCh, err := sm.BoundedIterCh(reversed, lowerBound, upperBound) + if err != nil { + fmt.Println(err) + } + defer iterCh.Close() + + for rec := range iterCh.Records() { + fmt.Printf("%+v\n", rec) } } ``` diff --git a/asc/error.go b/asc/errors.go similarity index 100% rename from asc/error.go rename to asc/errors.go diff --git a/delete.go b/delete.go index 586e75e..a6ce7e3 100644 --- a/delete.go +++ b/delete.go @@ -1,6 +1,9 @@ package sortedmap -import "sort" +import ( + "errors" + "sort" +) func (sm *SortedMap) delete(key interface{}) bool { if val, ok := sm.idx[key]; ok { @@ -27,16 +30,16 @@ func (sm *SortedMap) delete(key interface{}) bool { return false } -func (sm *SortedMap) boundedDelete(lowerBound, upperBound interface{}) bool { +func (sm *SortedMap) boundedDelete(lowerBound, upperBound interface{}) error { iterBounds := sm.boundsIdxSearch(lowerBound, upperBound) if iterBounds == nil { - return false + return errors.New(noValuesErr) } for i := iterBounds[0]; i <= iterBounds[1]-i; i++ { delete(sm.idx, sm.sorted[i]) sm.sorted = deleteInterface(sm.sorted, i) } - return true + return nil } // Delete removes a value from the collection, using the given key. @@ -56,6 +59,6 @@ func (sm *SortedMap) BatchDelete(keys []interface{}) []bool { // BoundedDelete removes values that are between the given values from the collection. // BoundedDelete returns true if the operation was successful, or false otherwise. -func (sm *SortedMap) BoundedDelete(lowerBound, upperBound interface{}) bool { +func (sm *SortedMap) BoundedDelete(lowerBound, upperBound interface{}) error { return sm.boundedDelete(lowerBound, upperBound) } diff --git a/delete_test.go b/delete_test.go index 48b668c..65276fa 100644 --- a/delete_test.go +++ b/delete_test.go @@ -10,13 +10,22 @@ func TestDelete(t *testing.T) { if err != nil { t.Fatal(err) } + if sm.Delete("") { t.Fatalf("Delete: %v", invalidDelete) } + for _, rec := range records { sm.Delete(rec.Key) } - if err := verifyRecords(sm.IterCh(), false); err != nil { + + iterCh := sm.IterCh() + if err != nil { + t.Fatal(err) + } + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { t.Fatal(err) } } @@ -40,7 +49,11 @@ func TestBatchDelete(t *testing.T) { t.Fatalf("BatchDelete: %v", invalidDelete) } } - if err := verifyRecords(sm.IterCh(), false); err != nil { + + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { t.Fatal(err) } } @@ -58,39 +71,63 @@ func TestBoundedDelete(t *testing.T) { earlierDate := time.Date(200, 1, 1, 0, 0, 0, 0, time.UTC) - if !sm.BoundedDelete(nil, nil) { - t.Fatalf("TestBoundedDelete failed: %v", generalBoundsErr) + if err := sm.BoundedDelete(nil, nil); err != nil { + t.Fatal(err) } - if !sm.BoundedDelete(nil, time.Now()) { - t.Fatalf("TestBoundedDelete failed: %v", generalBoundsErr) + if err := sm.BoundedDelete(nil, time.Now()); err != nil { + t.Fatal(err) } - if !sm.BoundedDelete(time.Now(), nil) { - t.Fatalf("TestBoundedDelete failed: %v", generalBoundsErr) - } - if err := verifyRecords(sm.IterCh(), false); err != nil { + if err := sm.BoundedDelete(time.Now(), nil); err != nil { t.Fatal(err) } - if !sm.BoundedDelete(earlierDate, time.Now()) { - t.Fatalf("TestBoundedDelete failed: %v", generalBoundsErr) - } - if err := verifyRecords(sm.IterCh(), false); err != nil { + func() { + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { + t.Fatal(err) + } + }() + + if err := sm.BoundedDelete(earlierDate, time.Now()); err != nil { t.Fatal(err) } - if !sm.BoundedDelete(time.Now(), earlierDate) { - t.Fatalf("TestBoundedDelete failed: %v", generalBoundsErr) - } - if err := verifyRecords(sm.IterCh(), false); err != nil { + func() { + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { + t.Fatal(err) + } + }() + + if err := sm.BoundedDelete(time.Now(), earlierDate); err != nil { t.Fatal(err) } - if sm.BoundedDelete(earlierDate, earlierDate) { - t.Fatalf("TestBoundedDelete failed: %v", generalBoundsErr) - } - if err := verifyRecords(sm.IterCh(), false); err != nil { + func() { + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { + t.Fatal(err) + } + }() + + if err := sm.BoundedDelete(earlierDate, earlierDate); err == nil { t.Fatal(err) } + + func() { + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { + t.Fatal(err) + } + }() } diff --git a/desc/error.go b/desc/errors.go similarity index 100% rename from desc/error.go rename to desc/errors.go diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..600d28e --- /dev/null +++ b/errors.go @@ -0,0 +1,3 @@ +package sortedmap + +const noValuesErr = "No values found that were equal to or within the given bounds." diff --git a/examples/README.md b/examples/README.md index 2293744..7d34649 100644 --- a/examples/README.md +++ b/examples/README.md @@ -114,8 +114,6 @@ SortedMap supports three specific ways of processing iterable data: ```IterCh``` is a simple way of iterating over the entire set, in order. -Be aware that the writing goroutine cannot exit until it finishes sending all of its records. To get around this default behavior, please use ```CustomIterCh``` and set a timeout. - ```go package main @@ -138,7 +136,10 @@ func main() { // BatchInsert the example records: sm.BatchInsert(records) - for rec := range sm.IterCh() { + iterCh := sm.IterCh() + defer iterCh.Close() + + for rec := range iterCh.Records() { fmt.Printf("%+v\n", rec) } } @@ -170,10 +171,14 @@ func main() { // BatchInsert the example records: sm.BatchInsert(records) - if ch, ok := sm.BoundedIterCh(false, time.Time{}, time.Now()); ok { - for rec := range ch { - fmt.Printf("%+v\n", rec) - } + iterCh, err := sm.BoundedIterCh(false, time.Time{}, time.Now()) + if err != nil { + fmt.Println(err) + } + defer iterCh.Close() + + for rec := range iterCh.Records() { + fmt.Printf("%+v\n", rec) } } ``` @@ -211,10 +216,14 @@ func main() { Reversed: true, } - if ch, ok := sm.CustomIterCh(params); ok { - for rec := range ch { - fmt.Printf("%+v\n", rec) - } + iterCh, err := sm.CustomIterCh(false, time.Time{}, time.Now()) + if err != nil { + fmt.Println(err) + } + defer iterCh.Close() + + for rec := range iterCh.Records() { + fmt.Printf("%+v\n", rec) } } ``` @@ -385,8 +394,8 @@ func main() { sm.BatchInsert(records) // Delete values equal to or within the given bound values. - if !sm.BoundedDelete(time.Time{}, time.Now()) { - fmt.Println("No values fall within the specified bounds.") + if err := sm.BoundedDelete(time.Time{}, time.Now()); err != nil { + fmt.Println(err) } } ``` diff --git a/get_test.go b/get_test.go index f9fda83..be1888f 100644 --- a/get_test.go +++ b/get_test.go @@ -7,12 +7,17 @@ func TestGet(t *testing.T) { if err != nil { t.Fatal(err) } + for i := range records { if val, ok := sm.Get(records[i].Key); val == nil || !ok { t.Fatalf("TestGet failed: %v", notFoundErr) } } - if err := verifyRecords(sm.IterCh(), false); err != nil { + + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { t.Fatal(err) } } @@ -22,13 +27,18 @@ func TestBatchGet(t *testing.T) { if err != nil { t.Fatal(err) } + values, results := sm.BatchGet(keys) for i, ok := range results { if values[i] == nil || !ok { t.Fatalf("TestBatchGet failed: %v", notFoundErr) } } - if err := verifyRecords(sm.IterCh(), false); err != nil { + + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { t.Fatal(err) } } diff --git a/has_test.go b/has_test.go index b598617..4606af0 100644 --- a/has_test.go +++ b/has_test.go @@ -7,12 +7,17 @@ func TestHas(t *testing.T) { if err != nil { t.Fatal(err) } + for i := range records { if !sm.Has(records[i].Key) { t.Fatalf("TestHas failed: %v", notFoundErr) } } - if err := verifyRecords(sm.IterCh(), false); err != nil { + + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { t.Fatal(err) } } @@ -22,12 +27,17 @@ func TestBatchHas(t *testing.T) { if err != nil { t.Fatal(err) } + for _, ok := range sm.BatchHas(keys) { if !ok { t.Fatalf("TestBatchHas failed: %v", notFoundErr) } } - if err := verifyRecords(sm.IterCh(), false); err != nil { + + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { t.Fatal(err) } } diff --git a/insert.go b/insert.go index 9a1c24f..8ac8c5d 100644 --- a/insert.go +++ b/insert.go @@ -1,6 +1,9 @@ package sortedmap -import "fmt" +import ( + "errors" + "fmt" +) func (sm *SortedMap) insert(key, val interface{}) bool { if _, ok := sm.idx[key]; !ok { @@ -59,6 +62,6 @@ func (sm *SortedMap) BatchInsertMap(v interface{}) error { return sm.batchInsertMapStringKeys(m) default: - return fmt.Errorf("%s", unsupportedTypeErr) + return errors.New(unsupportedTypeErr) } } diff --git a/insert_test.go b/insert_test.go index e691bab..b6f14c5 100644 --- a/insert_test.go +++ b/insert_test.go @@ -16,18 +16,30 @@ func TestInsert(t *testing.T) { t.Fatalf("Insert failed: %v", keyExistsErr) } } - if err := verifyRecords(sm.IterCh(), false); err != nil { - t.Fatal(err) - } + + func() { + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { + t.Fatal(err) + } + }() for i := range records { if sm.Insert(records[i].Key, records[i].Val) { t.Fatalf("Insert failed: %v", notFoundErr) } } - if err := verifyRecords(sm.IterCh(), false); err != nil { - t.Fatal(err) - } + + func() { + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { + t.Fatal(err) + } + }() } func TestBatchInsert(t *testing.T) { @@ -40,9 +52,15 @@ func TestBatchInsert(t *testing.T) { t.Fatalf("BatchInsert failed: %v", keyExistsErr) } } - if err := verifyRecords(sm.IterCh(), false); err != nil { - t.Fatal(err) - } + + func() { + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { + t.Fatal(err) + } + }() } func TestBatchInsertMapWithInterfaceKeys(t *testing.T) { diff --git a/iter.go b/iter.go index e0f204d..beedabf 100644 --- a/iter.go +++ b/iter.go @@ -1,6 +1,26 @@ package sortedmap -import "time" +import ( + "errors" + "time" +) + +type IterChCloser struct { + ch chan Record + canceled chan struct{} +} + +func (iterCh *IterChCloser) Close() error { + select { + case iterCh.canceled <- struct{}{}: + default: + } + return nil +} + +func (iterCh *IterChCloser) Records() <-chan Record { + return iterCh.ch +} // IterChParams contains configurable settings for CustomIterCh. // SendTimeout is disabled by default, though it should be set to allow @@ -38,55 +58,62 @@ func (sm *SortedMap) recordFromIdx(i int) Record { return rec } -func (sm *SortedMap) sendRecord(ch chan Record, sendTimeout time.Duration, i int) bool { +func (sm *SortedMap) sendRecord(iterCh IterChCloser, sendTimeout time.Duration, i int) bool { if sendTimeout <= time.Duration(0) { - ch <- sm.recordFromIdx(i) + iterCh.ch <- sm.recordFromIdx(i) return true } select { - case ch <- sm.recordFromIdx(i): + case <-iterCh.canceled: + return false + + case iterCh.ch <- sm.recordFromIdx(i): return true + case <-time.After(sendTimeout): return false } } -func (sm *SortedMap) iterCh(params IterChParams) (<-chan Record, bool) { +func (sm *SortedMap) iterCh(params IterChParams) (IterChCloser, error) { iterBounds := sm.boundsIdxSearch(params.LowerBound, params.UpperBound) if iterBounds == nil { - return nil, false + return IterChCloser{}, errors.New(noValuesErr) } - ch := make(chan Record, setBufSize(params.BufSize)) + iterCh := IterChCloser{ + ch: make(chan Record, setBufSize(params.BufSize)), + canceled: make(chan struct{}, 1), + } - go func(params IterChParams, ch chan Record) { + go func(params IterChParams, iterCh IterChCloser) { if params.Reversed { for i := iterBounds[1]; i >= iterBounds[0]; i-- { - if !sm.sendRecord(ch, params.SendTimeout, i) { + if !sm.sendRecord(iterCh, params.SendTimeout, i) { break } } } else { for i := iterBounds[0]; i <= iterBounds[1]; i++ { - if !sm.sendRecord(ch, params.SendTimeout, i) { + if !sm.sendRecord(iterCh, params.SendTimeout, i) { break } } } - close(ch) - }(params, ch) + close(iterCh.ch) + }(params, iterCh) - return ch, true + return iterCh, nil } -func (sm *SortedMap) iterFunc(reversed bool, lowerBound, upperBound interface{}, f IterCallbackFunc) bool { +func (sm *SortedMap) iterFunc(reversed bool, lowerBound, upperBound interface{}, f IterCallbackFunc) error { iterBounds := sm.boundsIdxSearch(lowerBound, upperBound) if iterBounds == nil { - return false + return errors.New(noValuesErr) } if reversed { @@ -103,21 +130,21 @@ func (sm *SortedMap) iterFunc(reversed bool, lowerBound, upperBound interface{}, } } - return true + return nil } // IterCh returns a channel that sorted records can be read from and processed. // This method defaults to the expected behavior of blocking until a read, with no timeout. -func (sm *SortedMap) IterCh() <-chan Record { - ch, _ := sm.iterCh(IterChParams{}) - return ch +func (sm *SortedMap) IterCh() IterChCloser { + iterCh, _ := sm.iterCh(IterChParams{}) + return iterCh } // BoundedIterCh returns a channel that sorted records can be read from and processed. // BoundedIterCh starts at the lower bound value and sends all values in the collection until reaching the upper bounds value. // Sort order is reversed if the reversed argument is set to true. // This method defaults to the expected behavior of blocking until a channel send completes, with no timeout. -func (sm *SortedMap) BoundedIterCh(reversed bool, lowerBound, upperBound interface{}) (<-chan Record, bool) { +func (sm *SortedMap) BoundedIterCh(reversed bool, lowerBound, upperBound interface{}) (IterChCloser, error) { return sm.iterCh(IterChParams{ Reversed: reversed, LowerBound: lowerBound, @@ -129,7 +156,7 @@ func (sm *SortedMap) BoundedIterCh(reversed bool, lowerBound, upperBound interfa // CustomIterCh starts at the lower bound value and sends all values in the collection until reaching the upper bounds value. // Sort order is reversed if the reversed argument is set to true. // This method defaults to the expected behavior of blocking until a channel send completes, with no timeout. -func (sm *SortedMap) CustomIterCh(params IterChParams) (<-chan Record, bool) { +func (sm *SortedMap) CustomIterCh(params IterChParams) (IterChCloser, error) { return sm.iterCh(params) } @@ -141,6 +168,6 @@ func (sm *SortedMap) IterFunc(reversed bool, f IterCallbackFunc) { // BoundedIterFunc starts at the lower bound value and passes all values in the collection to the callback function until reaching the upper bounds value. // Sort order is reversed if the reversed argument is set to true. -func (sm *SortedMap) BoundedIterFunc(reversed bool, lowerBound, upperBound interface{}, f IterCallbackFunc) bool { +func (sm *SortedMap) BoundedIterFunc(reversed bool, lowerBound, upperBound interface{}, f IterCallbackFunc) error { return sm.iterFunc(reversed, lowerBound, upperBound, f) } diff --git a/iter_test.go b/iter_test.go index 3c812aa..baac428 100644 --- a/iter_test.go +++ b/iter_test.go @@ -33,31 +33,41 @@ func TestIterChTimeout(t *testing.T) { SendTimeout: timeout, } - if ch, ok := sm.CustomIterCh(params); ok { - for i := 0; i < 5; i++ { - time.Sleep(sleepDur) - rec := <-ch - if i > 1 && rec.Key != nil { - t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key) - } - } + ch, err := sm.CustomIterCh(params) + if err != nil { + t.Fatal(err) } else { - t.Fatalf("TestIterChTimeout failed: %v", generalBoundsErr) + func() { + defer ch.Close() + + for i := 0; i < 5; i++ { + time.Sleep(sleepDur) + rec := <-ch.Records() + if i > 1 && rec.Key != nil { + t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key) + } + } + }() } params.LowerBound = time.Time{} params.UpperBound = maxTime - if ch, ok := sm.CustomIterCh(params); ok { - for i := 0; i < 5; i++ { - time.Sleep(sleepDur) - rec := <-ch - if i > 1 && rec.Key != nil { - t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key) - } - } + ch, err = sm.CustomIterCh(params) + if err != nil { + t.Fatal(err) } else { - t.Fatalf("TestIterChTimeout failed: %v", generalBoundsErr) + func() { + defer ch.Close() + + for i := 0; i < 5; i++ { + time.Sleep(sleepDur) + rec := <-ch.Records() + if i > 1 && rec.Key != nil { + t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key) + } + } + }() } } @@ -74,31 +84,41 @@ func TestReversedIterChTimeout(t *testing.T) { SendTimeout: timeout, } - if ch, ok := sm.CustomIterCh(params); ok { - for i := 0; i < 5; i++ { - time.Sleep(sleepDur) - rec := <-ch - if i > 1 && rec.Key != nil { - t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key) - } - } + ch, err := sm.CustomIterCh(params) + if err != nil { + t.Fatal(err) } else { - t.Fatalf("TestIterChTimeout failed: %v", generalBoundsErr) + func() { + defer ch.Close() + + for i := 0; i < 5; i++ { + time.Sleep(sleepDur) + rec := <-ch.Records() + if i > 1 && rec.Key != nil { + t.Fatalf("TestReversedIterChTimeout failed: %v: %v", nonNilValErr, rec.Key) + } + } + }() } params.LowerBound = time.Time{} params.UpperBound = maxTime - if ch, ok := sm.CustomIterCh(params); ok { - for i := 0; i < 5; i++ { - time.Sleep(sleepDur) - rec := <-ch - if i > 1 && rec.Key != nil { - t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key) - } - } + ch, err = sm.CustomIterCh(params) + if err != nil { + t.Fatal(err) } else { - t.Fatalf("TestIterChTimeout failed: %v", generalBoundsErr) + func() { + defer ch.Close() + + for i := 0; i < 5; i++ { + time.Sleep(sleepDur) + rec := <-ch.Records() + if i > 1 && rec.Key != nil { + t.Fatalf("TestReversedIterChTimeout failed: %v: %v", nonNilValErr, rec.Key) + } + } + }() } } @@ -112,40 +132,60 @@ func TestBoundedIterCh(t *testing.T) { earlierDate := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) laterDate := time.Date(5321, 1, 1, 0, 0, 0, 0, time.UTC) - if ch, ok := sm.BoundedIterCh(reversed, nil, nil); ok { - if err := verifyRecords(ch, reversed); err != nil { - t.Fatal(err) - } + ch, err := sm.BoundedIterCh(reversed, nil, nil) + if err != nil { + t.Fatal(err) } else { - t.Fatalf("TestBoundedIterCh failed: %v", generalBoundsErr) + func() { + defer ch.Close() + + if err := verifyRecords(ch.Records(), reversed); err != nil { + t.Fatal(err) + } + }() } - if ch, ok := sm.BoundedIterCh(reversed, time.Time{}, maxTime); ok { - if err := verifyRecords(ch, reversed); err != nil { - t.Fatal(err) - } + ch, err = sm.BoundedIterCh(reversed, time.Time{}, maxTime) + if err != nil { + t.Fatal(err) } else { - t.Fatalf("TestBoundedIterCh failed: %v", generalBoundsErr) + func() { + defer ch.Close() + + if err := verifyRecords(ch.Records(), reversed); err != nil { + t.Fatal(err) + } + }() } - if ch, ok := sm.BoundedIterCh(reversed, earlierDate, time.Now()); ok { - if err := verifyRecords(ch, reversed); err != nil { - t.Fatal(err) - } + ch, err = sm.BoundedIterCh(reversed, earlierDate, time.Now()) + if err != nil { + t.Fatal(err) } else { - t.Fatalf("TestBoundedIterCh failed: %v", generalBoundsErr) + func() { + defer ch.Close() + + if err := verifyRecords(ch.Records(), reversed); err != nil { + t.Fatal(err) + } + }() } - if ch, ok := sm.BoundedIterCh(reversed, time.Now(), laterDate); ok { - if err := verifyRecords(ch, reversed); err != nil { - t.Fatal(err) - } + ch, err = sm.BoundedIterCh(reversed, time.Now(), laterDate) + if err != nil { + t.Fatal(err) } else { - t.Fatalf("TestBoundedIterCh failed: %v", generalBoundsErr) + func() { + defer ch.Close() + + if err := verifyRecords(ch.Records(), reversed); err != nil { + t.Fatal(err) + } + }() } - if _, ok := sm.BoundedIterCh(reversed, laterDate, laterDate); ok { - t.Fatalf("TestBoundedIterCh failed: %v", generalBoundsErr) + if _, err := sm.BoundedIterCh(reversed, laterDate, laterDate); err == nil { + t.Fatalf("TestBoundedIterCh failed: %v", "equal bounds values were accepted error") } } @@ -170,13 +210,17 @@ func TestCustomIterCh(t *testing.T) { BufSize: buffSize, } - if ch, ok := sm.CustomIterCh(params); ok { - if err := verifyRecords(ch, params.Reversed); err != nil { + func() { + ch, err := sm.CustomIterCh(params) + if err != nil { t.Fatal(err) } - } else { - t.Fatalf("TestCustomIterCh failed: %v", generalBoundsErr) - } + defer ch.Close() + + if err := verifyRecords(ch.Records(), params.Reversed); err != nil { + t.Fatal(err) + } + }() params = IterChParams{ Reversed: reversed, @@ -185,13 +229,17 @@ func TestCustomIterCh(t *testing.T) { UpperBound: laterDate, } - if ch, ok := sm.CustomIterCh(params); ok { - if err := verifyRecords(ch, params.Reversed); err != nil { + func() { + ch, err := sm.CustomIterCh(params) + if err != nil { t.Fatal(err) } - } else { - t.Fatalf("TestCustomIterCh failed: %v", generalBoundsErr) - } + defer ch.Close() + + if err := verifyRecords(ch.Records(), params.Reversed); err != nil { + t.Fatal(err) + } + }() params = IterChParams{ Reversed: reversed, @@ -200,13 +248,17 @@ func TestCustomIterCh(t *testing.T) { UpperBound: earlierDate, } - if ch, ok := sm.CustomIterCh(params); ok { - if err := verifyRecords(ch, params.Reversed); err != nil { + func() { + ch, err := sm.CustomIterCh(params) + if err != nil { t.Fatal(err) } - } else { - t.Fatalf("TestCustomIterCh failed: %v", generalBoundsErr) - } + defer ch.Close() + + if err := verifyRecords(ch.Records(), params.Reversed); err != nil { + t.Fatal(err) + } + }() reversed = false params = IterChParams{ @@ -216,13 +268,17 @@ func TestCustomIterCh(t *testing.T) { UpperBound: earlierDate, } - if ch, ok := sm.CustomIterCh(params); ok { - if err := verifyRecords(ch, params.Reversed); err != nil { + func() { + ch, err := sm.CustomIterCh(params) + if err != nil { t.Fatal(err) } - } else { - t.Fatalf("TestCustomIterCh failed: %v", generalBoundsErr) - } + defer ch.Close() + + if err := verifyRecords(ch.Records(), params.Reversed); err != nil { + t.Fatal(err) + } + }() } func TestIterFunc(t *testing.T) { @@ -269,49 +325,40 @@ func TestBoundedIterFunc(t *testing.T) { earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) laterDate := time.Now() - if ok := sm.BoundedIterFunc(false, nil, nil, func(rec Record) bool { + if err := sm.BoundedIterFunc(false, nil, nil, func(rec Record) bool { if rec.Key == nil { t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) } return false - }); !ok { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + }); err != nil { + t.Fatalf("TestBoundedIterFunc failed: %v", err) } - if ok := sm.BoundedIterFunc(false, nil, laterDate, func(rec Record) bool { + if err := sm.BoundedIterFunc(false, nil, laterDate, func(rec Record) bool { if rec.Key == nil { t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) } return false - }); !ok { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + }); err != nil { + t.Fatalf("TestBoundedIterFunc failed: %v", err) } - if ok := sm.BoundedIterFunc(false, laterDate, nil, func(rec Record) bool { + if err := sm.BoundedIterFunc(false, laterDate, nil, func(rec Record) bool { if rec.Key == nil { t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) } return false - }); !ok { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + }); err != nil { + t.Fatalf("TestBoundedIterFunc failed: %v", err) } - if ok := sm.BoundedIterFunc(false, earlierDate, laterDate, func(rec Record) bool { + if err := sm.BoundedIterFunc(false, earlierDate, laterDate, func(rec Record) bool { if rec.Key == nil { t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) } return false - }); !ok { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) - } - - if ok := sm.BoundedIterFunc(false, laterDate, laterDate, func(rec Record) bool { - if rec.Key == nil { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) - } - return false - }); ok { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + }); err != nil { + t.Fatalf("TestBoundedIterFunc failed: %v", err) } } @@ -324,39 +371,39 @@ func TestReversedBoundedIterFunc(t *testing.T) { earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) laterDate := time.Now() - if ok := sm.BoundedIterFunc(true, nil, nil, func(rec Record) bool { + if err := sm.BoundedIterFunc(true, nil, nil, func(rec Record) bool { if rec.Key == nil { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + t.Fatalf("TestReversedBoundedIterFunc failed: %v", nilValErr) } return false - }); !ok { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + }); err != nil { + t.Fatalf("TestReversedBoundedIterFunc failed: %v", err) } - if ok := sm.BoundedIterFunc(true, nil, laterDate, func(rec Record) bool { + if err := sm.BoundedIterFunc(true, nil, laterDate, func(rec Record) bool { if rec.Key == nil { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + t.Fatalf("TestReversedBoundedIterFunc failed: %v", nilValErr) } return false - }); !ok { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + }); err != nil { + t.Fatalf("TestReversedBoundedIterFunc failed: %v", err) } - if ok := sm.BoundedIterFunc(true, laterDate, nil, func(rec Record) bool { + if err := sm.BoundedIterFunc(true, laterDate, nil, func(rec Record) bool { if rec.Key == nil { t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) } return false - }); !ok { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + }); err != nil { + t.Fatalf("TestReversedBoundedIterFunc failed: %v", err) } - if ok := sm.BoundedIterFunc(true, earlierDate, laterDate, func(rec Record) bool { + if err := sm.BoundedIterFunc(true, earlierDate, laterDate, func(rec Record) bool { if rec.Key == nil { t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) } return false - }); !ok { - t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr) + }); err != nil { + t.Fatalf("TestReversedBoundedIterFunc failed: %v", err) } } diff --git a/keys.go b/keys.go index f8de44e..029cc03 100644 --- a/keys.go +++ b/keys.go @@ -1,11 +1,13 @@ package sortedmap -func (sm *SortedMap) keys(lowerBound, upperBound interface{}) ([]interface{}, bool) { +import "errors" + +func (sm *SortedMap) keys(lowerBound, upperBound interface{}) ([]interface{}, error) { idxBounds := sm.boundsIdxSearch(lowerBound, upperBound) if idxBounds == nil { - return nil, false + return nil, errors.New(noValuesErr) } - return sm.sorted[idxBounds[0] : idxBounds[1]+1], true + return sm.sorted[idxBounds[0] : idxBounds[1]+1], nil } // Keys returns a slice containing sorted keys. @@ -17,6 +19,6 @@ func (sm *SortedMap) Keys() []interface{} { // BoundedKeys returns a slice containing sorted keys equal to or between the given bounds. // The returned slice is valid until the next modification to the SortedMap structure. -func (sm *SortedMap) BoundedKeys(lowerBound, upperBound interface{}) ([]interface{}, bool) { +func (sm *SortedMap) BoundedKeys(lowerBound, upperBound interface{}) ([]interface{}, error) { return sm.keys(lowerBound, upperBound) } diff --git a/keys_test.go b/keys_test.go index aa1a815..a0b6910 100644 --- a/keys_test.go +++ b/keys_test.go @@ -29,9 +29,9 @@ func TestBoundedKeys(t *testing.T) { t.Fatal(err) } i := 0 - keys, ok := sm.BoundedKeys(time.Time{}, time.Now()) - if !ok { - t.Fatal("No values fall between or are equal to the given bounds.") + keys, err := sm.BoundedKeys(time.Time{}, time.Now()) + if err != nil { + t.Fatal(err) } for _, key := range keys { if key == nil { @@ -43,13 +43,3 @@ func TestBoundedKeys(t *testing.T) { t.Fatal("The returned slice was empty.") } } - -func TestBoundedKeysWithNoBoundsReturned(t *testing.T) { - sm, _, err := newSortedMapFromRandRecords(300) - if err != nil { - t.Fatal(err) - } - if val, ok := sm.BoundedKeys(time.Now().Add(-1*time.Microsecond), time.Now()); ok { - t.Fatalf("Values fall between or are equal to the given bounds when it should not have returned bounds: %+v", sm.idx[val[0]]) - } -} diff --git a/replace.go b/replace.go index 9b49e0c..8a2567a 100644 --- a/replace.go +++ b/replace.go @@ -1,6 +1,6 @@ package sortedmap -import "fmt" +import "errors" func (sm *SortedMap) replace(key, val interface{}) { sm.delete(key) @@ -51,6 +51,6 @@ func (sm *SortedMap) BatchReplaceMap(v interface{}) error { return nil default: - return fmt.Errorf("%s", unsupportedTypeErr) + return errors.New(unsupportedTypeErr) } } diff --git a/replace_test.go b/replace_test.go index 9d005f7..a805d4a 100644 --- a/replace_test.go +++ b/replace_test.go @@ -16,7 +16,10 @@ func TestReplace(t *testing.T) { } } - if err := verifyRecords(sm.IterCh(), false); err != nil { + iterCh := sm.IterCh() + defer iterCh.Close() + + if err := verifyRecords(iterCh.Records(), false); err != nil { t.Fatal(err) } } diff --git a/testing_utils_test.go b/testing_utils_test.go index 9941129..271ab3f 100644 --- a/testing_utils_test.go +++ b/testing_utils_test.go @@ -83,7 +83,10 @@ func newSortedMapFromRandRecords(n int) (*SortedMap, []Record, error) { sm := New(0, asc.Time) sm.BatchReplace(records) - return sm, records, verifyRecords(sm.IterCh(), false) + iterCh := sm.IterCh() + defer iterCh.Close() + + return sm, records, verifyRecords(iterCh.Records(), false) } func newRandSortedMapWithKeys(n int) (*SortedMap, []Record, []interface{}, error) {