Skip to content

Instantly share code, notes, and snippets.

@CassiusPacheco
Last active August 16, 2024 07:29
Show Gist options
  • Select an option

  • Save CassiusPacheco/409e66e220ce563440df00385f39ac98 to your computer and use it in GitHub Desktop.

Select an option

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
// 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 => [];
}
@fredgrott
Copy link
Copy Markdown

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());

      return obj as Failure;
    } catch (e) {

      log(obj.toString());

      throw obj;
    }
  }),
);

}
}
`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment