diff --git a/main/src/io/github/iltotore/iron/MapLogic.scala b/main/src/io/github/iltotore/iron/MapLogic.scala new file mode 100644 index 00000000..304b4b94 --- /dev/null +++ b/main/src/io/github/iltotore/iron/MapLogic.scala @@ -0,0 +1,26 @@ +package io.github.iltotore.iron + +import scala.concurrent.Future + +/** + * A typeclass providing a `map` method. Mainly used to abstract over Cats and ZIO Prelude. + * + * @tparam F the wrapper type + */ +trait MapLogic[F[_]]: + + def map[A, B](wrapper: F[A], f: A => B): F[B] + +object MapLogic: + + given MapLogic[IterableOnce] with + + def map[A, B](wrapper: IterableOnce[A], f: A => B): IterableOnce[B] = wrapper.iterator.map(f) + + given [L]: MapLogic[[x] =>> Either[L, x]] with + + override def map[A, B](wrapper: Either[L, A], f: A => B): Either[L, B] = wrapper.map(f) + + given MapLogic[Future] with + + override def map[A, B](wrapper: Future[A], f: A => B): Future[B] = wrapper.map(f) \ No newline at end of file diff --git a/main/src/io/github/iltotore/iron/package.scala b/main/src/io/github/iltotore/iron/package.scala index bfd6d39c..60c7b4bd 100644 --- a/main/src/io/github/iltotore/iron/package.scala +++ b/main/src/io/github/iltotore/iron/package.scala @@ -5,7 +5,8 @@ import io.github.iltotore.iron.macros import scala.Console.{CYAN, RESET} import scala.compiletime.{codeOf, error, summonInline} import scala.reflect.TypeTest -import scala.util.NotGiven +import scala.util.{boundary, NotGiven} +import scala.util.boundary.break /** * The main package of Iron. Contains: @@ -107,4 +108,35 @@ extension [F[_], A](wrapper: F[A]) * @return constrained values, without performing constraint checks. * @see [[assume]], [[autoRefine]], [[refineUnsafe]]. */ - inline def assumeAll[B]: F[A :| B] = wrapper \ No newline at end of file + inline def assumeAll[B]: F[A :| B] = wrapper + + /** + * Refine the given value(s) at runtime. + * + * @param constraint the constraint to test with the value to refine. + * @return the given values as [[IronType]]. + * @throws an [[IllegalArgumentException]] if the constraint is not satisfied. + * @see [[refineUnsafe]]. + */ + inline def refineAllUnsafe[B](using mapLogic: MapLogic[F], inline constraint: Constraint[A, B]): F[A :| B] = + mapLogic.map(wrapper, _.refineUnsafe[B]) + + + inline def refineAllEither[B](using mapLogic: MapLogic[F], inline constraint: Constraint[A, B]): Either[String, F[A :| B]] = + boundary: + Right(mapLogic.map( + wrapper, + _.refineEither[B] match + case Right(value) => value + case Left(error) => break(Left(error)) + )) + + inline def refineAllOption[B](using mapLogic: MapLogic[F], inline constraint: Constraint[A, B]): Option[F[A :| B]] = + boundary: + Some(mapLogic.map( + wrapper, + _.refineOption[B] match + case Some(value) => value + case None => break(None) + )) +