All Downloads are FREE. Search and download functionalities are using the official Maven repository.

next.utils.feither.scala Maven / Gradle / Ivy

package otoroshi.next.utils

import otoroshi.utils.syntax.implicits._

import scala.concurrent.{ExecutionContext, Future}

/**
 * A very simple and straightforward implementation of EitherT where T is Future (Future[Either[Err, Res]]) to avoid using Cats
 */
class FEither[L, R](val value: Future[Either[L, R]]) {

  @inline
  def map[S](f: R => S)(implicit executor: ExecutionContext): FEither[L, S] = {
    val result = value.map {
      case Right(r)    => Right(f(r))
      case Left(error) => Left(error)
    }
    new FEither[L, S](result)
  }

  @inline
  def flatMap[S](f: R => FEither[L, S])(implicit executor: ExecutionContext): FEither[L, S] = {
    val result = value.flatMap {
      case Right(r)    => f(r).value
      case Left(error) => Left(error).vfuture
    }
    new FEither(result)
  }

  @inline
  def leftMap[S](f: L => S)(implicit executor: ExecutionContext): FEither[S, R] = {
    val result = value.map {
      case Right(r)    => Right(r)
      case Left(error) => Left(f(error))
    }
    new FEither[S, R](result)
  }

  @inline
  def leftFlatMap[S](f: L => FEither[S, R])(implicit executor: ExecutionContext): FEither[S, R] = {
    val result = value.flatMap {
      case Right(r)    => Right(r).vfuture
      case Left(error) => f(error).value
    }
    new FEither(result)
  }

  @inline
  def mapBoth[S, T](f: R => S, f2: L => T)(implicit executor: ExecutionContext): FEither[T, S] = {
    val result = value.map {
      case Right(r)    => Right(f(r))
      case Left(error) => Left(f2(error))
    }
    new FEither[T, S](result)
  }

  @inline
  def flatMapBoth[S, T](f: R => FEither[T, S], f2: L => FEither[T, S])(implicit
      executor: ExecutionContext
  ): FEither[T, S] = {
    val result = value.flatMap {
      case Right(r)    => f(r).value
      case Left(error) => f2(error).value
    }
    new FEither(result)
  }

  @inline
  def ensure(onFailure: => L)(f: R => Boolean)(implicit executor: ExecutionContext): FEither[L, R] = {
    val result = value.flatMap {
      case e @ Right(r) if f(r) => e.vfuture
      case Right(_)             => Left(onFailure).vfuture
      case l @ Left(_)          => l.vfuture
    }
    new FEither[L, R](result)
  }

  @inline
  def fold[S](f1: L => S, f2: R => S)(implicit executor: ExecutionContext): FEither[L, S] = {
    val result = value.map {
      case Right(r)    => Right(f2(r))
      case Left(error) => Right(f1(error))
    }
    new FEither[L, S](result)
  }

  @inline
  def foldF[S](f1: L => Future[S], f2: R => Future[S])(implicit executor: ExecutionContext): FEither[L, S] = {
    val result = value.flatMap {
      case Right(r)    => f2(r).map(r => Right(r))
      case Left(error) => f1(error).map(r => Right(r))
    }
    new FEither[L, S](result)
  }

  @inline
  def isLeft(implicit executor: ExecutionContext): Future[Boolean] = value.map {
    case Right(r)    => false
    case Left(error) => true
  }

  @inline
  def isRight(implicit executor: ExecutionContext): Future[Boolean] = value.map {
    case Right(r)    => true
    case Left(error) => false
  }

  @inline
  def swap(implicit executor: ExecutionContext): FEither[R, L] = {
    val result = value.map {
      case Right(r)    => Left(r)
      case Left(error) => Right(error)
    }
    new FEither[R, L](result)
  }

  // def filter(f: R => Boolean)(implicit executor: ExecutionContext): FEither[String, R] = {
  //   val result = value.flatMap {
  //     case e @ Right(r) if f(r) => e.vfuture
  //     case Right(_) => Left("predicate does not match").vfuture
  //     case l @ Left(_)  => l.vfuture
  //   }
  //   new FEither[String, R](result)
  // }
}

object FEither {

  @inline
  def apply[L, R](value: Future[Either[L, R]]): FEither[L, R]       = new FEither[L, R](value)
  @inline
  def fromEitherT[L, R](value: Future[Either[L, R]]): FEither[L, R] = new FEither[L, R](value)

  @inline
  def fromEither[L, R](value: Either[L, R]): FEither[L, R] = new FEither[L, R](value.vfuture)
  @inline
  def apply[L, R](value: Either[L, R]): FEither[L, R]      = fromEither(value)

  @inline
  def leftT[L, R](value: L): FEither[L, R]                                         = new FEither[L, R](Left(value).vfuture)
  @inline
  def left[L, R](value: L): FEither[L, R]                                          = new FEither[L, R](Left(value).vfuture)
  @inline
  def fleft[L, R](value: Future[L])(implicit ec: ExecutionContext): FEither[L, R]  =
    new FEither[L, R](value.map(v => Left(v)))
  @inline
  def rightT[L, R](value: R): FEither[L, R]                                        = new FEither[L, R](Right(value).vfuture)
  @inline
  def right[L, R](value: R): FEither[L, R]                                         = new FEither[L, R](Right(value).vfuture)
  @inline
  def fright[L, R](value: Future[R])(implicit ec: ExecutionContext): FEither[L, R] =
    new FEither[L, R](value.map(v => Right(v)))
  @inline
  def liftF[L, R](value: Future[R])(implicit ec: ExecutionContext): FEither[L, R]  = fright(value)

  @inline
  def fromOption[L, R](value: Option[R], err: L): FEither[L, R]                                         = value match {
    case None    => new FEither[L, R](Left(err).vfuture)
    case Some(v) => new FEither[L, R](Right(v).vfuture)
  }
  @inline
  def fromOptionF[L, R](value: Future[Option[R]], err: L)(implicit ec: ExecutionContext): FEither[L, R] =
    new FEither[L, R](value.map {
      case None    => Left(err)
      case Some(v) => Right(v)
    })
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy