Skip to content

Commit 7b9765d

Browse files
committed
feat(rx): add on error xxx and observe operators (#184)
feat(rx): add observe operator (#184) feat(rx): add on error operators (#184)
1 parent cd2dffd commit 7b9765d

File tree

4 files changed

+220
-8
lines changed

4 files changed

+220
-8
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package rx_test
2+
3+
import (
4+
"context"
5+
6+
"github.com/fortytw2/leaktest"
7+
. "github.com/onsi/ginkgo/v2" //nolint:revive // ginkgo ok
8+
. "github.com/onsi/gomega" //nolint:revive // gomega ok
9+
)
10+
11+
var _ = Describe("Observable operator", func() {
12+
Context("Observe", func() {
13+
When("observable", func() {
14+
It("🧪 should: receive all emitted items", func() {
15+
// rxgo: Test_Observable_Observe
16+
defer leaktest.Check(GinkgoT())()
17+
18+
ctx, cancel := context.WithCancel(context.Background())
19+
defer cancel()
20+
got := make([]int, 0)
21+
ch := testObservable[int](ctx, 1, 2, 3).Observe()
22+
for item := range ch {
23+
got = append(got, item.V)
24+
}
25+
Expect(got).To(ContainElements([]int{1, 2, 3}))
26+
})
27+
})
28+
})
29+
})
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package rx_test
2+
3+
import (
4+
"context"
5+
6+
"github.com/fortytw2/leaktest"
7+
. "github.com/onsi/ginkgo/v2" //nolint:revive // ginkgo ok
8+
9+
"github.com/snivilised/lorax/rx"
10+
)
11+
12+
var _ = Describe("Observable operator", func() {
13+
Context("OnErrorResumeNext", func() {
14+
When("error occurs", func() {
15+
It("🧪 should: continue with next iterable", func() {
16+
// rxgo: Test_Observable_OnErrorResumeNext
17+
defer leaktest.Check(GinkgoT())()
18+
19+
ctx, cancel := context.WithCancel(context.Background())
20+
defer cancel()
21+
obs := testObservable[int](ctx, 1, 2, errFoo, 4).OnErrorResumeNext(
22+
func(_ error) rx.Observable[int] {
23+
return testObservable[int](ctx, 10, 20)
24+
},
25+
)
26+
rx.Assert(ctx, obs,
27+
rx.HasItems[int]{
28+
Expected: []int{1, 2, 10, 20},
29+
},
30+
rx.HasNoError[int]{},
31+
)
32+
})
33+
})
34+
})
35+
36+
Context("OnErrorReturn", func() {
37+
When("error occurs", func() {
38+
It("🧪 should: emit translated error and continue", func() {
39+
// rxgo: Test_Observable_OnErrorReturn
40+
defer leaktest.Check(GinkgoT())()
41+
42+
ctx, cancel := context.WithCancel(context.Background())
43+
defer cancel()
44+
45+
obs := testObservable[int](ctx, 1, 2, errFoo, 4, errBar, 6).OnErrorReturn(
46+
func(_ error) int {
47+
return -1
48+
},
49+
)
50+
rx.Assert(ctx, obs,
51+
rx.HasItems[int]{
52+
Expected: []int{1, 2, -1, 4, -1, 6},
53+
},
54+
rx.HasNoError[int]{},
55+
)
56+
})
57+
})
58+
})
59+
60+
Context("OnErrorReturnItem", func() {
61+
When("error occurs", func() {
62+
It("🧪 should: emit translated error and continue", func() {
63+
// rxgo: Test_Observable_OnErrorReturnItem
64+
defer leaktest.Check(GinkgoT())()
65+
66+
ctx, cancel := context.WithCancel(context.Background())
67+
defer cancel()
68+
69+
obs := testObservable[int](ctx, 1, 2, errFoo, 4, errBar, 6).OnErrorReturnItem(-1)
70+
rx.Assert(ctx, obs,
71+
rx.HasItems[int]{
72+
Expected: []int{1, 2, -1, 4, -1, 6},
73+
},
74+
rx.HasNoError[int]{},
75+
)
76+
})
77+
})
78+
})
79+
})

rx/observable-operator.go

Lines changed: 109 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,6 @@ import (
77
"github.com/cenkalti/backoff/v4"
88
)
99

10-
// func isZero[T any](limit T) bool {
11-
// val := reflect.ValueOf(limit).Interface()
12-
// zero := reflect.Zero(reflect.TypeOf(limit)).Interface()
13-
14-
// return val != zero
15-
// }
16-
1710
// All determines whether all items emitted by an Observable meet some criteria.
1811
func (o *ObservableImpl[T]) All(predicate Predicate[T], opts ...Option[T]) Single[T] {
1912
const (
@@ -1359,6 +1352,115 @@ func (o *ObservableImpl[T]) Observe(opts ...Option[T]) <-chan Item[T] {
13591352
return o.iterable.Observe(opts...)
13601353
}
13611354

1355+
// OnErrorResumeNext instructs an Observable to pass control to another Observable rather than invoking
1356+
// onError if it encounters an error.
1357+
func (o *ObservableImpl[T]) OnErrorResumeNext(resumeSequence ErrorToObservable[T], opts ...Option[T]) Observable[T] {
1358+
const (
1359+
forceSeq = true
1360+
bypassGather = false
1361+
)
1362+
1363+
return observable(o.parent, o, func() operator[T] {
1364+
return &onErrorResumeNextOperator[T]{
1365+
resumeSequence: resumeSequence,
1366+
}
1367+
}, forceSeq, bypassGather, opts...)
1368+
}
1369+
1370+
type onErrorResumeNextOperator[T any] struct {
1371+
resumeSequence ErrorToObservable[T]
1372+
}
1373+
1374+
func (op *onErrorResumeNextOperator[T]) next(ctx context.Context, item Item[T],
1375+
dst chan<- Item[T], _ operatorOptions[T]) {
1376+
item.SendContext(ctx, dst)
1377+
}
1378+
1379+
func (op *onErrorResumeNextOperator[T]) err(_ context.Context, item Item[T],
1380+
_ chan<- Item[T], operatorOptions operatorOptions[T],
1381+
) {
1382+
operatorOptions.resetIterable(op.resumeSequence(item.E))
1383+
}
1384+
1385+
func (op *onErrorResumeNextOperator[T]) end(_ context.Context, _ chan<- Item[T]) {
1386+
}
1387+
1388+
func (op *onErrorResumeNextOperator[T]) gatherNext(_ context.Context, _ Item[T],
1389+
_ chan<- Item[T], _ operatorOptions[T]) {
1390+
}
1391+
1392+
func (o *ObservableImpl[T]) OnErrorReturn(resumeFunc ErrorFunc[T], opts ...Option[T]) Observable[T] {
1393+
const (
1394+
forceSeq = true
1395+
bypassGather = false
1396+
)
1397+
1398+
return observable(o.parent, o, func() operator[T] {
1399+
return &onErrorReturnOperator[T]{
1400+
resumeFunc: resumeFunc,
1401+
}
1402+
}, forceSeq, bypassGather, opts...)
1403+
}
1404+
1405+
type onErrorReturnOperator[T any] struct {
1406+
resumeFunc ErrorFunc[T]
1407+
}
1408+
1409+
func (op *onErrorReturnOperator[T]) next(ctx context.Context, item Item[T],
1410+
dst chan<- Item[T], _ operatorOptions[T]) {
1411+
item.SendContext(ctx, dst)
1412+
}
1413+
1414+
func (op *onErrorReturnOperator[T]) err(ctx context.Context, item Item[T],
1415+
dst chan<- Item[T], _ operatorOptions[T],
1416+
) {
1417+
Of[T](op.resumeFunc(item.E)).SendContext(ctx, dst)
1418+
}
1419+
1420+
func (op *onErrorReturnOperator[T]) end(_ context.Context, _ chan<- Item[T]) {
1421+
}
1422+
1423+
func (op *onErrorReturnOperator[T]) gatherNext(_ context.Context, _ Item[T],
1424+
_ chan<- Item[T], _ operatorOptions[T]) {
1425+
}
1426+
1427+
func (o *ObservableImpl[T]) OnErrorReturnItem(resume T, opts ...Option[T]) Observable[T] {
1428+
const (
1429+
forceSeq = true
1430+
bypassGather = false
1431+
)
1432+
1433+
return observable(o.parent, o, func() operator[T] {
1434+
return &onErrorReturnItemOperator[T]{
1435+
resume: resume,
1436+
}
1437+
}, true, false, opts...)
1438+
}
1439+
1440+
type onErrorReturnItemOperator[T any] struct {
1441+
resume T
1442+
}
1443+
1444+
func (op *onErrorReturnItemOperator[T]) next(ctx context.Context, item Item[T],
1445+
dst chan<- Item[T], _ operatorOptions[T],
1446+
) {
1447+
item.SendContext(ctx, dst)
1448+
}
1449+
1450+
func (op *onErrorReturnItemOperator[T]) err(ctx context.Context, _ Item[T],
1451+
dst chan<- Item[T], _ operatorOptions[T],
1452+
) {
1453+
Of(op.resume).SendContext(ctx, dst)
1454+
}
1455+
1456+
func (op *onErrorReturnItemOperator[T]) end(_ context.Context, _ chan<- Item[T]) {
1457+
}
1458+
1459+
func (op *onErrorReturnItemOperator[T]) gatherNext(_ context.Context, _ Item[T],
1460+
_ chan<- Item[T], _ operatorOptions[T],
1461+
) {
1462+
}
1463+
13621464
// ToSlice collects all items from an Observable and emit them in a slice and
13631465
// an optional error. Cannot be run in parallel.
13641466
func (o *ObservableImpl[T]) ToSlice(initialCapacity int, opts ...Option[T]) ([]Item[T], error) {

rx/observable.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ type Observable[T any] interface {
4040
Max(comparator Comparator[T], initLimit InitLimit[T], opts ...Option[T]) OptionalSingle[T]
4141
Map(apply Func[T], opts ...Option[T]) Observable[T]
4242
Min(comparator Comparator[T], initLimit InitLimit[T], opts ...Option[T]) OptionalSingle[T]
43-
43+
OnErrorResumeNext(resumeSequence ErrorToObservable[T], opts ...Option[T]) Observable[T]
44+
OnErrorReturn(resumeFunc ErrorFunc[T], opts ...Option[T]) Observable[T]
45+
OnErrorReturnItem(resume T, opts ...Option[T]) Observable[T]
4446
Run(opts ...Option[T]) Disposed
4547

4648
ToSlice(initialCapacity int, opts ...Option[T]) ([]Item[T], error)

0 commit comments

Comments
 (0)