Skip to content

Commit 2198c53

Browse files
authored
Merge pull request #22 from GregOnNet/feature/bindFailure
feat(api): add bindFailure & bindFailureAsync
2 parents 3583ff6 + 138d8f1 commit 2198c53

5 files changed

Lines changed: 220 additions & 0 deletions

File tree

src/result.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,38 @@ export class Result<TValue = Unit, TError = string> {
767767
: resultAsyncOrPromise;
768768
}
769769

770+
/**
771+
* Maps a failed Result to a new Result
772+
* @param projection
773+
* @returns
774+
*/
775+
bindFailure(
776+
projection: FunctionOfT<Result<TValue, TError>>
777+
): Result<TValue, TError> {
778+
return this.isSuccess ? this : projection();
779+
}
780+
781+
/**
782+
* Maps a failed Result to a new ResultAsync
783+
* @param projection
784+
* @returns
785+
*/
786+
bindFailureAsync(
787+
projection:
788+
| FunctionOfT<Promise<Result<TValue, TError>>>
789+
| FunctionOfT<ResultAsync<TValue, TError>>
790+
): ResultAsync<TValue, TError> {
791+
if (this.isSuccess) {
792+
return ResultAsync.success(this.getValueOrThrow());
793+
}
794+
795+
const resultAsyncOrPromise = projection();
796+
797+
return isPromise(resultAsyncOrPromise)
798+
? ResultAsync.from<TValue, TError>(resultAsyncOrPromise)
799+
: resultAsyncOrPromise;
800+
}
801+
770802
/**
771803
* Executes an action if the current Result has succeeded
772804
* @param action a function given the value of the current Result

src/resultAsync.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,31 @@ export class ResultAsync<TValue = Unit, TError = string> {
495495
);
496496
}
497497

498+
/**
499+
* Maps a failed ResultAsync to a new ResultAsync
500+
* @param projection
501+
* @returns
502+
*/
503+
bindFailure(
504+
projection:
505+
| FunctionOfT<Result<TValue, TError>>
506+
| FunctionOfT<ResultAsync<TValue, TError>>
507+
): ResultAsync<TValue, TError> {
508+
return new ResultAsync(
509+
this.value.then((r) => {
510+
if (r.isSuccess) {
511+
return Result.success(r.getValueOrThrow());
512+
}
513+
514+
const resultOrResultAsync = projection();
515+
516+
return resultOrResultAsync instanceof Result
517+
? resultOrResultAsync
518+
: resultOrResultAsync.toPromise();
519+
})
520+
);
521+
}
522+
498523
/**
499524
* Executes the given async action if the ResultAsync is successful
500525
* @param action an async function given the value of the successful ResultAsync

test/result/bindFailure.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Result } from '@/src/result';
2+
3+
describe('Result', () => {
4+
describe('bindFailure', () => {
5+
test('takes the result from the second result, when previous result fails', () => {
6+
const sut = Result.failure('💥');
7+
8+
expect(sut.bindFailure(() => Result.success('✅'))).toSucceedWith('✅');
9+
});
10+
11+
test('takes the result from the first result, when it succeeds', () => {
12+
const sut = Result.success('✅');
13+
14+
expect(sut.bindFailure(() => Result.failure('💥'))).toSucceedWith('✅');
15+
});
16+
17+
test('takes the failure from the second result, when both fail', () => {
18+
const sut = Result.failure('💥');
19+
20+
expect(sut.bindFailure(() => Result.failure('💥💥'))).toFailWith('💥💥');
21+
});
22+
});
23+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Result } from '@/src/result';
2+
import { ResultAsync } from '../../src';
3+
4+
describe('Result', () => {
5+
describe('bindFailureAsync', () => {
6+
describe('Promise', () => {
7+
test('takes the result from the second result, when previous result fails', async () => {
8+
const sut = Result.failure('💥');
9+
10+
const innerResult = await sut
11+
.bindFailureAsync(() => Promise.resolve(Result.success('✅')))
12+
.toPromise();
13+
14+
return expect(innerResult).toSucceedWith('✅');
15+
});
16+
17+
test('takes the result from the first result, when it succeeds', async () => {
18+
const sut = Result.success('✅');
19+
20+
const innerResult = await sut
21+
.bindFailureAsync(() => Promise.resolve(Result.failure('💥')))
22+
.toPromise();
23+
24+
return expect(innerResult).toSucceedWith('✅');
25+
});
26+
27+
test('takes the failure from the second result, when both fail', async () => {
28+
const sut = Result.failure('💥');
29+
30+
const innerResult = await sut
31+
.bindFailureAsync(() => Promise.resolve(Result.failure('💥💥')))
32+
.toPromise();
33+
34+
return expect(innerResult).toFailWith('💥💥');
35+
});
36+
});
37+
38+
describe('ResultAsync', () => {
39+
test('takes the result from the second result, when previous result fails', async () => {
40+
const sut = Result.failure('💥');
41+
42+
const innerResult = await sut
43+
.bindFailureAsync(() => ResultAsync.success('✅'))
44+
.toPromise();
45+
46+
return expect(innerResult).toSucceedWith('✅');
47+
});
48+
49+
test('takes the result from the first result, when it succeeds', async () => {
50+
const sut = Result.success('✅');
51+
52+
const innerResult = await sut
53+
.bindFailureAsync(() => ResultAsync.failure('💥'))
54+
.toPromise();
55+
56+
return expect(innerResult).toSucceedWith('✅');
57+
});
58+
59+
test('takes the failure from the second result, when both fail', async () => {
60+
const sut = Result.failure('💥');
61+
62+
const innerResult = await sut
63+
.bindFailureAsync(() => ResultAsync.failure('💥💥'))
64+
.toPromise();
65+
66+
return expect(innerResult).toFailWith('💥💥');
67+
});
68+
});
69+
});
70+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Result } from '@/src/result';
2+
import { ResultAsync } from '@/src/resultAsync';
3+
4+
describe('ResultAsync', () => {
5+
describe('bindFailure', () => {
6+
describe('Result', () => {
7+
test('takes the result from the second result, when previous result fails', async () => {
8+
const sut = ResultAsync.failure('💥');
9+
10+
const innerResult = await sut
11+
.bindFailure(() => Result.success('✅'))
12+
.toPromise();
13+
14+
return expect(innerResult).toSucceedWith('✅');
15+
});
16+
17+
test('takes the result from the first result, when it succeeds', async () => {
18+
const sut = ResultAsync.success('✅');
19+
20+
const innerResult = await sut
21+
.bindFailure(() => Result.failure('💥'))
22+
.toPromise();
23+
24+
return expect(innerResult).toSucceedWith('✅');
25+
});
26+
27+
test('takes the failure from the second result, when both fail', async () => {
28+
const sut = ResultAsync.failure('💥');
29+
30+
const innerResult = await sut
31+
.bindFailure(() => Result.failure('💥💥'))
32+
.toPromise();
33+
34+
return expect(innerResult).toFailWith('💥💥');
35+
});
36+
});
37+
38+
describe('ResultAsync', () => {
39+
test('takes the result from the second result, when previous result fails', async () => {
40+
const sut = ResultAsync.failure('💥');
41+
42+
const innerResult = await sut
43+
.bindFailure(() => ResultAsync.success('✅'))
44+
.toPromise();
45+
46+
return expect(innerResult).toSucceedWith('✅');
47+
});
48+
49+
test('takes the result from the first result, when it succeeds', async () => {
50+
const sut = ResultAsync.success('✅');
51+
52+
const innerResult = await sut
53+
.bindFailure(() => ResultAsync.failure('💥'))
54+
.toPromise();
55+
56+
return expect(innerResult).toSucceedWith('✅');
57+
});
58+
59+
test('takes the failure from the second result, when both fail', async () => {
60+
const sut = ResultAsync.failure('💥');
61+
62+
const innerResult = await sut
63+
.bindFailure(() => ResultAsync.failure('💥💥'))
64+
.toPromise();
65+
66+
return expect(innerResult).toFailWith('💥💥');
67+
});
68+
});
69+
});
70+
});

0 commit comments

Comments
 (0)