From 57366d4a7cf513a4bf53976f3ee779cd3bed985c Mon Sep 17 00:00:00 2001 From: Zhiburt Date: Sun, 5 May 2019 12:46:02 +0300 Subject: [PATCH] Send func (#88) Fixes #84 --- README.md | 1 + functions/main.go | 1 + functions/send.go | 24 ++++++ pie/carpointers_pie.go | 20 +++++ pie/carpointers_test.go | 50 +++++++++++ pie/cars_pie.go | 20 +++++ pie/cars_test.go | 50 +++++++++++ pie/float64s_pie.go | 20 +++++ pie/float64s_test.go | 50 +++++++++++ pie/ints_pie.go | 20 +++++ pie/ints_test.go | 50 +++++++++++ pie/main_test.go | 181 +++++++++++++++++++++++++++++++++++++++- pie/strings_pie.go | 20 +++++ pie/strings_test.go | 50 +++++++++++ template.go | 25 ++++++ 15 files changed, 580 insertions(+), 2 deletions(-) create mode 100644 functions/send.go diff --git a/README.md b/README.md index f9e146b..584755a 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ This will only generate `myInts.Average`, `myInts.Sum` and `myStrings.Select`. | `Min` | ✓ | ✓ | | | n | The minimum value, or a zeroed value. | | `Random` | ✓ | ✓ | ✓ | | 1 | Select a random element, or a zeroed value if empty. | | `Reverse` | ✓ | ✓ | ✓ | | n | Reverse elements. | +| `Send` | ✓ | ✓ | ✓ | | n | Send all element to channel. | | `Select` | ✓ | ✓ | ✓ | | n | A new slice containing only the elements that returned true from the condition. | | `Sort` | ✓ | ✓ | | | n⋅log(n) | Return a new sorted slice. | | `Sum` | | ✓ | | | n | Sum (total) of all elements. | diff --git a/functions/main.go b/functions/main.go index 41c70d2..234aa20 100644 --- a/functions/main.go +++ b/functions/main.go @@ -42,6 +42,7 @@ var Functions = []struct { {"Min", "min.go", ForNumbersAndStrings}, {"Random", "random.go", ForAll}, {"Reverse", "reverse.go", ForAll}, + {"Send", "send.go", ForAll}, {"Select", "select.go", ForAll}, {"Sort", "sort.go", ForNumbersAndStrings}, {"Sum", "sum.go", ForNumbers}, diff --git a/functions/send.go b/functions/send.go new file mode 100644 index 0000000..a64033a --- /dev/null +++ b/functions/send.go @@ -0,0 +1,24 @@ +package functions + +import ( + "context" +) + +// Send sends elements to channel +// in normal act it sends all elements but if func canceled it can be less +// +// it locks execution of gorutine +// it doesn't close channel after work +// returns sended elements if len(this) != len(old) considered func was canceled +func (ss SliceType) Send(ctx context.Context, ch chan<- ElementType) SliceType { + for i, s := range ss { + select { + case <-ctx.Done(): + return ss[:i] + default: + ch <- s + } + } + + return ss +} diff --git a/pie/carpointers_pie.go b/pie/carpointers_pie.go index cf2905e..17d5222 100755 --- a/pie/carpointers_pie.go +++ b/pie/carpointers_pie.go @@ -1,6 +1,7 @@ package pie import ( + "context" "encoding/json" "github.com/elliotchance/pie/pie/util" "math/rand" @@ -194,6 +195,25 @@ func (ss carPointers) Reverse() carPointers { return sorted } +// Send sends elements to channel +// in normal act it sends all elements but if func canceled it can be less +// +// it locks execution of gorutine +// it doesn't close channel after work +// returns sended elements if len(this) != len(old) considered func was canceled +func (ss carPointers) Send(ctx context.Context, ch chan<- *car) carPointers { + for i, s := range ss { + select { + case <-ctx.Done(): + return ss[:i] + default: + ch <- s + } + } + + return ss +} + // Select will return a new slice containing only the elements that return // true from the condition. The returned slice may contain zero elements (nil). // diff --git a/pie/carpointers_test.go b/pie/carpointers_test.go index 28518d2..362ace4 100644 --- a/pie/carpointers_test.go +++ b/pie/carpointers_test.go @@ -5,6 +5,7 @@ import ( "math/rand" "strings" "testing" + "time" "github.com/elliotchance/testify-stats/assert" ) @@ -628,3 +629,52 @@ func TestCarPointers_Random(t *testing.T) { }) } } + +var carPointersSendTests = []struct { + ss carPointers + recieveDelay time.Duration + canceledDelay time.Duration + expected carPointers +}{ + { + nil, + 0, + 0, + nil, + }, + { + carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}, + 0, + 0, + carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}, + }, + { + carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}, + time.Millisecond * 30, + time.Millisecond * 10, + carPointers{&car{"bar", "yellow"}}, + }, + { + carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}, + time.Millisecond * 3, + time.Millisecond * 10, + carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}, + }, +} + +func TestCarPointers_Send(t *testing.T) { + for _, test := range carPointersSendTests { + t.Run("", func(t *testing.T) { + defer assertImmutableCarPointers(t, &test.ss)() + ch := make(chan *car) + actual := getCarPointersFromChan(ch, test.recieveDelay) + ctx := createContextByDelay(test.canceledDelay) + + actualSended := test.ss.Send(ctx, ch) + close(ch) + + assert.Equal(t, test.expected, actualSended) + assert.Equal(t, test.expected, actual()) + }) + } +} diff --git a/pie/cars_pie.go b/pie/cars_pie.go index dd9ce24..a9978a4 100755 --- a/pie/cars_pie.go +++ b/pie/cars_pie.go @@ -1,6 +1,7 @@ package pie import ( + "context" "encoding/json" "github.com/elliotchance/pie/pie/util" "math/rand" @@ -194,6 +195,25 @@ func (ss cars) Reverse() cars { return sorted } +// Send sends elements to channel +// in normal act it sends all elements but if func canceled it can be less +// +// it locks execution of gorutine +// it doesn't close channel after work +// returns sended elements if len(this) != len(old) considered func was canceled +func (ss cars) Send(ctx context.Context, ch chan<- car) cars { + for i, s := range ss { + select { + case <-ctx.Done(): + return ss[:i] + default: + ch <- s + } + } + + return ss +} + // Select will return a new slice containing only the elements that return // true from the condition. The returned slice may contain zero elements (nil). // diff --git a/pie/cars_test.go b/pie/cars_test.go index d278179..01be965 100644 --- a/pie/cars_test.go +++ b/pie/cars_test.go @@ -5,6 +5,7 @@ import ( "math/rand" "strings" "testing" + "time" "github.com/elliotchance/testify-stats/assert" ) @@ -612,3 +613,52 @@ func TestCars_Random(t *testing.T) { }) } } + +var carsSendTests = []struct { + ss cars + recieveDelay time.Duration + canceledDelay time.Duration + expected cars +}{ + { + nil, + 0, + 0, + nil, + }, + { + cars{car{"bar", "yellow"}, car{"Baz", "black"}}, + 0, + 0, + cars{car{"bar", "yellow"}, car{"Baz", "black"}}, + }, + { + cars{car{"bar", "yellow"}, car{"Baz", "black"}}, + time.Millisecond * 30, + time.Millisecond * 10, + cars{car{"bar", "yellow"}}, + }, + { + cars{car{"bar", "yellow"}, car{"Baz", "black"}}, + time.Millisecond * 3, + time.Millisecond * 10, + cars{car{"bar", "yellow"}, car{"Baz", "black"}}, + }, +} + +func TestCar_Send(t *testing.T) { + for _, test := range carsSendTests { + t.Run("", func(t *testing.T) { + defer assertImmutableCars(t, &test.ss)() + ch := make(chan car) + actual := getCarsFromChan(ch, test.recieveDelay) + ctx := createContextByDelay(test.canceledDelay) + + actualSended := test.ss.Send(ctx, ch) + close(ch) + + assert.Equal(t, test.expected, actualSended) + assert.Equal(t, test.expected, actual()) + }) + } +} diff --git a/pie/float64s_pie.go b/pie/float64s_pie.go index 3de3ff3..479949e 100755 --- a/pie/float64s_pie.go +++ b/pie/float64s_pie.go @@ -1,6 +1,7 @@ package pie import ( + "context" "encoding/json" "github.com/elliotchance/pie/pie/util" "math" @@ -285,6 +286,25 @@ func (ss Float64s) Reverse() Float64s { return sorted } +// Send sends elements to channel +// in normal act it sends all elements but if func canceled it can be less +// +// it locks execution of gorutine +// it doesn't close channel after work +// returns sended elements if len(this) != len(old) considered func was canceled +func (ss Float64s) Send(ctx context.Context, ch chan<- float64) Float64s { + for i, s := range ss { + select { + case <-ctx.Done(): + return ss[:i] + default: + ch <- s + } + } + + return ss +} + // Select will return a new slice containing only the elements that return // true from the condition. The returned slice may contain zero elements (nil). // diff --git a/pie/float64s_test.go b/pie/float64s_test.go index 9c853ca..aa183d3 100644 --- a/pie/float64s_test.go +++ b/pie/float64s_test.go @@ -4,6 +4,7 @@ import ( "fmt" "math/rand" "testing" + "time" "github.com/elliotchance/testify-stats/assert" ) @@ -757,3 +758,52 @@ func TestFloat64s_Abs(t *testing.T) { }) } } + +var float64sSendTests = []struct { + ss Float64s + recieveDelay time.Duration + canceledDelay time.Duration + expected Float64s +}{ + { + nil, + 0, + 0, + nil, + }, + { + Float64s{1.2, 3.2}, + 0, + 0, + Float64s{1.2, 3.2}, + }, + { + Float64s{1.2, 3.2}, + time.Millisecond * 30, + time.Millisecond * 10, + Float64s{1.2}, + }, + { + Float64s{1.2, 3.2}, + time.Millisecond * 3, + time.Millisecond * 10, + Float64s{1.2, 3.2}, + }, +} + +func TestFloat64s_Send(t *testing.T) { + for _, test := range float64sSendTests { + t.Run("", func(t *testing.T) { + defer assertImmutableFloat64s(t, &test.ss)() + ch := make(chan float64) + actual := getFloat64sFromChan(ch, test.recieveDelay) + ctx := createContextByDelay(test.canceledDelay) + + actualSended := test.ss.Send(ctx, ch) + close(ch) + + assert.Equal(t, test.expected, actualSended) + assert.Equal(t, test.expected, actual()) + }) + } +} diff --git a/pie/ints_pie.go b/pie/ints_pie.go index c4370dc..a0e49b7 100755 --- a/pie/ints_pie.go +++ b/pie/ints_pie.go @@ -1,6 +1,7 @@ package pie import ( + "context" "encoding/json" "github.com/elliotchance/pie/pie/util" "math" @@ -285,6 +286,25 @@ func (ss Ints) Reverse() Ints { return sorted } +// Send sends elements to channel +// in normal act it sends all elements but if func canceled it can be less +// +// it locks execution of gorutine +// it doesn't close channel after work +// returns sended elements if len(this) != len(old) considered func was canceled +func (ss Ints) Send(ctx context.Context, ch chan<- int) Ints { + for i, s := range ss { + select { + case <-ctx.Done(): + return ss[:i] + default: + ch <- s + } + } + + return ss +} + // Select will return a new slice containing only the elements that return // true from the condition. The returned slice may contain zero elements (nil). // diff --git a/pie/ints_test.go b/pie/ints_test.go index d67a91b..bb0957e 100644 --- a/pie/ints_test.go +++ b/pie/ints_test.go @@ -4,6 +4,7 @@ import ( "fmt" "math/rand" "testing" + "time" "github.com/elliotchance/testify-stats/assert" ) @@ -723,3 +724,52 @@ func TestInts_Abs(t *testing.T) { assert.Equal(t, Ints{689845, 688969, 220373, 89437, 308836}, Ints{-689845, -688969, -220373, -89437, 308836}.Abs()) assert.Equal(t, Ints{1, 2}, Ints{1, 2}.Abs()) } + +var intsSendTests = []struct { + ss Ints + recieveDelay time.Duration + canceledDelay time.Duration + expected Ints +}{ + { + nil, + 0, + 0, + nil, + }, + { + Ints{1, 3}, + 0, + 0, + Ints{1, 3}, + }, + { + Ints{1, 3}, + time.Millisecond * 30, + time.Millisecond * 10, + Ints{1}, + }, + { + Ints{1, 3}, + time.Millisecond * 3, + time.Millisecond * 10, + Ints{1, 3}, + }, +} + +func TestInts_Send(t *testing.T) { + for _, test := range intsSendTests { + t.Run("", func(t *testing.T) { + defer assertImmutableInts(t, &test.ss)() + ch := make(chan int) + actual := getIntsFromChan(ch, test.recieveDelay) + ctx := createContextByDelay(test.canceledDelay) + + actualSended := test.ss.Send(ctx, ch) + close(ch) + + assert.Equal(t, test.expected, actualSended) + assert.Equal(t, test.expected, actual()) + }) + } +} diff --git a/pie/main_test.go b/pie/main_test.go index 9130472..9fbfe96 100644 --- a/pie/main_test.go +++ b/pie/main_test.go @@ -1,10 +1,13 @@ package pie import ( - testify_stats "github.com/elliotchance/testify-stats" - "github.com/elliotchance/testify-stats/assert" + "context" "os" "testing" + "time" + + testify_stats "github.com/elliotchance/testify-stats" + "github.com/elliotchance/testify-stats/assert" ) func TestMain(m *testing.M) { @@ -60,3 +63,177 @@ func assertImmutableCarPointers(t *testing.T, ss *carPointers) func() { assert.True(t, before == after) } } + +func createContextByDelay(t time.Duration) context.Context { + ctx := context.Background() + if t > 0 { + ctx, _ = context.WithTimeout(ctx, t) + } + + return ctx +} + +func getCarPointersFromChan(ch chan *car, t time.Duration) func() carPointers { + done := make(chan struct{}) + var cars carPointers + if t > 0 { + go func() { + ticker := time.NewTicker(t) + defer ticker.Stop() + for range ticker.C { + val, ok := <-ch + if !ok { + break + } else { + cars = append(cars, val) + } + } + done <- struct{}{} + + }() + } else { + go func() { + for val := range ch { + cars = append(cars, val) + } + done <- struct{}{} + }() + } + + return func() carPointers { + <-done + return cars + } +} + +func getCarsFromChan(ch chan car, t time.Duration) func() cars { + done := make(chan struct{}) + var c cars + if t > 0 { + go func() { + ticker := time.NewTicker(t) + defer ticker.Stop() + for range ticker.C { + val, ok := <-ch + if !ok { + break + } else { + c = append(c, val) + } + } + done <- struct{}{} + + }() + } else { + go func() { + for val := range ch { + c = append(c, val) + } + done <- struct{}{} + }() + } + + return func() cars { + <-done + return c + } +} + +func getFloat64sFromChan(ch chan float64, t time.Duration) func() Float64s { + done := make(chan struct{}) + var c Float64s + if t > 0 { + go func() { + ticker := time.NewTicker(t) + defer ticker.Stop() + for range ticker.C { + val, ok := <-ch + if !ok { + break + } else { + c = append(c, val) + } + } + done <- struct{}{} + + }() + } else { + go func() { + for val := range ch { + c = append(c, val) + } + done <- struct{}{} + }() + } + + return func() Float64s { + <-done + return c + } +} + +func getIntsFromChan(ch chan int, t time.Duration) func() Ints { + done := make(chan struct{}) + var c Ints + if t > 0 { + go func() { + ticker := time.NewTicker(t) + defer ticker.Stop() + for range ticker.C { + val, ok := <-ch + if !ok { + break + } else { + c = append(c, val) + } + } + done <- struct{}{} + + }() + } else { + go func() { + for val := range ch { + c = append(c, val) + } + done <- struct{}{} + }() + } + + return func() Ints { + <-done + return c + } +} + +func getStringsFromChan(ch chan string, t time.Duration) func() Strings { + done := make(chan struct{}) + var c Strings + if t > 0 { + go func() { + ticker := time.NewTicker(t) + defer ticker.Stop() + for range ticker.C { + val, ok := <-ch + if !ok { + break + } else { + c = append(c, val) + } + } + done <- struct{}{} + + }() + } else { + go func() { + for val := range ch { + c = append(c, val) + } + done <- struct{}{} + }() + } + + return func() Strings { + <-done + return c + } +} diff --git a/pie/strings_pie.go b/pie/strings_pie.go index e55e2fd..c9ec05e 100755 --- a/pie/strings_pie.go +++ b/pie/strings_pie.go @@ -1,6 +1,7 @@ package pie import ( + "context" "encoding/json" "github.com/elliotchance/pie/pie/util" "math/rand" @@ -254,6 +255,25 @@ func (ss Strings) Reverse() Strings { return sorted } +// Send sends elements to channel +// in normal act it sends all elements but if func canceled it can be less +// +// it locks execution of gorutine +// it doesn't close channel after work +// returns sended elements if len(this) != len(old) considered func was canceled +func (ss Strings) Send(ctx context.Context, ch chan<- string) Strings { + for i, s := range ss { + select { + case <-ctx.Done(): + return ss[:i] + default: + ch <- s + } + } + + return ss +} + // Select will return a new slice containing only the elements that return // true from the condition. The returned slice may contain zero elements (nil). // diff --git a/pie/strings_test.go b/pie/strings_test.go index 39d0353..98580fb 100644 --- a/pie/strings_test.go +++ b/pie/strings_test.go @@ -5,6 +5,7 @@ import ( "math/rand" "strings" "testing" + "time" "github.com/elliotchance/testify-stats/assert" ) @@ -703,3 +704,52 @@ func TestStrings_Random(t *testing.T) { }) } } + +var stringsSendTests = []struct { + ss Strings + recieveDelay time.Duration + canceledDelay time.Duration + expected Strings +}{ + { + nil, + 0, + 0, + nil, + }, + { + Strings{"foo", "bar"}, + 0, + 0, + Strings{"foo", "bar"}, + }, + { + Strings{"foo", "bar"}, + time.Millisecond * 30, + time.Millisecond * 10, + Strings{"foo"}, + }, + { + Strings{"foo", "bar"}, + time.Millisecond * 3, + time.Millisecond * 10, + Strings{"foo", "bar"}, + }, +} + +func TestStrings_Send(t *testing.T) { + for _, test := range stringsSendTests { + t.Run("", func(t *testing.T) { + defer assertImmutableStrings(t, &test.ss)() + ch := make(chan string) + actual := getStringsFromChan(ch, test.recieveDelay) + ctx := createContextByDelay(test.canceledDelay) + + actualSended := test.ss.Send(ctx, ch) + close(ch) + + assert.Equal(t, test.expected, actualSended) + assert.Equal(t, test.expected, actual()) + }) + } +} diff --git a/template.go b/template.go index d7d878d..f3a835f 100644 --- a/template.go +++ b/template.go @@ -391,6 +391,31 @@ func (ss SliceType) Select(condition func(ElementType) bool) (ss2 SliceType) { return } +`, + "Send": `package functions + +import ( + "context" +) + +// Send sends elements to channel +// in normal act it sends all elements but if func canceled it can be less +// +// it locks execution of gorutine +// it doesn't close channel after work +// returns sended elements if len(this) != len(old) considered func was canceled +func (ss SliceType) Send(ctx context.Context, ch chan<- ElementType) SliceType { + for i, s := range ss { + select { + case <-ctx.Done(): + return ss[:i] + default: + ch <- s + } + } + + return ss +} `, "Shuffle": `package functions