From 1cf0fe18c3898c1f60947c0f66d3ed04a91a9480 Mon Sep 17 00:00:00 2001 From: Mateusz Kubuszok Date: Thu, 21 Dec 2023 20:31:44 +0100 Subject: [PATCH 1/6] Add new utilities to partial.Result, improve existing Scaladocs a bit --- CONTRIBUTING.md | 2 +- README.md | 2 +- .../chimney/partial/syntax/package.scala | 20 ++ .../chimney/partial/syntax/syntax.scala | 16 ++ .../chimney/PartialTransformer.scala | 40 +++- .../scala/io/scalaland/chimney/Patcher.scala | 27 ++- .../io/scalaland/chimney/Transformer.scala | 35 ++- .../dsl/ImplicitTransformerPreference.scala | 6 + .../chimney/dsl/PatcherFlagsDsl.scala | 20 +- .../chimney/dsl/TransformerFlagsDsl.scala | 6 +- .../chimney/internal/runtime/IsFunction.scala | 5 +- .../scalaland/chimney/partial/AsResult.scala | 28 +++ .../io/scalaland/chimney/partial/Path.scala | 4 + .../chimney/partial/PathElement.scala | 8 + .../io/scalaland/chimney/partial/Result.scala | 206 ++++++++++++------ 15 files changed, 324 insertions(+), 101 deletions(-) create mode 100644 chimney/src/main/scala-2/io/scalaland/chimney/partial/syntax/package.scala create mode 100644 chimney/src/main/scala-3/io/scalaland/chimney/partial/syntax/syntax.scala create mode 100644 chimney/src/main/scala/io/scalaland/chimney/partial/AsResult.scala diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86fda2e14..73816d42b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,7 +76,7 @@ Information about Scala macros can be found on: * [EPFL papers](https://infoscience.epfl.ch/search?ln=en&as=1&m1=p&p1=macros&f1=keyword&op1=a&m2=p&p2=scala&f2=&op2=a&m3=a&p3=&f3=&dt=&d1d=&d1m=&d1y=&d2d=&d2m=&d2y=&rm=&action_search=Search&sf=title&so=a&rg=10&c=Infoscience&of=hb) Very basic introduction can be found in [design doc](DESIGN.md) and in the -[Under the hood](https://chimney.readthedocs.io/en/stable/under-the-hood/) section of the documentation. +[Under the hood](https://chimney.readthedocs.io/under-the-hood/) section of the documentation. From then on we suggest looking at tests, and using`.enableMacrosLogging` to see how some branches are triggered. If still at doubt, you can ask us on GH discussions. diff --git a/README.md b/README.md index 55a0d0d54..b1681e53b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ If you are looking to up-to-date artifacts versions ready to copy-paste into you ## Contribution A way to get started is described in [CONTRIBUTING.md](CONTRIBUTING.md) and the general overview of the architecture -is given in [DESIGN.md](DESIGN.md) and in [Under the hood](https://chimney.readthedocs.io/en/stable/under-the-hood/) +is given in [DESIGN.md](DESIGN.md) and in [Under the hood](https://chimney.readthedocs.io/under-the-hood/) section of the documentation. ## Thanks diff --git a/chimney/src/main/scala-2/io/scalaland/chimney/partial/syntax/package.scala b/chimney/src/main/scala-2/io/scalaland/chimney/partial/syntax/package.scala new file mode 100644 index 000000000..795479b88 --- /dev/null +++ b/chimney/src/main/scala-2/io/scalaland/chimney/partial/syntax/package.scala @@ -0,0 +1,20 @@ +package io.scalaland.chimney.partial + +// TODO: docs + +package object syntax { + + implicit class PartialResultOptionOps[A](private val option: Option[A]) extends AnyVal { + + def orErrorAsResult(onEmpty: => Error): Result[A] = Result.fromOptionOrError(option, onEmpty) + + def orStringAsResult(onEmpty: => String): Result[A] = Result.fromOptionOrString(option, onEmpty) + + def orThrowableAsResult(onEmpty: => Throwable): Result[A] = Result.fromOptionOrThrowable(option, onEmpty) + } + + implicit class PartialResultAsResultOps[F[_], A](private val fa: F[A]) extends AnyVal { + + def asResult(implicit F: AsResult[F]): Result[A] = F.asResult(fa) + } +} diff --git a/chimney/src/main/scala-3/io/scalaland/chimney/partial/syntax/syntax.scala b/chimney/src/main/scala-3/io/scalaland/chimney/partial/syntax/syntax.scala new file mode 100644 index 000000000..b681b3779 --- /dev/null +++ b/chimney/src/main/scala-3/io/scalaland/chimney/partial/syntax/syntax.scala @@ -0,0 +1,16 @@ +package io.scalaland.chimney.partial.syntax + +import io.scalaland.chimney.partial.{AsResult, Error, Result} + +// TODO: docs + +extension [A](option: Option[A]) + + def orErrorAsResult(onEmpty: => Error): Result[A] = Result.fromOptionOrError(option, onEmpty) + + def orStringAsResult(onEmpty: => String): Result[A] = Result.fromOptionOrString(option, onEmpty) + + def orThrowableAsResult(onEmpty: => Throwable): Result[A] = Result.fromOptionOrThrowable(option, onEmpty) + +extension [F[_], A](fa: F[A]) + def asResult(using F: AsResult[F]): Result[A] = F.asResult(fa) diff --git a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala index f94c1c968..a6874a93f 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala @@ -3,10 +3,12 @@ package io.scalaland.chimney import io.scalaland.chimney.dsl.{PartialTransformerDefinition, TransformerDefinitionCommons} import io.scalaland.chimney.internal.runtime.{TransformerCfg, TransformerFlags} -/** Type class expressing partial transformation between - * source type `From` and target type `To`, with the ability +/** Type class expressing partial transformation between source type `From` and target type `To`, with the ability * of reporting path-annotated transformation error(s). * + * @see [[https://chimney.readthedocs.io/supported-transformations/]] + * @see [[https://chimney.readthedocs.io/supported-transformations/#total-transformers-vs-partialtransformers]] + * * @tparam From type of input value * @tparam To type of output value * @@ -17,8 +19,9 @@ trait PartialTransformer[From, To] extends PartialTransformer.AutoDerived[From, /** Run transformation using provided value as a source. * * @param src source value - * @param failFast should fail as early as the first set of errors appear - * @return result of transformation + * @param failFast whether the transformerion should return as early as the first set of errors appear (`true`), + * or should it attempt to convert what it can and then aggregate all errors (`false`) + * @return [[io.scalaland.chimney.partial.Result]] of the transformation * * @since 0.7.0 */ @@ -27,7 +30,7 @@ trait PartialTransformer[From, To] extends PartialTransformer.AutoDerived[From, /** Run transformation using provided value as a source in error accumulation mode. * * @param src source value - * @return result of transformation + * @return [[io.scalaland.chimney.partial.Result]] of the transformation * * @since 0.7.0 */ @@ -37,7 +40,7 @@ trait PartialTransformer[From, To] extends PartialTransformer.AutoDerived[From, /** Run transformation using provided value as a source in short-circuit (fail fast) mode. * * @param src source value - * @return result of transformation + * @return [[io.scalaland.chimney.partial.Result]] of the transformation * * @since 0.7.0 */ @@ -45,6 +48,13 @@ trait PartialTransformer[From, To] extends PartialTransformer.AutoDerived[From, transform(src, failFast = true) } +/** Companion of [[io.scalaland.chimney.PartialTransformer]]. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/]] + * @see [[https://chimney.readthedocs.io/supported-transformations/#total-transformers-vs-partialtransformers]] + * + * @since 0.7.0 + */ object PartialTransformer extends PartialTransformerCompanionPlatform { /** Construct ad-hoc instance of partial transformer from transforming function returning partial result. @@ -93,8 +103,8 @@ object PartialTransformer extends PartialTransformerCompanionPlatform { def liftTotal[From, To](t: Transformer[From, To]): PartialTransformer[From, To] = fromFunction[From, To](t.transform) - /** Creates an empty [[io.scalaland.chimney.dsl.PartialTransformerDefinition]] that - * you can customize to derive [[io.scalaland.chimney.PartialTransformer]]. + /** Creates an empty [[io.scalaland.chimney.dsl.PartialTransformerDefinition]] that you can customize to derive + * [[io.scalaland.chimney.PartialTransformer]]. * * @see [[io.scalaland.chimney.dsl.PartialTransformerDefinition]] for available settings * @@ -107,9 +117,23 @@ object PartialTransformer extends PartialTransformerCompanionPlatform { def define[From, To]: PartialTransformerDefinition[From, To, TransformerCfg.Empty, TransformerFlags.Default] = new PartialTransformerDefinition(TransformerDefinitionCommons.emptyRuntimeDataStore) + /** Type class used when you want o allow using automatically derived transformations. + * + * When we want to only allow semiautomatically derived/manually defined instances you should use + * [[io.scalaland.chimney.PartialTransformer]]. + * + * @see [[https://chimney.readthedocs.io/cookbook/#automatic-semiautomatic-and-inlined-derivation]] for more details + * + * @tparam From type of input value + * @tparam To type of output value + * + * @since 0.8.0 + */ trait AutoDerived[From, To] { def transform(src: From, failFast: Boolean): partial.Result[To] } + + /** @since 0.8.0 */ object AutoDerived extends PartialTransformerAutoDerivedCompanionPlatform { implicit def liftTotal[From, To](implicit total: Transformer[From, To]): AutoDerived[From, To] = diff --git a/chimney/src/main/scala/io/scalaland/chimney/Patcher.scala b/chimney/src/main/scala/io/scalaland/chimney/Patcher.scala index faea0a499..e4e65e00a 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/Patcher.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/Patcher.scala @@ -4,6 +4,8 @@ import io.scalaland.chimney.dsl.PatcherDefinition import io.scalaland.chimney.internal.runtime.{PatcherCfg, PatcherFlags} /** Type class definition that wraps patching behavior. + * + * @see [[https://chimney.readthedocs.io/supported-patching/]] * * @tparam A type of object to apply patch to * @tparam Patch type of patch object @@ -23,11 +25,16 @@ trait Patcher[A, Patch] extends Patcher.AutoDerived[A, Patch] { def patch(obj: A, patch: Patch): A } -/** @since 0.1.3 */ +/** Companion of [[io.scalaland.chimney.Patcher]]. + * + * @see [[https://chimney.readthedocs.io/supported-patching/]] + * + * @since 0.1.3 + */ object Patcher extends PatcherCompanionPlatform { - /** Creates an empty [[io.scalaland.chimney.dsl.PatcherDefinition]] that - * you can customize to derive [[io.scalaland.chimney.Patcher]]. + /** Creates an empty [[io.scalaland.chimney.dsl.PatcherDefinition]] that you can customize to derive + * [[io.scalaland.chimney.Patcher]]. * * @see [[io.scalaland.chimney.dsl.PatcherDefinition]] for available settings * @@ -40,8 +47,22 @@ object Patcher extends PatcherCompanionPlatform { def define[A, Patch]: PatcherDefinition[A, Patch, PatcherCfg.Empty, PatcherFlags.Default] = new PatcherDefinition + /** Type class used when you want o allow using automatically derived patchings. + * + * When we want to only allow semiautomatically derived/manually defined instances you should use + * [[io.scalaland.chimney.Patcher]]. + * + * @see [[https://chimney.readthedocs.io/cookbook/#automatic-semiautomatic-and-inlined-derivation]] for more details + * + * @tparam A type of object to apply patch to + * @tparam Patch type of patch object + * + * @since 0.8.0 + */ trait AutoDerived[A, Patch] { def patch(obj: A, patch: Patch): A } + + /** @since 0.8.0 */ object AutoDerived extends PatcherAutoDerivedCompanionPlatform } diff --git a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala index ed2f9552e..594492823 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala @@ -3,8 +3,10 @@ package io.scalaland.chimney import io.scalaland.chimney.dsl.{PartialTransformerDefinition, TransformerDefinition, TransformerDefinitionCommons} import io.scalaland.chimney.internal.runtime.{TransformerCfg, TransformerFlags} -/** Type class expressing total transformation between - * source type `From` and target type `To`. +/** Type class expressing total transformation between source type `From` and target type `To`. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/]] + * @see [[https://chimney.readthedocs.io/supported-transformations/#total-transformers-vs-partialtransformers]] * * @tparam From type of input value * @tparam To type of output value @@ -23,10 +25,17 @@ trait Transformer[From, To] extends Transformer.AutoDerived[From, To] { def transform(src: From): To } +/** Companion of [[io.scalaland.chimney.Transformer]]. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/]] + * @see [[https://chimney.readthedocs.io/supported-transformations/#total-transformers-vs-partialtransformers]] + * + * @since 0.2.0 + */ object Transformer extends TransformerCompanionPlatform { - /** Creates an empty [[io.scalaland.chimney.dsl.TransformerDefinition]] that - * you can customize to derive [[io.scalaland.chimney.Transformer]]. + /** Creates an empty [[io.scalaland.chimney.dsl.TransformerDefinition]] that you can customize to derive + * [[io.scalaland.chimney.Transformer]]. * * @see [[io.scalaland.chimney.dsl.TransformerDefinition]] for available settings * @@ -39,8 +48,8 @@ object Transformer extends TransformerCompanionPlatform { def define[From, To]: TransformerDefinition[From, To, TransformerCfg.Empty, TransformerFlags.Default] = new TransformerDefinition(TransformerDefinitionCommons.emptyRuntimeDataStore) - /** Creates an empty [[io.scalaland.chimney.dsl.PartialTransformerDefinition]] that - * you can customize to derive [[io.scalaland.chimney.PartialTransformer]]. + /** Creates an empty [[io.scalaland.chimney.dsl.PartialTransformerDefinition]] that you can customize to derive + * [[io.scalaland.chimney.PartialTransformer]]. * * @see [[io.scalaland.chimney.dsl.PartialTransformerDefinition]] for available settings * @@ -53,8 +62,22 @@ object Transformer extends TransformerCompanionPlatform { def definePartial[From, To]: PartialTransformerDefinition[From, To, TransformerCfg.Empty, TransformerFlags.Default] = new PartialTransformerDefinition(TransformerDefinitionCommons.emptyRuntimeDataStore) + /** Type class used when you want o allow using automatically derived transformations. + * + * When we want to only allow semiautomatically derived/manually defined instances you should use + * [[io.scalaland.chimney.Transformer]]. + * + * @see [[https://chimney.readthedocs.io/cookbook/#automatic-semiautomatic-and-inlined-derivation]] for more details + * + * @tparam From type of input value + * @tparam To type of output value + * + * @since 0.8.0 + */ trait AutoDerived[From, To] { def transform(src: From): To } + + /** @since 0.8.0 */ object AutoDerived extends TransformerAutoDerivedCompanionPlatform } diff --git a/chimney/src/main/scala/io/scalaland/chimney/dsl/ImplicitTransformerPreference.scala b/chimney/src/main/scala/io/scalaland/chimney/dsl/ImplicitTransformerPreference.scala index 70bed2e66..8450c4ddb 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/dsl/ImplicitTransformerPreference.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/dsl/ImplicitTransformerPreference.scala @@ -1,18 +1,24 @@ package io.scalaland.chimney.dsl /** Whether derivation should prefer total or partial transformers if both are provided for some field transformation. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/#resolving-priority-of-implicit-total-vs-partial-transformers]] for more details * * @since 0.7.0 */ sealed abstract class ImplicitTransformerPreference /** Tell the derivation to prefer total transformers. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/#resolving-priority-of-implicit-total-vs-partial-transformers]] for more details * * @since 0.7.0 */ case object PreferTotalTransformer extends ImplicitTransformerPreference /** Tell the derivation to prefer partial transformers. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/#resolving-priority-of-implicit-total-vs-partial-transformers]] for more details * * @since 0.7.0 */ diff --git a/chimney/src/main/scala/io/scalaland/chimney/dsl/PatcherFlagsDsl.scala b/chimney/src/main/scala/io/scalaland/chimney/dsl/PatcherFlagsDsl.scala index 1cca484da..bdbe9d167 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/dsl/PatcherFlagsDsl.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/dsl/PatcherFlagsDsl.scala @@ -9,12 +9,10 @@ import io.scalaland.chimney.internal.runtime.PatcherFlags */ private[dsl] trait PatcherFlagsDsl[UpdateFlag[_ <: PatcherFlags], Flags <: PatcherFlags] { - /** In case when both object to patch and patch value contain field - * of type `Option[T]`, this option allows to treat `None` value in - * patch like the value was not provided. + /** In case when both object to patch and patch value contain field of type [[scala.Option]], this option allows + * to treat [[scala.None]] value in patch as if the value was not provided. * - * By default, when `None` is delivered in patch, Chimney clears - * the value of such field on patching. + * By default, when [[scala.None]] is delivered in patch, Chimney clears the value of such field on patching. * * @see [[https://chimney.readthedocs.io/supported-patching/#treating-none-as-no-update-instead-of-set-to-none]] for more details * @@ -25,7 +23,7 @@ private[dsl] trait PatcherFlagsDsl[UpdateFlag[_ <: PatcherFlags], Flags <: Patch def ignoreNoneInPatch: UpdateFlag[Enable[IgnoreNoneInPatch, Flags]] = enableFlag[IgnoreNoneInPatch] - /** Then there Option is patching Option, on None value will be cleared. + /** Then there [[scala.Option]] is patching [[scala.Option]], on [[scala.None]] value will be cleared. * * @see [[https://chimney.readthedocs.io/supported-patching/#treating-none-as-no-update-instead-of-set-to-none]] for more details * @@ -36,13 +34,11 @@ private[dsl] trait PatcherFlagsDsl[UpdateFlag[_ <: PatcherFlags], Flags <: Patch def clearOnNoneInPatch: UpdateFlag[Disable[IgnoreNoneInPatch, Flags]] = disableFlag[IgnoreNoneInPatch] - /** In case that patch object contains a redundant field (i.e. field that - * is not present in patched object type), this option enables ignoring - * value of such fields and generate patch successfully. + /** In case that patch object contains a redundant field (i.e. field that is not present in patched object type), this + * option enables ignoring value of such fields and generate patch successfully. * - * By default, when Chimney detects a redundant field in patch object, it - * fails the compilation in order to prevent silent oversight of field name - * typos. + * By default, when Chimney detects a redundant field in patch object, it fails the compilation in order to prevent + * silent oversight of field name typos. * * @see [[https://chimney.readthedocs.io/supported-patching/#ignoring-fields-in-patches]] for more details * diff --git a/chimney/src/main/scala/io/scalaland/chimney/dsl/TransformerFlagsDsl.scala b/chimney/src/main/scala/io/scalaland/chimney/dsl/TransformerFlagsDsl.scala index 2521a4686..fc9025884 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/dsl/TransformerFlagsDsl.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/dsl/TransformerFlagsDsl.scala @@ -5,7 +5,8 @@ import io.scalaland.chimney.internal.runtime.TransformerFlags import scala.annotation.unused -/** Type-level representation of derivation flags which can be enabled/disabled for a specific transformation or globally. +/** Type-level representation of derivation flags which can be enabled/disabled for a specific transformation or + * globally. * * @since 0.6.0 */ @@ -13,7 +14,8 @@ private[dsl] trait TransformerFlagsDsl[UpdateFlag[_ <: TransformerFlags], Flags /** Enable lookup in definitions inherited from supertype. * - * By default only values defined directly in the type are considered. With this flag supertype methods would not be filtered out + * By default only values defined directly in the type are considered. With this flag supertype methods would not be + * filtered out * * @see [[https://chimney.readthedocs.io/supported-transformations/#reading-from-inherited-valuesmethods]] for more details * diff --git a/chimney/src/main/scala/io/scalaland/chimney/internal/runtime/IsFunction.scala b/chimney/src/main/scala/io/scalaland/chimney/internal/runtime/IsFunction.scala index 7eef628c0..b42a485bc 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/internal/runtime/IsFunction.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/internal/runtime/IsFunction.scala @@ -3,9 +3,10 @@ package io.scalaland.chimney.internal.runtime import scala.annotation.implicitNotFound /** Allow us to provide some better IDE support than just accepting everything as a parameter and waiting for the macro - * to scream with compilation error, in the world where different function types have no common ancestor (other and AnyRef). + * to scream with compilation error, in the world where different function types have no common ancestor + * (other than AnyRef). * - * @since 0.7.4 + * @since 0.8.4 */ @implicitNotFound( "Expected function of any arity (scala.Function0, scala.Function1, scala.Function2, ...) that returns a value of the target type, got ${Fn}" diff --git a/chimney/src/main/scala/io/scalaland/chimney/partial/AsResult.scala b/chimney/src/main/scala/io/scalaland/chimney/partial/AsResult.scala new file mode 100644 index 000000000..153518bbd --- /dev/null +++ b/chimney/src/main/scala/io/scalaland/chimney/partial/AsResult.scala @@ -0,0 +1,28 @@ +package io.scalaland.chimney.partial + +import scala.util.Try + +// TODO: docs + +trait AsResult[F[_]] { + + def asResult[A](fa: F[A]): Result[A] +} +object AsResult { + + implicit val optionAsResult: AsResult[Option] = new AsResult[Option] { + def asResult[A](fa: Option[A]): Result[A] = Result.fromOption(fa) + } + + implicit val eitherErrorAsResult: AsResult[Either[Result.Errors, *]] = new AsResult[Either[Result.Errors, *]] { + def asResult[A](fa: Either[Result.Errors, A]): Result[A] = Result.fromEither(fa) + } + + implicit val eitherStringAsResult: AsResult[Either[String, *]] = new AsResult[Either[String, *]] { + def asResult[A](fa: Either[String, A]): Result[A] = Result.fromEitherString(fa) + } + + implicit val tryAsResult: AsResult[Try] = new AsResult[Try] { + def asResult[A](fa: Try[A]): Result[A] = Result.fromTry(fa) + } +} diff --git a/chimney/src/main/scala/io/scalaland/chimney/partial/Path.scala b/chimney/src/main/scala/io/scalaland/chimney/partial/Path.scala index 5429135ec..0c3cd4ad2 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/partial/Path.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/partial/Path.scala @@ -37,6 +37,10 @@ final case class Path(private val elems: List[PathElement]) extends AnyVal { } } +/** Companion of [[io.scalaland.chimney.partial.Path]] + * + * @since 0.7.0 + */ object Path { /** Empty error path diff --git a/chimney/src/main/scala/io/scalaland/chimney/partial/PathElement.scala b/chimney/src/main/scala/io/scalaland/chimney/partial/PathElement.scala index e7327d88f..def39f802 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/partial/PathElement.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/partial/PathElement.scala @@ -1,6 +1,8 @@ package io.scalaland.chimney.partial /** Data type for representing path element in a (possibly) nested object structure. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/#total-transformers-vs-partialtransformers]] * * @since 0.7.0 */ @@ -13,6 +15,12 @@ sealed trait PathElement { def asString: String } +/** Companion of [[io.scalaland.chimney.partial.PathElement]]. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/#total-transformers-vs-partialtransformers]] + * + * @since 0.7.0 + */ object PathElement { /** Object property accessor (e.g case class param name). diff --git a/chimney/src/main/scala/io/scalaland/chimney/partial/Result.scala b/chimney/src/main/scala/io/scalaland/chimney/partial/Result.scala index d45fe67bf..8efee671d 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/partial/Result.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/partial/Result.scala @@ -4,7 +4,9 @@ import io.scalaland.chimney.internal.runtime.NonEmptyErrorsChain import scala.collection.compat.* import scala.util.{Failure, Success, Try} -/** Data type representing either successfully computed value or collection of path-annotated errors. +/** Data type representing either successfully computed value or non-empty collection of path-annotated errors. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/#total-transformers-vs-partialtransformers]] * * @tparam A type of success value * @@ -56,11 +58,53 @@ sealed trait Result[+A] { final def asErrorPathMessageStrings: Iterable[(String, String)] = this.asErrorPathMessages.map { case (path, message) => (path, message.asString) } + /** Converts a partial result to an [[scala.Either]] with collection of tuples with conventional string representation + * of path and errors message as [[scala.Left]]. + * + * @return [[scala.Right]] if success, + * [[scala.Left]] containing a sequence of pairs (a path to a failed field, + * an [[io.scalaland.chimney.partial.ErrorMessage]]) otherwise + * + * @since 0.8.5 + */ + final def asEitherErrorPathMessages: Either[Iterable[(String, ErrorMessage)], A] = this match { + case Result.Value(value) => Right(value) + case errors: Result.Errors => Left(errors.asErrorPathMessages) + } + + /** Converts a partial result to an [[scala.Either]] with collection of tuples with conventional string representation + * of path and string representation of error message as [[scala.Left]]. + * + * @return [[scala.Right]] if success, + * [[scala.Left]] containing a sequence of pairs (a path to a failed field, an errors message as + * [[java.lang.String]] otherwise + * + * @since 0.8.5 + */ + final def asEitherErrorPathMessageStrings: Either[Iterable[(String, String)], A] = this match { + case Result.Value(value) => Right(value) + case errors: Result.Errors => Left(errors.asErrorPathMessageStrings) + } + + /** Extracts value from a partial result and applies it to the appropriate function. + * + * @tparam B the type of the folding + * @param onValue the function to apply to success value + * @param onErrors the function to apply to errors + * @return a new value + * + * @since 0.8.5 + */ + final def fold[B](onValue: A => B, onErrors: Result.Errors => B): B = this match { + case Result.Value(value) => onValue(value) + case errors: Result.Errors => onErrors(errors) + } + /** Builds a new result by applying a function to a success value. * * @tparam B the element type of the returned result * @param f the function to apply to a success value - * @return a new result built from applying a function to a success value + * @return a new [[io.scalaland.chimney.partial.Result]] built from applying a function to a success value * * @since 0.7.0 */ @@ -73,8 +117,8 @@ sealed trait Result[+A] { * * @tparam B the element type of the returned result * @param f the function to apply to a success value - * @return a new result built from applying a function to a success value - * and using the result returned by that function + * @return a new [[io.scalaland.chimney.partial.Result]] built from applying a function to a success value + * and using the [[io.scalaland.chimney.partial.Result]] returned by that function * * @since 0.7.0 */ @@ -83,16 +127,24 @@ sealed trait Result[+A] { case _: Result.Errors => this.asInstanceOf[Result[B]] } - // TODO: docs + /** Builds a new result by flattening the current value. + * + * @tparam B the element type of the returned result + * @return a new [[io.scalaland.chimney.partial.Result]] built from applying a function to a success value + * and using the [[io.scalaland.chimney.partial.Result]] returned by that function + * + * @since 0.8.4 + */ final def flatten[B](implicit ev: A <:< Result[B]): Result[B] = this match { case Result.Value(value) => ev(value) case _: Result.Errors => this.asInstanceOf[Result[B]] } - /** Prepends a path element to all errors represented by this result. + /** Prepends a [[io.scalaland.chimney.partial.PathElement]] to all errors represented by this result. * - * @param pathElement path element to be prepended - * @return a result with path element prepended to all errors + * @param pathElement [[io.scalaland.chimney.partial.PathElement]] to be prepended + * @return a [[io.scalaland.chimney.partial.Result]] with [[io.scalaland.chimney.partial.PathElement]] prepended + * to all errors * * @since 0.7.0 */ @@ -102,12 +154,18 @@ sealed trait Result[+A] { } } +/** Companion to [[io.scalaland.chimney.partial.Result]]. + * + * @see [[https://chimney.readthedocs.io/supported-transformations/#total-transformers-vs-partialtransformers]] + * + * @since 0.7.0 + */ object Result { - /** Success value case representation + /** Success value case representation. * * @tparam A type of success value - * @param value value of type `T` + * @param value value of type `A` * * @since 0.7.0 */ @@ -116,7 +174,7 @@ object Result { def asErrorPathMessages: Iterable[(String, ErrorMessage)] = Iterable.empty } - /** Errors case representation + /** Errors case representation. * * @param errors non-empty collection of path-annotated errors * @@ -131,21 +189,22 @@ object Result { object Errors { - /** Creates failed result from one error or more. + /** Creates failed result from one [[io.scalaland.chimney.partial.Error]] or more. * * @param error required head [[io.scalaland.chimney.partial.Error]] * @param errors optional tail [[io.scalaland.chimney.partial.Error]]s - * @return result aggregating all passed errors + * @return [[io.scalaland.chimney.partial.Result.Errors]] aggregating all passed + * [[io.scalaland.chimney.partial.Error]]s * * @since 0.7.0 */ final def apply(error: Error, errors: Error*): Errors = apply(NonEmptyErrorsChain.from(error, errors*)) - /** Creates failed result from a single error. + /** Creates failed result from a single [[io.scalaland.chimney.partial.Error]]. * - * @param error required error - * @return result containing one error + * @param error required [[io.scalaland.chimney.partial.Error]] + * @return [[io.scalaland.chimney.partial.Result.Errors]] containing one [[io.scalaland.chimney.partial.Error]] * * @since 0.7.0 */ @@ -154,8 +213,8 @@ object Result { /** Creates failed result from an error message. * - * @param message message to wrap in Error - * @return result containing one error + * @param message message to wrap in [[io.scalaland.chimney.partial.Error]] + * @return [[io.scalaland.chimney.partial.Result.Errors]] containing one [[io.scalaland.chimney.partial.Error]] * * @since 0.7.0 */ @@ -164,9 +223,10 @@ object Result { /** Creates failed result from one error message or more. * - * @param message required message to wrap in Error - * @param messages optional messages to wrap in Error - * @return result aggregating all passed errors + * @param message required message to wrap in [[io.scalaland.chimney.partial.Error]] + * @param messages optional messages to wrap in [[io.scalaland.chimney.partial.Error]] + * @return [[io.scalaland.chimney.partial.Result.Errors]] aggregating all passed + * [[io.scalaland.chimney.partial.Error]]s * * @since 0.7.0 */ @@ -175,9 +235,9 @@ object Result { /** Creates new failed result containing all errors of 2 existing failed results. * - * @param errors1 failed result which errors should appear in the beginning - * @param errors2 failed result which errors should appear in the end - * @return result aggregating errors from both results + * @param errors1 failed result which [[io.scalaland.chimney.partial.Error]]s should appear in the beginning + * @param errors2 failed result which [[io.scalaland.chimney.partial.Error]]s should appear in the end + * @return [[io.scalaland.chimney.partial.Result.Errors]] aggregating errors from both results * * @since 0.7.0 */ @@ -186,19 +246,19 @@ object Result { /** Used internally by macro. Please don't use in your code. */ - final def __mergeResultNullable[T](errorsNullable: Errors, result: Result[T]): Errors = + final def __mergeResultNullable[A](errorsNullable: Errors, result: Result[A]): Errors = result match { case _: Value[?] => errorsNullable case errors: Errors => if (errorsNullable == null) errors else merge(errorsNullable, errors) } } - /** Converts a function that throws Exceptions into function that returns Result. + /** Converts a function that throws Exceptions into function that returns [[io.scalaland.chimney.partial.Result]]. * * @tparam A input type * @tparam B output type * @param f function that possibly throws - * @return function that caches Exceptions as failed results + * @return function that catches Exceptions as [[io.scalaland.chimney.partial.Result.Errors]] * * @since 0.7.0 */ @@ -206,12 +266,14 @@ object Result { Result.fromCatching(f(a)) } - /** Converts a partial function that throws Exceptions into function that returns Result. + /** Converts a partial function that throws Exceptions into function that returns + * [[io.scalaland.chimney.partial.Result]]. * * @tparam A input type * @tparam B output type * @param pf partial function that possibly throws - * @return function that caches Exceptions and arguments without defined value as failed Results + * @return function that catches Exceptions and arguments without defined value as + * [[io.scalaland.chimney.partial.Result.Errors]] * * @since 0.7.0 */ @@ -223,11 +285,11 @@ object Result { } } - /** Creates successful Result from a precomputed value. + /** Creates successful [[io.scalaland.chimney.partial.Result]] from a precomputed value. * - * @tparam A type of sucessful value + * @tparam A type of successful value * @param value successful value to return in result - * @return successful result + * @return successful [[io.scalaland.chimney.partial.Result.Value]] * * @since 0.7.0 */ @@ -236,34 +298,37 @@ object Result { /** Creates failed Result with an empty value error. * * @tparam A type of successful result - * @return failed result + * @return failed [[io.scalaland.chimney.partial.Result.Errors]] containing one + * [[io.scalaland.chimney.partial.Error.fromEmptyValue]] * * @since 0.7.0 */ final def fromEmpty[A]: Result[A] = Errors.single(Error.fromEmptyValue) - /** Creates failed result from a single error. + /** Creates failed [[io.scalaland.chimney.partial.Result]] from a single error. * * @tparam A type of successful result * @param error required error - * @return result containing one error + * @return failed [[io.scalaland.chimney.partial.Result.Errors]] containing one + * [[io.scalaland.chimney.partial.Error]] * * @since 0.7.0 */ final def fromError[A](error: Error): Result[A] = Errors.single(error) - /** Creates failed result from one error or more. + /** Creates failed [[io.scalaland.chimney.partial.Result]] from one error or more. * * @tparam A type of successful result * @param error required head [[io.scalaland.chimney.partial.Error]] * @param errors optional tail [[io.scalaland.chimney.partial.Error]]s - * @return result aggregating all passed errors + * @return failed [[io.scalaland.chimney.partial.Result.Errors]] aggregating all passed + * [[io.scalaland.chimney.partial.Error]]s * * @since 0.7.0 */ final def fromErrors[A](error: Error, errors: Error*): Result[A] = Errors(error, errors*) - /** Creates failed result from an error message. + /** Creates failed [[io.scalaland.chimney.partial.Result]] from an error message. * * @tparam A type of successful result * @param message message to wrap in Error @@ -273,7 +338,7 @@ object Result { */ final def fromErrorString[A](message: String): Result[A] = Errors.fromString(message) - /** Creates failed result from one error message or more. + /** Creates failed [[io.scalaland.chimney.partial.Result]] from one error message or more. * * @tparam A type of successful result * @param message required message to wrap in Error @@ -285,7 +350,7 @@ object Result { final def fromErrorStrings[A](message: String, messages: String*): Result[A] = Errors.fromStrings(message, messages*) - /** Creates failed result from argument for which PartialFunction was not defined. + /** Creates failed [[io.scalaland.chimney.partial.Result]] from argument for which PartialFunction was not defined. * * @tparam A type of successful result * @param value value for which partial function was not defined @@ -295,7 +360,7 @@ object Result { */ final def fromErrorNotDefinedAt[A](value: Any): Result[A] = Errors.single(Error.fromNotDefinedAt(value)) - /** Creates failed result from Exception that was caught. + /** Creates failed [[io.scalaland.chimney.partial.Result]] from Exception that was caught. * * @tparam W type of successful result * @param throwable exception @@ -305,7 +370,7 @@ object Result { */ final def fromErrorThrowable[W](throwable: Throwable): Result[W] = Errors.single(Error.fromThrowable(throwable)) - /** Converts Option to Result, using EmptyValue error if None. + /** Converts Option to [[io.scalaland.chimney.partial.Result]], using EmptyValue error if None. * * @tparam A type of successful result * @param value Option to convert @@ -318,12 +383,13 @@ object Result { case _ => fromEmpty[A] } - /** Converts Option to Result, using provided Error if None. + /** Converts Option to [[io.scalaland.chimney.partial.Result]], using provided Error if None. * * @tparam A type of successful result * @param value Option to convert * @param ifEmpty lazy error for [[scala.None]] * @return successful result if [[scala.Some]], failed result with provided error if [[scala.None]] + * * @since 0.7.0 */ final def fromOptionOrError[A](value: Option[A], ifEmpty: => Error): Result[A] = value match { @@ -331,10 +397,10 @@ object Result { case _ => fromError(ifEmpty) } - /** Converts Option to Result, using provided error message if None. + /** Converts Option to [[io.scalaland.chimney.partial.Result]], using provided error message if None. * * @tparam A type of successful result - * @param value Option to convert + * @param value [[scala.Option]] to convert * @param ifEmpty lazy error message for [[scala.None]] * @return successful result if [[scala.Some]], failed result with provided error message if [[scala.None]] * @@ -359,7 +425,7 @@ object Result { case _ => fromErrorThrowable(ifEmpty) } - /** Converts Either to Result, using Errors from Left as failed result. + /** Converts Either to [[io.scalaland.chimney.partial.Result]], using Errors from Left as failed result. * * @tparam A type of successful result * @param value Either to convert @@ -372,7 +438,7 @@ object Result { case Left(errors: Errors) => errors } - /** Converts Either to Result, using an error message from Left as failed result. + /** Converts Either to [[io.scalaland.chimney.partial.Result]], using an error message from Left as failed result. * * @tparam A type of successful result * @param value Either to convert @@ -383,10 +449,10 @@ object Result { final def fromEitherString[A](value: Either[String, A]): Result[A] = fromEither(value.left.map(Errors.fromString)) - /** Converts Try to Result, using Throwable from Failure as failed result. + /** Converts Try to [[io.scalaland.chimney.partial.Result]], using Throwable from Failure as failed result. * * @tparam A type of successful result - * @param value Try to convert + * @param value [[scala.util.Try]] to convert * @return successful result if [[scala.util.Success]], failed result with Throwable if [[scala.util.Failure]] * * @since 0.7.0 @@ -396,11 +462,12 @@ object Result { case Failure(throwable) => fromErrorThrowable(throwable) } - /** Converts possibly throwing computation into Result. + /** Converts possibly throwing computation into [[io.scalaland.chimney.partial.Result]]. * * @tparam A type of successful result - * @param value computation to run while catching its exceptions - * @return successful Result if computation didn't throw, failed Result with caught exception if it threw + * @param value computation to run while catching its Exceptions + * @return successful [[io.scalaland.chimney.partial.Result.Value]] if computation didn't throw, + * failed [[io.scalaland.chimney.partial.Result.Errors]] with caught Exception if it threw * * @since 0.7.0 */ @@ -411,8 +478,8 @@ object Result { case t: Throwable => fromErrorThrowable(t) } - /** Converts an Iterator of input type into selected collection of output type, aggregating errors from conversions - * of individual elements. + /** Converts an [[scala.collection.Iterator]] of input type into selected collection of output type, aggregating + * errors from conversions of individual elements. * * @tparam M type of output - output collection of output element * @tparam A type of elements of input @@ -422,8 +489,8 @@ object Result { * @param failFast whether conversion should stop at first failed element conversion * or should it continue to aggregate all errors * @param fac factory of output type - * @return result with user-selected collection of converted elements if all conversions succeeded, - * result with conversion errors if at least one conversion failed + * @return [[io.scalaland.chimney.partial.Result.Value]] with user-selected collection of converted elements if all conversions succeeded, + * [[io.scalaland.chimney.partial.Result.Errors]] with conversion errors if at least one conversion failed * * @since 0.7.0 */ @@ -455,7 +522,8 @@ object Result { } } - /** Converts an Iterator containing Results into a Result with selected collection of successful values. + /** Converts an [[scala.collection.Iterator]] containing [[io.scalaland.chimney.partial.Result]]s into + * a [[io.scalaland.chimney.partial.Result]] with selected collection of successful values. * * @tparam M type of output - output collection of output element * @tparam A type of successful values in Results @@ -471,7 +539,8 @@ object Result { final def sequence[M, A](it: Iterator[Result[A]], failFast: Boolean)(implicit fac: Factory[A, M]): Result[M] = traverse(it, identity[Result[A]], failFast) - /** Combines 2 Results into 1 by combining their successful values or aggregating errors. + /** Combines 2 [[io.scalaland.chimney.partial.Result]]s into 1 by combining their successful values or aggregating + * errors. * * @tparam A first successful input type * @tparam B second successful input type @@ -481,8 +550,10 @@ object Result { * @param f function combining 2 successful input values into successful output value * @param failFast whether conversion should stop at first failed element * or should it aggregate errors from both Results - * @return successful Result of combination if both results were successful, - * failed result if at least one of Result were failure + * @return successful [[io.scalaland.chimney.partial.Result.Value]] of combination if both + * [[io.scalaland.chimney.partial.Result]]s were successful, + * failed [[io.scalaland.chimney.partial.Result.Errors]] if at least one + * of [[io.scalaland.chimney.partial.Result]]s were failure * * @since 0.7.0 */ @@ -505,16 +576,19 @@ object Result { } } - /** Combines 2 Results into 1 by tupling their successful values or aggregating errors. + /** Combines 2 [[io.scalaland.chimney.partial.Result]]s into 1 by tupling their successful values or aggregating + * errors. * * @tparam A first successful input type * @tparam B second successful input type - * @param resultA first Result - * @param resultB second Result + * @param resultA first [[io.scalaland.chimney.partial.Result]] + * @param resultB second [[io.scalaland.chimney.partial.Result]] * @param failFast whether conversion should stop at first failed element - * or should it aggregate errors from both Results - * @return successful Result with a tuple if both results were successful, - * failed result if at least one of Result were failure + * or should it aggregate errors from both [[io.scalaland.chimney.partial.Result]]s + * @return successful [[io.scalaland.chimney.partial.Result.Value]] with a tuple if both + * [[io.scalaland.chimney.partial.Result]]s were successful, + * failed [[io.scalaland.chimney.partial.Result.Errors]] if at least one + * of [[io.scalaland.chimney.partial.Result]] were failure * * @since 0.7.0 */ From f8a0c804b99e7091608737f0412c35915e447760 Mon Sep 17 00:00:00 2001 From: Mateusz Kubuszok Date: Tue, 2 Jan 2024 11:57:48 +0100 Subject: [PATCH 2/6] Remove sidebar from landing and benchmark page in docs --- docs/docs/benchmarks.md | 6 ++++++ docs/docs/index.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/docs/docs/benchmarks.md b/docs/docs/benchmarks.md index d273bc1c9..03b38ba3b 100644 --- a/docs/docs/benchmarks.md +++ b/docs/docs/benchmarks.md @@ -1,3 +1,9 @@ +--- +hide: + - navigation + - toc +--- +