// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

part of 'either_base.dart';

extension FutureResultExtension<T, E> on Future<Result<T, E>> {
  /// Represents the left side of [Result] class.
  Future<bool> get isOk => then((result) => result.isOk);

  /// Represents the right side of [Result] class.
  Future<bool> get isErr => then((result) => result.isErr);

  /// Get [U] value, may throw an exception.
  Future<U> unwrap<U>() => then((result) => result.unwrap<U>());

  /// Get **async** [U] value, may throw an exception.
  ///
  /// With nullable, `Future(Future(U)) == Future(U)`
  Future<U> unwrapAsync<U>() => then((result) => result.unwrapAsync<U>());

  /// Fold [Ok] and [Err] into the value of one type
  Future<U> fold<U>(
    U Function(T value) valueTransformer,
    U Function(E error) errorTransformer,
  ) =>
      then((result) => result.fold(valueTransformer, errorTransformer));

  /// Fold [Ok] and [Err] **asynchronously** into the value of one type
  Future<U> foldAsync<U>(
    Future<U> Function(T value) valueTransformer,
    Future<U> Function(E error) errorTransformer,
  ) =>
      then((result) => result.foldAsync(valueTransformer, errorTransformer));

  /// Swap [Ok] and [Err]
  Future<Result<E, T>> swap() => then((result) => result.swap());

  /// Returns [res] if the [Result] is [Ok], otherwise returns
  /// the [Err] value of this.
  Future<Result<U, E>> and<U>(Result<U, E> res) =>
      then((result) => result.and(res));

  /// Returns [res] if the [Result] is [Err], otherwise returns
  /// the [Ok] value of this.
  Future<Result<T, F>> or<F>(Result<T, F> res) =>
      then((result) => result.or(res));

  /// Returns true if the result is an [Ok] or [Err] value containing
  /// the given value/error.
  Future<bool> contains<U>(U x) => then((result) => result.contains(x));

  /// Returns the contained [Ok] value. Throw [ResultException] on [Err] with
  /// its content.
  Future<T> expect(String msg) => then((result) => result.expect(msg));

  /// Returns the contained [Err] value. Throw [ResultException] on [Ok] with
  /// its content.
  Future<E> expectErr(String msg) => then((result) => result.expectErr(msg));

  /// Maps a [Result<T, E>] to [Result<U, E>] by applying a function to a
  /// contained [Ok] value, leaving an [Err] value untouched.
  Future<Result<U, E>> map<U>(U Function(T value) mapper) =>
      then((result) => result.map(mapper));

  /// Maps a [Result<T, E>] to [Result<U, E>] by applying an **async** function
  /// to a contained [Ok] value, leaving an [Err] value untouched.
  Future<Result<U, E>> mapAsync<U>(Future<U> Function(T value) mapper) =>
      then((result) => result.mapAsync(mapper));

  /// Maps a [Result<T, E>] to [Result<T, F>] by applying a function to a
  /// contained [Err] value, leaving an [Ok] value untouched.
  Future<Result<T, F>> mapErr<F>(F Function(E error) mapper) =>
      then((result) => result.mapErr(mapper));

  /// Maps a [Result<T, E>] to [Result<U, E>] by applying an **async** function
  /// to a contained [Err] value, leaving an [Ok] value untouched.
  Future<Result<T, F>> mapErrAsync<F>(Future<F> Function(E error) mapper) =>
      then((result) => result.mapErrAsync(mapper));

  /// Transforms a [Result<T, E>] to [Result<U, F>] by applying functions to
  /// contained [Ok] and [Err] values.
  Future<Result<U, F>> either<U, F>(
    U Function(T value) valueTransformer,
    F Function(E error) errorTransformer,
  ) =>
      then((result) => result.either(valueTransformer, errorTransformer));

  /// Transforms a [Result<T, E>] to [Result<U, F>] by applying **async**
  /// functions to contained [Ok] and [Err] values.
  Future<Result<U, F>> eitherAsync<U, F>(
    Future<U> Function(T value) valueTransformer,
    Future<F> Function(E error) errorTransformer,
  ) =>
      then((result) => result.eitherAsync(valueTransformer, errorTransformer));
}
