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.

Revisions

  1. CassiusPacheco revised this gist Mar 20, 2021. 2 changed files with 10 additions and 16 deletions.
    18 changes: 6 additions & 12 deletions data_result.dart
    Original file line number Diff line number Diff line change
    @@ -31,17 +31,11 @@ abstract class DataResult<S> extends Equatable {
    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(
    'No error found. Check `isFailure` before calling `error`'));

    /// Get [data] value, may throw an exception when the value is [error]
    S get data => fold<S>(
    (error) => throw Exception(
    'No data found. Check `isSuccess` before calling `data`'),
    (data) => data);
    /// Get [error] value, returns null when the value is actually [data]
    Failure? get error => fold<Failure?>((error) => error, (data) => null);

    /// Get [data] value, returns null when the value is actually [error]
    S? get data => fold<S?>((error) => null, (data) => data);

    /// Returns `true` if the object is of the `SuccessResult` type, which means
    /// `data` will return a valid result.
    @@ -53,7 +47,7 @@ abstract class DataResult<S> extends Equatable {

    /// Returns `data` if `isSuccess` returns `true`, otherwise it returns
    /// `other`.
    S dataOrElse(S other) => isSuccess ? data : other;
    S dataOrElse(S other) => isSuccess && data != null ? data! : other;

    /// Sugar syntax that calls `dataOrElse` under the hood. Returns left value if
    /// `isSuccess` returns `true`, otherwise it returns the right value.
    8 changes: 4 additions & 4 deletions data_result_test.dart
    Original file line number Diff line number Diff line change
    @@ -9,9 +9,9 @@ void main() {
    expect(dataResult.data, data);
    });

    test('data throws exception when it is Failure result', () {
    test('data returns null when it is Failure result', () {
    final dataResult = DataResult.failure(GenericFailure());
    expect(() => dataResult.data, throwsException);
    expect(dataResult.data, null);
    });

    test('`isSuccess` returns true for Success result', () {
    @@ -51,9 +51,9 @@ void main() {
    expect(dataResult.error, APIFailure());
    });

    test('failure throws exception when it is Success result', () {
    test('failure returns null when it is Success result', () {
    final dataResult = DataResult.success('something');
    expect(() => dataResult.error, throwsException);
    expect(dataResult.error, null);
    });
    });

  2. CassiusPacheco revised this gist Mar 18, 2021. 2 changed files with 2 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion data_result.dart
    Original file line number Diff line number Diff line change
    @@ -80,7 +80,7 @@ abstract class DataResult<S> extends Equatable {
    T fold<T>(T Function(Failure error) fnFailure, T Function(S data) fnData);

    @override
    List<Object> get props => [if (isSuccess) data else error];
    List<Object?> get props => [if (isSuccess) data else error];
    }

    /// Success implementation of `DataResult`. It contains `data`. It's abstracted
    2 changes: 1 addition & 1 deletion data_result_test.dart
    Original file line number Diff line number Diff line change
    @@ -168,7 +168,7 @@ void main() {
    expect(result.data, APIFailure());
    });
    });

    group('DataResult either', () {
    test('only executes error changing the error type for Failure result', () {
    final result = DataResult.failure<String>(GenericFailure()).either(
  3. CassiusPacheco revised this gist Feb 23, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions data_result_test.dart
    Original file line number Diff line number Diff line change
    @@ -173,7 +173,7 @@ void main() {
    test('only executes error changing the error type for Failure result', () {
    final result = DataResult.failure<String>(GenericFailure()).either(
    (error) => APIFailure(),
    (data) => throw Exception('This will never happen'),
    (data) => throw Exception('This will never happen for failure'),
    );

    expect(result.isFailure, true);
    @@ -182,7 +182,7 @@ void main() {

    test('only executes data for Success result', () {
    final result = DataResult.success('yo').either(
    (error) => throw Exception('This will never happen'),
    (error) => throw Exception('This will never happen for success'),
    (data) => 'new value here',
    );

  4. CassiusPacheco revised this gist Feb 23, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion data_result_test.dart
    Original file line number Diff line number Diff line change
    @@ -169,7 +169,7 @@ void main() {
    });
    });

    group('DataResult either', () {
    group('DataResult either', () {
    test('only executes error changing the error type for Failure result', () {
    final result = DataResult.failure<String>(GenericFailure()).either(
    (error) => APIFailure(),
  5. CassiusPacheco revised this gist Feb 23, 2021. 1 changed file with 22 additions and 0 deletions.
    22 changes: 22 additions & 0 deletions data_result_test.dart
    Original file line number Diff line number Diff line change
    @@ -168,4 +168,26 @@ void main() {
    expect(result.data, APIFailure());
    });
    });

    group('DataResult either', () {
    test('only executes error changing the error type for Failure result', () {
    final result = DataResult.failure<String>(GenericFailure()).either(
    (error) => APIFailure(),
    (data) => throw Exception('This will never happen'),
    );

    expect(result.isFailure, true);
    expect(result.error, APIFailure());
    });

    test('only executes data for Success result', () {
    final result = DataResult.success('yo').either(
    (error) => throw Exception('This will never happen'),
    (data) => 'new value here',
    );

    expect(result.isSuccess, true);
    expect(result.data, 'new value here');
    });
    });
    }
  6. CassiusPacheco revised this gist Feb 23, 2021. 1 changed file with 171 additions and 0 deletions.
    171 changes: 171 additions & 0 deletions data_result_test.dart
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,171 @@
    import 'package:test/test.dart';
    import 'package:app/data_result.dart';

    void main() {
    group('DataResult', () {
    test('gets the data when it is Success result', () {
    const data = 'hello';
    final dataResult = DataResult.success(data);
    expect(dataResult.data, data);
    });

    test('data throws exception when it is Failure result', () {
    final dataResult = DataResult.failure(GenericFailure());
    expect(() => dataResult.data, throwsException);
    });

    test('`isSuccess` returns true for Success result', () {
    const data = 'hello';
    final dataResult = DataResult.success(data);
    expect(dataResult.isSuccess, true);
    });

    test('`isSuccess` returns false for Failure result', () {
    final dataResult = DataResult.failure(GenericFailure());
    expect(dataResult.isSuccess, false);
    });

    test('dataOrElse returns `data` for Success result', () {
    const data = 'foo';
    final dataResult = DataResult.success(data);
    expect(dataResult.dataOrElse('bar'), 'foo');
    });

    test('dataOrElse returns else data for Failure result', () {
    final dataResult = DataResult.failure(GenericFailure());
    expect(dataResult.dataOrElse('bar'), 'bar');
    });

    test('isFailure returns true for Failure result', () {
    final dataResult = DataResult.failure(APIFailure());
    expect(dataResult.isFailure, true);
    });

    test('isFailure returns false for Success result', () {
    final dataResult = DataResult.success('something');
    expect(dataResult.isFailure, false);
    });

    test('gets error when it is Failure result', () {
    final dataResult = DataResult.failure(APIFailure());
    expect(dataResult.error, APIFailure());
    });

    test('failure throws exception when it is Success result', () {
    final dataResult = DataResult.success('something');
    expect(() => dataResult.error, throwsException);
    });
    });

    group('DataResult | operator', () {
    test("returns existing value if it's Success result", () {
    const data = 'foo';
    final dataResult = DataResult.success(data);
    expect(dataResult | 'bar', 'foo');
    });

    test("returns other value if it's Failure result", () {
    final dataResult = DataResult.failure(GenericFailure());
    expect(dataResult | 'bar', 'bar');
    });
    });

    group('DataResult', () {
    test('should be equal when two success objects have equal data', () {
    const data = 'hello';
    final dataResult = DataResult.success(data);
    const data2 = 'hello';
    final dataResult2 = DataResult.success(data2);
    expect(dataResult == dataResult2, true);
    });

    test('should not be equal when two success objects have different data',
    () {
    const data = 'hello';
    final dataResult = DataResult.success(data);
    const data2 = 'hello2';
    final dataResult2 = DataResult.success(data2);
    expect(dataResult == dataResult2, false);
    });

    test('should be equal when two failure objects have equal error', () {
    final dataResult = DataResult.failure(APIFailure());
    final dataResult2 = DataResult.failure(APIFailure());
    expect(dataResult == dataResult2, true);
    });

    test('should not be equal when two failure objects have different error',
    () {
    final dataResult = DataResult.failure(GenericFailure());
    final dataResult2 = DataResult.failure(APIFailure());
    expect(dataResult == dataResult2, false);
    });
    });

    group('DataResult fold', () {
    test('transforms failure into a false bool', () {
    final result = DataResult.failure<String>(GenericFailure())
    .fold<bool>((failure) => false, (data) => true);

    expect(result, false);
    });

    test('transforms data into a true bool', () {
    final result = DataResult.success('yo')
    .fold<bool>((failure) => false, (data) => true);

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

    group('DataResult then', () {
    test('bubbles up failure instead of transforming the success value', () {
    final result = DataResult.failure<String>(GenericFailure())
    .then((data) => DataResult.success(1.34));

    expect(result.isFailure, true);
    expect(result.error, GenericFailure());
    });

    test('transforms data into a double value', () {
    final result =
    DataResult.success('yo').then((data) => DataResult.success(1.34));

    expect(result.isSuccess, true);
    expect(result.data, 1.34);
    });

    test('transforms data into a failure', () {
    final result = DataResult.success<String>('yo')
    .then((data) => DataResult.failure(APIFailure()));

    expect(result.isSuccess, false);
    expect(result.error, APIFailure());
    });
    });

    group('DataResult map', () {
    test('bubbles up failure instead of transforming the success value', () {
    final result =
    DataResult.failure<String>(GenericFailure()).map((data) => 1.34);

    expect(result.isFailure, true);
    expect(result.error, GenericFailure());
    });

    test('transforms data into a double value', () {
    final result = DataResult.success('yo').map((data) => 1.34);

    expect(result.isSuccess, true);
    expect(result.data, 1.34);
    });

    test('can only transform data into a failure if it is the new data type',
    () {
    final result = DataResult.success('yo').map((data) => APIFailure());

    expect(result.isSuccess, true);
    expect(result.data, APIFailure());
    });
    });
    }
  7. CassiusPacheco revised this gist Feb 23, 2021. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions data_result.dart
    Original file line number Diff line number Diff line change
    @@ -35,12 +35,12 @@ abstract class DataResult<S> extends Equatable {
    Failure get error => fold<Failure>(
    (error) => error,
    (data) => throw Exception(
    'No error found. Check `isFailure()` before calling `error`'));
    'No error found. Check `isFailure` before calling `error`'));

    /// Get [data] value, may throw an exception when the value is [error]
    S get data => fold<S>(
    (error) => throw Exception(
    'No data found. Check `isSuccess()` before calling `data`'),
    'No data found. Check `isSuccess` before calling `data`'),
    (data) => data);

    /// Returns `true` if the object is of the `SuccessResult` type, which means
    @@ -51,12 +51,12 @@ abstract class DataResult<S> extends Equatable {
    /// `error` will return a valid result.
    bool get isFailure => this is _FailureResult<S>;

    /// Returns `data` if `isSuccess()` returns `true`, otherwise it returns
    /// 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.
    /// `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
  8. CassiusPacheco revised this gist Feb 23, 2021. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion data_result.dart
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,8 @@
    // The code below was inspired by https://github.com/avdosev/either_dart/blob/master/lib/src/either.dart
    // The code below was inspired by my swift implementation https://gist.github.com/CassiusPacheco/4378d30d69316e4a6ba28a0c3af72628
    // and Avdosev's Dart Either https://github.com/avdosev/either_dart/blob/master/lib/src/either.dart

    import 'package:equatable/equatable.dart';

    abstract class Failure extends Equatable implements Exception {
    @override
    String toString() => '$runtimeType Exception';
  9. CassiusPacheco revised this gist Feb 23, 2021. 1 changed file with 36 additions and 34 deletions.
    70 changes: 36 additions & 34 deletions data_result.dart
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,10 @@
    // The code below was inspired by https://github.com/avdosev/either_dart/blob/master/lib/src/either.dart

    import 'package:equatable/equatable.dart';
    abstract class Failure extends Equatable implements Exception {
    @override
    String toString() => '$runtimeType Exception';

    abstract class Failure extends Equatable {
    @override
    List<Object> get props => [];
    }
    @@ -12,35 +14,44 @@ class GenericFailure extends Failure {}

    class APIFailure extends Failure {}

    /// This abstraction contains either a success data of generic type `S` or a
    /// failure error of type `Failure` as its result.
    ///
    /// `data` property must only be retrieved when `DataResult` was constructed by
    /// using `DataResult.success(value)`. It can be validated by calling
    /// `isSuccess` first. Alternatively, `dataOrElse` can be used instead since it
    /// ensures a valid value is returned in case the result is a failure.
    ///
    /// `error` must only be retrieved when `DataResult` was constructed by using
    /// `DataResult.failure(error)`. It can be validated by calling `isFailure`
    /// first.
    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'));
    'No error found. Check `isFailure()` before calling `error`'));

    /// 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'),
    'No data found. Check `isSuccess()` before calling `data`'),
    (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>;
    bool get 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>;
    bool get isFailure => this is _FailureResult<S>;

    /// Returns `data` if `isSuccess()` returns `true`, otherwise it returns
    /// `other`.
    S dataOrElse(S other) => isSuccess() ? data : 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.
    @@ -66,73 +77,64 @@ abstract class DataResult<S> extends Equatable {
    /// `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];
    List<Object> get props => [if (isSuccess) data else error];
    }

    /// Success implementation of `DataResult`. It contains `data`.
    /// Success implementation of `DataResult`. It contains `data`. It's abstracted
    /// away by `DataResult`. It shouldn't be used directly in the app.
    class _SuccessResult<S> extends DataResult<S> {
    final S value;
    final S _value;

    const _SuccessResult(this.value);
    _SuccessResult(this._value);

    @override
    _SuccessResult<T> either<T>(
    Failure Function(Failure error) fnFailure, T Function(S data) fnData) {
    return _SuccessResult<T>(fnData(value));
    return _SuccessResult<T>(fnData(_value));
    }

    @override
    DataResult<T> then<T>(DataResult<T> Function(S data) fnData) {
    return fnData(value);
    return fnData(_value);
    }

    @override
    _SuccessResult<T> map<T>(T Function(S data) fnData) {
    return _SuccessResult<T>(fnData(value));
    return _SuccessResult<T>(fnData(_value));
    }

    @override
    T fold<T>(T Function(Failure error) fnFailure, T Function(S data) fnData) {
    return fnData(value);
    return fnData(_value);
    }
    }

    /// Failure implementation of `DataResult`. It contains `error`.
    /// Failure implementation of `DataResult`. It contains `error`. It's
    /// abstracted away by `DataResult`. It shouldn't be used directly in the app.
    class _FailureResult<S> extends DataResult<S> {
    final Failure value;
    final Failure _value;

    const _FailureResult(this.value);
    _FailureResult(this._value);

    @override
    _FailureResult<T> either<T>(
    Failure Function(Failure error) fnFailure, T Function(S data) fnData) {
    return _FailureResult<T>(fnFailure(value));
    return _FailureResult<T>(fnFailure(_value));
    }

    @override
    _FailureResult<T> map<T>(T Function(S data) fnData) {
    return _FailureResult<T>(value);
    return _FailureResult<T>(_value);
    }

    @override
    _FailureResult<T> then<T>(DataResult<T> Function(S data) fnData) {
    return _FailureResult<T>(value);
    return _FailureResult<T>(_value);
    }

    @override
    T fold<T>(T Function(Failure error) fnFailure, T Function(S data) fnData) {
    return fnFailure(value);
    return fnFailure(_value);
    }
    }

    class Empty extends Equatable {
    @override
    List<Object> get props => [];
    }
  10. CassiusPacheco revised this gist Feb 23, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion data_result.dart
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    // The code below was inspired on https://github.com/avdosev/either_dart/blob/master/lib/src/either.dart
    // The code below was inspired by https://github.com/avdosev/either_dart/blob/master/lib/src/either.dart

    import 'package:equatable/equatable.dart';

  11. CassiusPacheco created this gist Feb 23, 2021.
    138 changes: 138 additions & 0 deletions data_result.dart
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,138 @@
    // 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 => [];
    }