Skip to content

Commit

Permalink
Remove package result, add group
Browse files Browse the repository at this point in the history
  • Loading branch information
eikemeier committed Mar 26, 2024
1 parent 01e6fb5 commit 57f770f
Show file tree
Hide file tree
Showing 21 changed files with 548 additions and 409 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: 🧸 golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: v1.56.2
version: v1.57.1
- name: 🔨 Test
run: go test -race ./...
env:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
!/.github/
!/.gitignore
!/.golangci.yaml
!/.markdownlint.json
!/.markdownlint.yaml
!/.mockery.yaml
!/.prettierrc.yaml
!/.yamlfmt
!/.yamllint
/bin/
Expand Down
11 changes: 0 additions & 11 deletions .markdownlint.json

This file was deleted.

7 changes: 7 additions & 0 deletions .markdownlint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
no-hard-tabs:
ignore_code_languages:
- go
spaces_per_tab: 4
line-length:
line_length: 120
9 changes: 9 additions & 0 deletions .prettierrc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
printWidth: 120
proseWrap: always
tabWidth: 4
useTabs: false
overrides:
- files: "*.md"
options:
tabWidth: 2
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![Maintainability](https://api.codeclimate.com/v1/badges/12a77c18122e2d1e1f6b/maintainability)](https://codeclimate.com/github/fillmore-labs/promise/maintainability)
[![Go Report Card](https://goreportcard.com/badge/fillmore-labs.com/promise)](https://goreportcard.com/report/fillmore-labs.com/promise)
[![License](https://img.shields.io/github/license/fillmore-labs/promise)](https://www.apache.org/licenses/LICENSE-2.0)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ffillmore-labs%2Fpromise.svg?type=shield&issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2Ffillmore-labs%2Fpromise?ref=badge_shield&issueType=license)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ffillmore-labs%2Fpromise.svg?type=shield&issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2Ffillmore-labs%2Fpromise)

The `promise` package provides interfaces and utilities for writing asynchronous code in Go.

Expand All @@ -16,19 +16,18 @@ The `promise` package provides interfaces and utilities for writing asynchronous
Promises and futures are constructs used for asynchronous and concurrent programming, allowing developers to work with
values that may not be immediately available and can be evaluated in a different execution context.

Go is known for its built-in concurrency features like goroutines and channels.
The select statement further allows for efficient multiplexing and synchronization of multiple channels, thereby
enabling developers to coordinate and orchestrate asynchronous operations effectively.
Additionally, the context package offers a standardized way to manage cancellation, deadlines, and timeouts within
concurrent and asynchronous code.
Go is known for its built-in concurrency features like goroutines and channels. The select statement further allows for
efficient multiplexing and synchronization of multiple channels, thereby enabling developers to coordinate and
orchestrate asynchronous operations effectively. Additionally, the context package offers a standardized way to manage
cancellation, deadlines, and timeouts within concurrent and asynchronous code.

On the other hand, Go's error handling mechanism, based on explicit error values returned from functions, provides a
clear and concise way to handle errors.

The purpose of this package is to provide a library which simplifies the integration of concurrent
code while providing a cohesive strategy for handling asynchronous errors.
By adhering to Go's standard conventions for asynchronous and concurrent code, as well as error propagation, this
package aims to enhance developer productivity and code reliability in scenarios requiring asynchronous operations.
The purpose of this package is to provide a library which simplifies the integration of concurrent code while providing
a cohesive strategy for handling asynchronous errors. By adhering to Go's standard conventions for asynchronous and
concurrent code, as well as error propagation, this package aims to enhance developer productivity and code reliability
in scenarios requiring asynchronous operations.

## Usage

Expand Down
44 changes: 19 additions & 25 deletions combine.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (
"context"
"fmt"
"reflect"

"fillmore-labs.com/promise/result"
)

// AnyFuture matches a [Future] of any type.
Expand All @@ -31,36 +29,32 @@ type AnyFuture interface {

// AwaitAll returns a function that yields the results of all futures.
// If the context is canceled, it returns an error for the remaining futures.
func AwaitAll[R any](ctx context.Context, futures ...Future[R]) func(yield func(int, result.Result[R]) bool) {
i := newIterator(ctx, convertValue[R], futures)

return i.yieldTo
func AwaitAll[R any](ctx context.Context, futures ...Future[R]) func(yield func(int, Result[R]) bool) {
return newIterator(ctx, convertValue[R], futures)
}

// AwaitAllAny returns a function that yields the results of all futures.
// If the context is canceled, it returns an error for the remaining futures.
func AwaitAllAny(ctx context.Context, futures ...AnyFuture) func(yield func(int, result.Result[any]) bool) {
i := newIterator(ctx, convertValueAny, futures)

return i.yieldTo
func AwaitAllAny(ctx context.Context, futures ...AnyFuture) func(yield func(int, Result[any]) bool) {
return newIterator(ctx, convertValueAny, futures)
}

// AwaitAllResults waits for all futures to complete and returns the results.
// If the context is canceled, it returns early with errors for the remaining futures.
func AwaitAllResults[R any](ctx context.Context, futures ...Future[R]) []result.Result[R] {
func AwaitAllResults[R any](ctx context.Context, futures ...Future[R]) []Result[R] {
return awaitAllResults(len(futures), AwaitAll(ctx, futures...))
}

// AwaitAllResultsAny waits for all futures to complete and returns the results.
// If the context is canceled, it returns early with errors for the remaining futures.
func AwaitAllResultsAny(ctx context.Context, futures ...AnyFuture) []result.Result[any] {
func AwaitAllResultsAny(ctx context.Context, futures ...AnyFuture) []Result[any] {
return awaitAllResults(len(futures), AwaitAllAny(ctx, futures...))
}

func awaitAllResults[R any](n int, iter func(yield func(int, result.Result[R]) bool)) []result.Result[R] {
results := make([]result.Result[R], n)
func awaitAllResults[R any](n int, iter func(yield func(int, Result[R]) bool)) []Result[R] {
results := make([]Result[R], n)

iter(func(i int, r result.Result[R]) bool {
iter(func(i int, r Result[R]) bool {
results[i] = r

return true
Expand All @@ -81,17 +75,17 @@ func AwaitAllValuesAny(ctx context.Context, futures ...AnyFuture) ([]any, error)
return awaitAllValues(len(futures), AwaitAllAny(ctx, futures...))
}

func awaitAllValues[R any](n int, iter func(yield func(int, result.Result[R]) bool)) ([]R, error) {
func awaitAllValues[R any](n int, iter func(yield func(int, Result[R]) bool)) ([]R, error) {
results := make([]R, n)
var yieldErr error

iter(func(i int, r result.Result[R]) bool {
if r.Err() != nil {
yieldErr = fmt.Errorf("list AwaitAllValues result %d: %w", i, r.Err())
iter(func(i int, r Result[R]) bool {
if r.Err != nil {
yieldErr = fmt.Errorf("list AwaitAllValues result %d: %w", i, r.Err)

return false
}
results[i] = r.Value()
results[i] = r.Value

return true
})
Expand All @@ -111,11 +105,11 @@ func AwaitFirstAny(ctx context.Context, futures ...AnyFuture) (any, error) {
return awaitFirst(AwaitAllAny(ctx, futures...))
}

func awaitFirst[R any](iter func(yield func(int, result.Result[R]) bool)) (R, error) {
var v result.Result[R]
func awaitFirst[R any](iter func(yield func(int, Result[R]) bool)) (R, error) {
var v *Result[R]

iter(func(_ int, r result.Result[R]) bool {
v = r
iter(func(_ int, r Result[R]) bool {
v = &r

return false
})
Expand All @@ -124,5 +118,5 @@ func awaitFirst[R any](iter func(yield func(int, result.Result[R]) bool)) (R, er
return *new(R), ErrNoResult
}

return v.V()
return v.Value, v.Err
}
25 changes: 11 additions & 14 deletions combine_all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"testing"

"fillmore-labs.com/promise"
"fillmore-labs.com/promise/result"
"github.com/stretchr/testify/assert"
)

Expand All @@ -49,20 +48,18 @@ func TestAll(t *testing.T) {
defer cancel()

// when
results := make([]result.Result[int], len(futures))
results := make([]promise.Result[int], len(futures))
for i, r := range promise.AwaitAll(ctx, futures...) { //nolint:typecheck
results[i] = r
}

// then
if assert.NoError(t, results[0].Err()) {
assert.Equal(t, 1, results[0].Value())
if assert.NoError(t, results[0].Err) {
assert.Equal(t, 1, results[0].Value)
}
if assert.ErrorIs(t, results[1].Err(), errTest) {
_ = results[1].Value() // Should not panic
}
if assert.NoError(t, results[2].Err()) {
assert.Equal(t, 2, results[2].Value())
assert.ErrorIs(t, results[1].Err, errTest)
if assert.NoError(t, results[2].Err) {
assert.Equal(t, 2, results[2].Value)
}
}

Expand Down Expand Up @@ -99,21 +96,21 @@ func TestAnyAll(t *testing.T) {
p3.Resolve(struct{}{})

// when
results := make([]result.Result[any], 3)
results := make([]promise.Result[any], 3)
for i, r := range promise.AwaitAllAny(ctx, f1, f2, f3) { //nolint:typecheck
results[i] = r
}

// then
for i, r := range results {
if assert.NoError(t, r.Err()) {
if assert.NoError(t, r.Err) {
switch i {
case 0:
assert.Equal(t, 1, r.Value())
assert.Equal(t, 1, r.Value)
case 1:
assert.Equal(t, "test", r.Value())
assert.Equal(t, "test", r.Value)
case 2:
assert.Equal(t, struct{}{}, r.Value())
assert.Equal(t, struct{}{}, r.Value)
default:
assert.Fail(t, "unexpected index")
}
Expand Down
66 changes: 21 additions & 45 deletions combine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"testing"

"fillmore-labs.com/promise"
"fillmore-labs.com/promise/result"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -54,9 +53,9 @@ func TestWaitAll(t *testing.T) {

// then
assert.Len(t, results, len(futures))
v0, err0 := results[0].V()
_, err1 := results[1].V()
_, err2 := results[2].V()
v0, err0 := results[0].Value, results[0].Err
err1 := results[1].Err
err2 := results[2].Err

if assert.NoError(t, err0) {
assert.Equal(t, 1, v0)
Expand Down Expand Up @@ -134,7 +133,7 @@ func TestCombineCancellation(t *testing.T) {
{name: "All", combine: func(futures []promise.Future[int], ctx context.Context) error {
r := promise.AwaitAllResults(ctx, futures...)

return r[0].Err()
return r[0].Err
}},
{name: "AllValues", combine: func(futures []promise.Future[int], ctx context.Context) error {
_, err := promise.AwaitAllValues(ctx, futures...)
Expand Down Expand Up @@ -181,15 +180,15 @@ func TestCombineMemoized(t *testing.T) {
return promise.AwaitAllResults(ctx, futures...), nil
}, expect: func(t *testing.T, actual any) {
t.Helper()
vv, ok := actual.([]result.Result[int])
vv, ok := actual.([]promise.Result[int])
if !ok {
assert.Fail(t, "Unexpected result type")

return
}

for _, v := range vv {
value, err := v.V()
value, err := v.Value, v.Err
if assert.NoError(t, err) {
assert.Equal(t, 3, value)
}
Expand Down Expand Up @@ -282,55 +281,32 @@ func TestAllAny(t *testing.T) {
p3, f3 := promise.New[struct{}]()

p1.Resolve(1)
p2.Resolve("test")
close(p2)
p3.Resolve(struct{}{})

// when
results := make([]result.Result[any], 3)
promise.AwaitAllAny(ctx, f1, f2, f3)(func(i int, r result.Result[any]) bool {
results := make([]promise.Result[any], 3)
promise.AwaitAllAny(ctx, f1, f2, f3)(func(i int, r promise.Result[any]) bool {
results[i] = r

return true
})

// then
for i, r := range results {
if assert.NoError(t, r.Err()) {
switch i {
case 0:
assert.Equal(t, 1, r.Value())
case 1:
assert.Equal(t, "test", r.Value())
case 2:
assert.Equal(t, struct{}{}, r.Value())
default:
assert.Fail(t, "unexpected index")
switch i {
case 0:
if assert.NoError(t, r.Err) {
assert.Equal(t, 1, r.Value)
}
case 1:
assert.ErrorIs(t, r.Err, promise.ErrNoResult)
case 2:
if assert.NoError(t, r.Err) {
assert.Equal(t, struct{}{}, r.Value)
}
default:
assert.Fail(t, "unexpected index")
}
}
}

func TestAllNil(t *testing.T) {
// given
t.Parallel()
ctx := context.Background()

p1, f1 := promise.New[struct{}]()
p1 <- nil

// when
var v result.Result[any]
var set bool
promise.AwaitAllAny(ctx, f1)(func(_ int, r result.Result[any]) bool {
if set {
assert.Fail(t, "Value already set")
}
v = r
set = true

return false
})

// then
assert.ErrorIs(t, v.Err(), promise.ErrNoResult)
}
Loading

0 comments on commit 57f770f

Please sign in to comment.