Skip to content

Commit

Permalink
Cats instances with Outer Transformers
Browse files Browse the repository at this point in the history
  • Loading branch information
MateuszKubuszok committed Oct 8, 2024
1 parent 979fb5d commit 13584be
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.scalaland.chimney.cats

import cats.{Order, Traverse}
import cats.data.{Chain, NonEmptyChain, NonEmptyList, NonEmptyMap, NonEmptySeq, NonEmptySet, NonEmptyVector}
import io.scalaland.chimney.integrations.*
import io.scalaland.chimney.partial
Expand All @@ -10,6 +11,54 @@ import scala.collection.mutable
/** @since 1.0.0 */
trait CatsDataImplicits extends CatsDataImplicitsCompat {

/** @since 1.5.0 */
implicit def catsTotalOuterTransformerFromTraverse[F[_]: Traverse, A, B]: TotalOuterTransformer[F[A], F[B], A, B] =
new TotalOuterTransformer[F[A], F[B], A, B] {

def transformWithTotalInner(src: F[A], inner: A => B): F[B] = Traverse[F].map(src)(inner)

def transformWithPartialInner(
src: F[A],
failFast: Boolean,
inner: A => partial.Result[B]
): partial.Result[F[B]] = Traverse[F].traverseWithIndexM(src) { (a, idx) =>
inner(a).prependErrorPath(partial.PathElement.Index(idx))
}
}

/** @since 1.5.0 */
implicit def catsTotalOuterTransformerForNonEmptyMap[A, B, C: Order, D]
: TotalOuterTransformer[NonEmptyMap[A, B], NonEmptyMap[C, D], (A, B), (C, D)] =
new TotalOuterTransformer[NonEmptyMap[A, B], NonEmptyMap[C, D], (A, B), (C, D)] {

def transformWithTotalInner(src: NonEmptyMap[A, B], inner: ((A, B)) => (C, D)): NonEmptyMap[C, D] =
src.mapBoth((k, v) => inner(k -> v))

def transformWithPartialInner(
src: NonEmptyMap[A, B],
failFast: Boolean,
inner: ((A, B)) => partial.Result[(C, D)]
): partial.Result[NonEmptyMap[C, D]] = partial.Result
.traverse[Seq[(C, D)], (A, B), (C, D)](src.toSortedMap.iterator, inner, failFast)
.map(seq => NonEmptyMap.of(seq.head, seq.tail*))
}

/** @since 1.5.0 */
implicit def catsTotalOuterTransformerForNonEmptySet[A, B: Order]
: TotalOuterTransformer[NonEmptySet[A], NonEmptySet[B], A, B] =
new TotalOuterTransformer[NonEmptySet[A], NonEmptySet[B], A, B] {

def transformWithTotalInner(src: NonEmptySet[A], inner: A => B): NonEmptySet[B] = src.map(inner)

def transformWithPartialInner(
src: NonEmptySet[A],
failFast: Boolean,
inner: A => partial.Result[B]
): partial.Result[NonEmptySet[B]] = partial.Result
.traverse[Seq[B], A, B](src.toSortedSet.iterator, inner, failFast)
.map(seq => NonEmptySet.of(seq.head, seq.tail*))
}

/** @since 1.0.0 */
implicit def catsChainIsTotallyBuildIterable[A]: TotallyBuildIterable[Chain[A], A] =
new TotallyBuildIterable[Chain[A], A] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ package io.scalaland.chimney.cats

import cats.data.NonEmptyLazyList
import io.scalaland.chimney.ChimneySpec
import io.scalaland.chimney.Transformer
import io.scalaland.chimney.dsl.*

class CatsData213Spec extends ChimneySpec {

test("DSL should always allow transformation between NonEmptyLazyLists") {
implicit val intToStr: Transformer[Int, String] = _.toString

NonEmptyLazyList(1).transformInto[NonEmptyLazyList[String]] ==> NonEmptyLazyList("1")
}

test("DSL should handle transformation to and from cats.data.NonEmptyLazyList") {
List("test").transformIntoPartial[NonEmptyLazyList[String]].asOption ==> Some(NonEmptyLazyList("test"))
List.empty[String].transformIntoPartial[NonEmptyLazyList[String]].asOption ==> None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,33 @@ package io.scalaland.chimney.cats

import cats.data.{Chain, NonEmptyChain, NonEmptyList, NonEmptyMap, NonEmptySeq, NonEmptySet, NonEmptyVector}
import io.scalaland.chimney.ChimneySpec
import io.scalaland.chimney.Transformer
import io.scalaland.chimney.dsl.*

class CatsDataSpec extends ChimneySpec {

test("DSL should always allow transformation when outer type has Traverse") {
implicit val intToStr: Transformer[Int, String] = _.toString

NonEmptyChain.one(1).transformInto[NonEmptyChain[String]] ==> NonEmptyChain.one("1")
NonEmptyList.one(1).transformInto[NonEmptyList[String]] ==> NonEmptyList.one("1")
NonEmptyMap.one(1, 1).transformInto[NonEmptyMap[String, String]] ==> NonEmptyMap.one("1", "1")
NonEmptySeq.one(1).transformInto[NonEmptySeq[String]] ==> NonEmptySeq.one("1")
NonEmptyVector.one(1).transformInto[NonEmptyVector[String]] ==> NonEmptyVector.one("1")
}

test("DSL should always allow transformation between NonEmptyMaps") {
implicit val intToStr: Transformer[Int, String] = _.toString

NonEmptyMap.one(1, 1).transformInto[NonEmptyMap[String, String]] ==> NonEmptyMap.one("1", "1")
}

test("DSL should always allow transformation between NonEmptySets") {
implicit val intToStr: Transformer[Int, String] = _.toString

NonEmptySet.one(1).transformInto[NonEmptySet[String]] ==> NonEmptySet.one("1")
}

test("DSL should handle transformation to and from cats.data.Chain") {
List("test").transformInto[Chain[String]] ==> Chain("test")

Expand Down
22 changes: 14 additions & 8 deletions docs/docs/cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,14 +524,20 @@ Cats integration module contains the following utilities:
- `InvariantSemigroupal[Iso[First, *]]` (implementing also `Invariant`, `Semigroupal`)
- instances for `cats.data` types allowing Chimney to recognize them as collections:
- `cats.data.Chain` (transformation _from_ and _to_ always available)
- `cats.data.NonEmptyChain` (transformations: _from_ always available, _to_ only with `PartialTransformer`)
- `cats.data.NonEmptyLazyList` (transformation: _from_ always available, _to_ only with `PartialTransformer`,
the type is only defined on 2.13+)
- `cats.data.NonEmptyList` (transformation: _from_ always available, _to_ only with `PartialTransformer`)
- `cats.data.NonEmptyMap` (transformation: _from_ always available, _to_ only with `PartialTransformer`)
- `cats.data.NonEmptySeq` (transformation: _from_ always available, _to_ only with `PartialTransformer`)
- `cats.data.NonEmptySet` (transformation: _from_ always available, _to_ only with `PartialTransformer`)
- `cats.data.NonEmptyVector` (transformation: _from_ always available, _to_ only with `PartialTransformer`)
- `cats.data.NonEmptyChain` (transformations: _from_ always available, _to_ only with `PartialTransformer`
or to another `NonEmptyChain`)
- `cats.data.NonEmptyLazyList` (transformation: _from_ always available, _to_ only with `PartialTransformer`
or to another `NonEmptyLazyList`, the type is only defined on 2.13+)
- `cats.data.NonEmptyList` (transformation: _from_ always available, _to_ only with `PartialTransformer`
or to another `NonEmptyList`)
- `cats.data.NonEmptyMap` (transformation: _from_ always available, _to_ only with `PartialTransformer`
or to another `NonEmptyMap`)
- `cats.data.NonEmptySeq` (transformation: _from_ always available, _to_ only with `PartialTransformer`
or to another `NonEmptySeq`)
- `cats.data.NonEmptySet` (transformation: _from_ always available, _to_ only with `PartialTransformer`
or to another `NonEmptySet`)
- `cats.data.NonEmptyVector` (transformation: _from_ always available, _to_ only with `PartialTransformer`
or to another `NonEmptyVector`)

!!! important

Expand Down

0 comments on commit 13584be

Please sign in to comment.