Skip to content

Commit

Permalink
feat!: add Result.unwrap (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
ookami-kb committed Feb 16, 2024
1 parent 4745288 commit 4b13af3
Show file tree
Hide file tree
Showing 13 changed files with 60 additions and 53 deletions.
8 changes: 8 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
include: package:mews_pedantic/analysis_options.yaml

dart_code_metrics:
rules:
prefer-explicit-parameter-names: false
match-getter-setter-field-names: false
move-records-to-typedefs: false
avoid-barrel-files: false
prefer-typedefs-for-callbacks: false
2 changes: 0 additions & 2 deletions lib/dfunc.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
library dfunc;

export 'src/always.dart';
export 'src/cast.dart';
export 'src/compact.dart';
Expand Down
2 changes: 2 additions & 0 deletions lib/src/either/either.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes

/// Represents a value that can be either of type [L] or of type [R].
/// Usually [L] is assumed to be of Error type and [R] of Right type, e.g.:
///
Expand Down
2 changes: 1 addition & 1 deletion lib/src/either/eithers.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:dfunc/src/either/either.dart';

abstract class Eithers {
Eithers._();
const Eithers._();

static Either<L, (R1, R2)> combine2<L, R1, R2>(
Either<L, R1> first,
Expand Down
46 changes: 22 additions & 24 deletions lib/src/either/try_either.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,42 @@ import 'dart:async';
import 'package:dfunc/dfunc.dart';

typedef Result<T> = Either<Exception, T>;

typedef AsyncResult<T> = Future<Result<T>>;

typedef BindEither = T Function<T>(Result<T>);

AsyncResult<T> tryEitherAsync<T>(
FutureOr<T> Function(BindEither bind) block,
) async {
A bind<A>(Result<A> either) => either.fold((e) => throw e, identity);
extension ResultUnwrap<R> on Result<R> {
R unwrap() => fold((e) => throw e, (r) => r);
}

/// Wraps [block] function into try..catch and returns [Right] with the
/// result. In case of any [Exception] returns [Left] with the exception.
///
/// It doesn't catch [Error]s as it usually means a bug in the code.
Result<T> tryEither<T>(T Function() block) {
try {
return Either.right(await block(bind));
} on Exception catch (e) {
return Either.left(e);
return Either.right(block());
} on Exception catch (error) {
return Either.left(error);
}
}

Result<T> tryEither<T>(T Function(BindEither bind) block) {
A bind<A>(Result<A> either) => either.fold((e) => throw e, identity);

/// Wraps [block] function into try..catch asynchronously and returns [Future]
/// with [Right] with the result. In case of any [Exception] returns [Future]
/// with [Left] containing the exception.
///
/// It doesn't catch [Error]s as it usually means a bug in the code.
AsyncResult<T> tryEitherAsync<T>(FutureOr<T> Function() block) async {
try {
return Either.right(block(bind));
} on Exception catch (e) {
return Either.left(e);
return Either.right(await block());
} on Exception catch (error) {
return Either.left(error);
}
}

extension FutureToEitherExt<T> on Future<T> {
AsyncResult<T> toEither() async {
try {
return Either.right(await this);
} on Exception catch (e) {
return Either.left(e);
}
}
AsyncResult<T> toEither() => tryEitherAsync(() => this);
}

extension IterableResultExt<T> on Iterable<Result<T>> {
Result<List<T>> sequence() =>
tryEither((bind) => this.map((e) => bind(e)).toList());
tryEither(() => this.map((e) => e.unwrap()).toList());
}
2 changes: 1 addition & 1 deletion lib/src/intersperse.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ extension Intersperse<A> on Iterable<A> {
}

yield itemBuilder(iterator.current);
var prev = iterator.current;
A prev = iterator.current;
while (iterator.moveNext()) {
yield separatorBuilder(prev, iterator.current);
prev = iterator.current;
Expand Down
4 changes: 2 additions & 2 deletions lib/src/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:dfunc/src/func.dart';
typedef State<S, A> = (A, S) Function(S);

abstract class States {
States._();
const States._();

static State<S, A> unit<S, A>(A a) => (s) => (a, s);

Expand Down Expand Up @@ -37,7 +37,7 @@ extension StateExt<S, A> on State<S, A> {
extension StateExtIterable<S, A> on Iterable<State<S, A>> {
State<S, Iterable<A>> sequence() => (s) {
final r = <A>[];
var x = s;
S x = s;
for (final a in this) {
final s2 = a(x);
r.add(s2.$1);
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ environment:

dev_dependencies:
melos: ^3.0.1
mews_pedantic: ^0.13.0
mews_pedantic: ^0.23.0
test: ^1.16.2
11 changes: 5 additions & 6 deletions test/either/either_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void main() {
test('Either::flatMapLeftAsync', () async {
final either =
await Either<Exception, String>.left(Exception()).flatMapLeftAsync(
(e) async => const Either<String, String>.left('Error'),
(e) => const Either<String, String>.left('Error'),
);
expect(either.fold(identity, (_) => throw Error()), 'Error');
});
Expand Down Expand Up @@ -155,8 +155,7 @@ void main() {

test('Future<Either>::foldAsync with async mappers', () async {
final either = Future.value(const Either<int, String>.right('Test'));
final value =
await either.foldAsync((_) async => false, (_) async => true);
final value = await either.foldAsync((_) => false, (_) => true);
expect(value, true);
});

Expand All @@ -168,7 +167,7 @@ void main() {

test('Future<Either>::mapAsync with async mapper', () async {
final either = Future.value(const Either<int, String>.right('123'));
final value = await either.mapAsync((x) async => int.parse(x));
final value = await either.mapAsync(int.parse);
expect(value.fold(always(0), identity), 123);
});

Expand All @@ -180,14 +179,14 @@ void main() {

test('Future<Either>::mapLeftAsync with async mapper', () async {
final either = Future.value(const Either<String, int>.left('123'));
final value = await either.mapLeftAsync((x) async => int.parse(x));
final value = await either.mapLeftAsync(int.parse);
expect(value.fold(identity, always(0)), 123);
});

test('Future<Either>::flatMapAsync with async mapper', () async {
final either = Future.value(const Either<int, String>.right('123'));
final value =
await either.flatMapAsync((x) async => Either.right(int.parse(x)));
await either.flatMapAsync((x) => Either.right(int.parse(x)));
expect(value.fold(always(0), identity), 123);
});

Expand Down
28 changes: 14 additions & 14 deletions test/either/try_either_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,39 @@ import 'package:test/test.dart';
void main() {
group('tryEither', () {
test('returns right', () {
final result = tryEither<int>((bind) => int.parse('123'));
final result = tryEither(() => int.parse('123'));

expect(result, const Either<Exception, int>.right(123));
expect(result, const Result<int>.right(123));
});

test('returns left on exception', () {
final result = tryEither<int>((bind) => int.parse('wrong'));
final result = tryEither(() => int.parse('wrong'));

expect(result.isLeft(), true);
});
});

group('tryEitherAsync', () {
test('returns right', () async {
final result = await tryEitherAsync<int>((bind) => int.parse('123'));
final result = await tryEitherAsync(() => int.parse('123'));

expect(result, const Either<Exception, int>.right(123));
expect(result, const Result<int>.right(123));
});

test('returns left on exception', () async {
final result = await tryEitherAsync<int>((bind) => int.parse('wrong'));
final result = await tryEitherAsync(() => int.parse('wrong'));

expect(result.isLeft(), true);
});
});

group('toEither', () {
Future<int> parse(String value) async => int.parse(value);
Future<int> parse(String value) => Future.sync(() => int.parse(value));

test('returns right', () async {
expect(
await parse('123').toEither(),
const Either<Exception, int>.right(123),
const Result<int>.right(123),
);
});

Expand All @@ -48,9 +48,9 @@ void main() {
group('Result.sequence', () {
test('returns right', () {
[
const Either<Exception, int>.right(1),
const Either<Exception, int>.right(2),
const Either<Exception, int>.right(3),
const Result<int>.right(1),
const Result<int>.right(2),
const Result<int>.right(3),
].sequence().fold(
(l) => fail('Expected right, got left: $l'),
(r) => expect(r, [1, 2, 3]),
Expand All @@ -61,9 +61,9 @@ void main() {
final exception = Exception('error');

[
const Either<Exception, int>.right(1),
Either<Exception, int>.left(exception),
const Either<Exception, int>.right(3),
const Result<int>.right(1),
Result<int>.left(exception),
const Result<int>.right(3),
].sequence().fold(
(l) => expect(l, exception),
(r) => fail('Expected left, got right: $r'),
Expand Down
2 changes: 2 additions & 0 deletions test/group_by_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ignore_for_file: avoid-duplicate-collection-elements

import 'package:dfunc/dfunc.dart';
import 'package:test/test.dart';

Expand Down
2 changes: 1 addition & 1 deletion test/intersperse_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void main() {
String buildItem(int a) => a.toString();
String buildSeparator(int a, int b) => '<$a-$b>';

sut(
Iterable<String> sut(
Iterable<int> items, {
bool beforeFirst = false,
bool afterLast = false,
Expand Down
2 changes: 1 addition & 1 deletion test/scope_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ void main() {
});

test('also', () {
var x = 0;
int x = 0;
final result = 3.also((it) => x += 5);
expect(result, 3);
expect(x, 5);
Expand Down

0 comments on commit 4b13af3

Please sign in to comment.