Last active
August 16, 2024 07:29
-
-
Save CassiusPacheco/409e66e220ce563440df00385f39ac98 to your computer and use it in GitHub Desktop.
Dart's DataResult<S> inspired by my Swift enum implementation and Avdosev's Dart Either implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // The code below was inspired on https://github.com/avdosev/either_dart/blob/master/lib/src/either.dart | |
| import 'package:equatable/equatable.dart'; | |
| abstract class Failure extends Equatable { | |
| @override | |
| List<Object> get props => []; | |
| } | |
| // General failures | |
| class GenericFailure extends Failure {} | |
| class APIFailure extends Failure {} | |
| abstract class DataResult<S> extends Equatable { | |
| const DataResult(); | |
| static DataResult<S> failure<S>(Failure failure) => _FailureResult(failure); | |
| static DataResult<S> success<S>(S data) => _SuccessResult(data); | |
| /// Get [error] value, may throw an exception when the value is [data] | |
| Failure get error => fold<Failure>( | |
| (error) => error, | |
| (data) => throw Exception( | |
| 'Illegal use. You should check isFailure() before calling')); | |
| /// Get [data] value, may throw an exception when the value is [error] | |
| S get data => fold<S>( | |
| (error) => throw Exception( | |
| 'Illegal use. You should check isSuccess() before calling'), | |
| (data) => data); | |
| /// Returns `true` if the object is of the `SuccessResult` type, which means | |
| /// `data` will return a valid result. | |
| bool isSuccess() => this is _SuccessResult<S>; | |
| /// Returns `true` if the object is of the `FailureResult` type, which means | |
| /// `error` will return a valid result. | |
| bool isFailure() => this is _FailureResult<S>; | |
| /// Returns `data` if `isSuccess()` returns `true`, otherwise it returns | |
| /// `other`. | |
| S dataOrElse(S other) => isSuccess() ? data : other; | |
| /// Sugar syntax that calls `dataOrElse` under the hood. Returns left value if | |
| /// `isSuccess()` returns `true`, otherwise it returns the right value. | |
| S operator |(S other) => dataOrElse(other); | |
| /// Transforms values of [error] and [data] in new a `DataResult` type. Only | |
| /// the matching function to the object type will be executed. For example, | |
| /// for a `SuccessResult` object only the [fnData] function will be executed. | |
| DataResult<T> either<T>( | |
| Failure Function(Failure error) fnFailure, T Function(S data) fnData); | |
| /// Transforms value of [data] allowing a new `DataResult` to be returned. | |
| /// A `SuccessResult` might return a `FailureResult` and vice versa. | |
| DataResult<T> then<T>(DataResult<T> Function(S data) fnData); | |
| /// Transforms value of [data] always keeping the original identity of the | |
| /// `DataResult`, meaning that a `SuccessResult` returns a `SuccessResult` and | |
| /// a `FailureResult` always returns a `FailureResult`. | |
| DataResult<T> map<T>(T Function(S data) fnData); | |
| /// Folds [error] and [data] into the value of one type. Only the matching | |
| /// function to the object type will be executed. For example, for a | |
| /// `SuccessResult` object only the [fnData] function will be executed. | |
| T fold<T>(T Function(Failure error) fnFailure, T Function(S data) fnData); | |
| /// For instances where generics gets lost with `SuccessResult` and | |
| /// `FailureResult` failing to cast to `DataResult` this method ensures the | |
| /// casting works. | |
| // ignore: avoid_returning_this | |
| // DataResult<S> asDataResult() => this; | |
| @override | |
| List<Object> get props => [if (isSuccess()) data else error]; | |
| } | |
| /// Success implementation of `DataResult`. It contains `data`. | |
| class _SuccessResult<S> extends DataResult<S> { | |
| final S value; | |
| const _SuccessResult(this.value); | |
| @override | |
| _SuccessResult<T> either<T>( | |
| Failure Function(Failure error) fnFailure, T Function(S data) fnData) { | |
| return _SuccessResult<T>(fnData(value)); | |
| } | |
| @override | |
| DataResult<T> then<T>(DataResult<T> Function(S data) fnData) { | |
| return fnData(value); | |
| } | |
| @override | |
| _SuccessResult<T> map<T>(T Function(S data) fnData) { | |
| return _SuccessResult<T>(fnData(value)); | |
| } | |
| @override | |
| T fold<T>(T Function(Failure error) fnFailure, T Function(S data) fnData) { | |
| return fnData(value); | |
| } | |
| } | |
| /// Failure implementation of `DataResult`. It contains `error`. | |
| class _FailureResult<S> extends DataResult<S> { | |
| final Failure value; | |
| const _FailureResult(this.value); | |
| @override | |
| _FailureResult<T> either<T>( | |
| Failure Function(Failure error) fnFailure, T Function(S data) fnData) { | |
| return _FailureResult<T>(fnFailure(value)); | |
| } | |
| @override | |
| _FailureResult<T> map<T>(T Function(S data) fnData) { | |
| return _FailureResult<T>(value); | |
| } | |
| @override | |
| _FailureResult<T> then<T>(DataResult<T> Function(S data) fnData) { | |
| return _FailureResult<T>(value); | |
| } | |
| @override | |
| T fold<T>(T Function(Failure error) fnFailure, T Function(S data) fnData) { | |
| return fnFailure(value); | |
| } | |
| } | |
| class Empty extends Equatable { | |
| @override | |
| List<Object> get props => []; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To jazz it up add this
`extension TaskX<T extends Either<Object, U>, U> on Task {
Task<Either<Failure, U>> mapLeftToFailure() {
// ignore: unnecessary_this
return this.map(
(either) => either.leftMap((obj) {
try {
log(obj.toString());
}
}
`