Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cats data integrations #511

Merged
merged 2 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ lazy val chimney = projectMatrix

lazy val chimneyCats = projectMatrix
.in(file("chimney-cats"))
.someVariations(versions.scalas, versions.platforms)(only1VersionInIDE *)
.someVariations(versions.scalas, versions.platforms)((preAndPost212 +: only1VersionInIDE) *)
.enablePlugins(GitVersioning, GitBranchPrompt)
.disablePlugins(WelcomePlugin, ProtocPlugin)
.settings(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.scalaland.chimney.cats

/** @since 1.0.0 */
private[cats] trait CatsDataImplicitsCompat {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.scalaland.chimney.cats

import cats.data.NonEmptyLazyList
import io.scalaland.chimney.integrations.*
import io.scalaland.chimney.partial

import scala.collection.compat.Factory
import scala.collection.mutable

/** @since 1.0.0 */
private[cats] trait CatsDataImplicitsCompat {

/** @since 1.0.0 */
implicit def catsNonEmptyLazyListIsPartiallyBuildIterable[A]: PartiallyBuildIterable[NonEmptyLazyList[A], A] =
new PartiallyBuildIterable[NonEmptyLazyList[A], A] {
def partialFactory: Factory[A, partial.Result[NonEmptyLazyList[A]]] =
new FactoryCompat[A, partial.Result[NonEmptyLazyList[A]]] {
def newBuilder: mutable.Builder[A, partial.Result[NonEmptyLazyList[A]]] =
new FactoryCompat.Builder[A, partial.Result[NonEmptyLazyList[A]]] {
private val impl = mutable.ListBuffer.empty[A]
def clear(): Unit = impl.clear()

Check warning on line 21 in chimney-cats/src/main/scala-2.13+/io/scalaland/chimney/cats/CatsDataImplicitsCompat.scala

View check run for this annotation

Codecov / codecov/patch

chimney-cats/src/main/scala-2.13+/io/scalaland/chimney/cats/CatsDataImplicitsCompat.scala#L21

Added line #L21 was not covered by tests
def result(): partial.Result[NonEmptyLazyList[A]] =
partial.Result.fromOption(NonEmptyLazyList.fromSeq(impl.result()))
def addOne(elem: A): this.type = { impl += elem; this }
}
}
def iterator(collection: NonEmptyLazyList[A]): Iterator[A] = collection.iterator
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package io.scalaland.chimney.cats

import cats.data.{Chain, NonEmptyChain, NonEmptyList, NonEmptyMap, NonEmptySeq, NonEmptySet, NonEmptyVector}
import io.scalaland.chimney.integrations.*
import io.scalaland.chimney.partial

import scala.collection.compat.Factory
import scala.collection.mutable

/** @since 1.0.0 */
trait CatsDataImplicits extends CatsDataImplicitsCompat {

/** @since 1.0.0 */
implicit def catsChainIsTotallyBuildIterable[A]: TotallyBuildIterable[Chain[A], A] =
new TotallyBuildIterable[Chain[A], A] {
def totalFactory: Factory[A, Chain[A]] = new FactoryCompat[A, Chain[A]] {
def newBuilder: mutable.Builder[A, Chain[A]] = new FactoryCompat.Builder[A, Chain[A]] {
private var impl = Chain.empty[A]
def clear(): Unit = impl = Chain.empty[A]

Check warning on line 19 in chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala

View check run for this annotation

Codecov / codecov/patch

chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala#L19

Added line #L19 was not covered by tests
def result(): Chain[A] = impl
def addOne(elem: A): this.type = { impl = impl.append(elem); this }
}
}
def iterator(collection: Chain[A]): Iterator[A] = collection.iterator
}

/** @since 1.0.0 */
implicit def catsNonEmptyChainIsPartiallyBuildIterable[A]: PartiallyBuildIterable[NonEmptyChain[A], A] =
new PartiallyBuildIterable[NonEmptyChain[A], A] {
def partialFactory: Factory[A, partial.Result[NonEmptyChain[A]]] =
new FactoryCompat[A, partial.Result[NonEmptyChain[A]]] {
def newBuilder: mutable.Builder[A, partial.Result[NonEmptyChain[A]]] =
new FactoryCompat.Builder[A, partial.Result[NonEmptyChain[A]]] {
private var impl = Chain.empty[A]
def clear(): Unit = impl = Chain.empty[A]

Check warning on line 35 in chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala

View check run for this annotation

Codecov / codecov/patch

chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala#L35

Added line #L35 was not covered by tests
def result(): partial.Result[NonEmptyChain[A]] = partial.Result.fromOption(NonEmptyChain.fromChain(impl))
def addOne(elem: A): this.type = { impl = impl.append(elem); this }
}
}
def iterator(collection: NonEmptyChain[A]): Iterator[A] = collection.iterator
}

/** @since 1.0.0 */
implicit def catsNonEmptyListIsPartiallyBuildIterable[A]: PartiallyBuildIterable[NonEmptyList[A], A] =
new PartiallyBuildIterable[NonEmptyList[A], A] {
def partialFactory: Factory[A, partial.Result[NonEmptyList[A]]] =
new FactoryCompat[A, partial.Result[NonEmptyList[A]]] {
def newBuilder: mutable.Builder[A, partial.Result[NonEmptyList[A]]] =
new FactoryCompat.Builder[A, partial.Result[NonEmptyList[A]]] {
private val impl = List.newBuilder[A]
def clear(): Unit = impl.clear()

Check warning on line 51 in chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala

View check run for this annotation

Codecov / codecov/patch

chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala#L51

Added line #L51 was not covered by tests
def result(): partial.Result[NonEmptyList[A]] =
partial.Result.fromOption(NonEmptyList.fromList(impl.result()))
def addOne(elem: A): this.type = { impl += elem; this }
}
}
def iterator(collection: NonEmptyList[A]): Iterator[A] = collection.iterator
}

/** @since 1.0.0 */
implicit def catsNonEmptyMapIsPartiallyBuildMap[K: Ordering, V]: PartiallyBuildMap[NonEmptyMap[K, V], K, V] =
new PartiallyBuildMap[NonEmptyMap[K, V], K, V] {
def partialFactory: Factory[(K, V), partial.Result[NonEmptyMap[K, V]]] =
new FactoryCompat[(K, V), partial.Result[NonEmptyMap[K, V]]] {
def newBuilder: mutable.Builder[(K, V), partial.Result[NonEmptyMap[K, V]]] =
new FactoryCompat.Builder[(K, V), partial.Result[NonEmptyMap[K, V]]] {
private val impl = scala.collection.immutable.SortedMap.newBuilder[K, V]
def clear(): Unit = impl.clear()

Check warning on line 68 in chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala

View check run for this annotation

Codecov / codecov/patch

chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala#L68

Added line #L68 was not covered by tests
def result(): partial.Result[NonEmptyMap[K, V]] =
partial.Result.fromOption(NonEmptyMap.fromMap(impl.result()))
def addOne(elem: (K, V)): this.type = { impl += elem; this }
}
}
def iterator(collection: NonEmptyMap[K, V]): Iterator[(K, V)] = collection.toSortedMap.iterator
}

/** @since 1.0.0 */
implicit def catsNonEmptySeqIsPartiallyBuildIterable[A]: PartiallyBuildIterable[NonEmptySeq[A], A] =
new PartiallyBuildIterable[NonEmptySeq[A], A] {
def partialFactory: Factory[A, partial.Result[NonEmptySeq[A]]] =
new FactoryCompat[A, partial.Result[NonEmptySeq[A]]] {
def newBuilder: mutable.Builder[A, partial.Result[NonEmptySeq[A]]] =
new FactoryCompat.Builder[A, partial.Result[NonEmptySeq[A]]] {
private val impl = mutable.ListBuffer.empty[A]
def clear(): Unit = impl.clear()

Check warning on line 85 in chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala

View check run for this annotation

Codecov / codecov/patch

chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala#L85

Added line #L85 was not covered by tests
def result(): partial.Result[NonEmptySeq[A]] =
partial.Result.fromOption(NonEmptySeq.fromSeq(impl.result()))
def addOne(elem: A): this.type = { impl += elem; this }
}
}
def iterator(collection: NonEmptySeq[A]): Iterator[A] = collection.iterator
}

/** @since 1.0.0 */
implicit def catsNonEmptySetIsPartiallyBuildIterable[A: Ordering]: PartiallyBuildIterable[NonEmptySet[A], A] =
new PartiallyBuildIterable[NonEmptySet[A], A] {
def partialFactory: Factory[A, partial.Result[NonEmptySet[A]]] =
new FactoryCompat[A, partial.Result[NonEmptySet[A]]] {
def newBuilder: mutable.Builder[A, partial.Result[NonEmptySet[A]]] =
new FactoryCompat.Builder[A, partial.Result[NonEmptySet[A]]] {
private val impl = scala.collection.immutable.SortedSet.newBuilder[A]
def clear(): Unit = impl.clear()

Check warning on line 102 in chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala

View check run for this annotation

Codecov / codecov/patch

chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala#L102

Added line #L102 was not covered by tests
def result(): partial.Result[NonEmptySet[A]] =
partial.Result.fromOption(NonEmptySet.fromSet(impl.result()))
def addOne(elem: A): this.type = { impl += elem; this }
}
}
def iterator(collection: NonEmptySet[A]): Iterator[A] = collection.toNonEmptyList.iterator
}

/** @since 1.0.0 */
implicit def catsNonEmptyVectorIsPartiallyBuildIterable[A]: PartiallyBuildIterable[NonEmptyVector[A], A] =
new PartiallyBuildIterable[NonEmptyVector[A], A] {
def partialFactory: Factory[A, partial.Result[NonEmptyVector[A]]] =
new FactoryCompat[A, partial.Result[NonEmptyVector[A]]] {
def newBuilder: mutable.Builder[A, partial.Result[NonEmptyVector[A]]] =
new FactoryCompat.Builder[A, partial.Result[NonEmptyVector[A]]] {
private val impl = Vector.newBuilder[A]
def clear(): Unit = impl.clear()

Check warning on line 119 in chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala

View check run for this annotation

Codecov / codecov/patch

chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsDataImplicits.scala#L119

Added line #L119 was not covered by tests
def result(): partial.Result[NonEmptyVector[A]] =
partial.Result.fromOption(NonEmptyVector.fromVector(impl.result()))
def addOne(elem: A): this.type = { impl += elem; this }
}
}
def iterator(collection: NonEmptyVector[A]): Iterator[A] = collection.iterator
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.scalaland.chimney

/** @since 0.5.0 */
package object cats
extends CatsTotalTransformerImplicits
extends CatsDataImplicits
with CatsTotalTransformerImplicits
with CatsPartialTransformerImplicits
with CatsPartialResultImplicits
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.scalaland.chimney.cats

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

class CatsData213Spec extends ChimneySpec {

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

NonEmptyLazyList("test").transformInto[List[String]] ==> List("test")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package io.scalaland.chimney.cats

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

class CatsDataSpec extends ChimneySpec {

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

Chain("test").transformInto[List[String]] ==> List("test")
}

test("DSL should handle transformation to and from cats.data.NonEmptyChain") {
List("test").transformIntoPartial[NonEmptyChain[String]].asOption ==> Some(NonEmptyChain.one("test"))
List.empty[String].transformIntoPartial[NonEmptyChain[String]].asOption ==> None

NonEmptyChain.one("test").transformInto[List[String]] ==> List("test")
}

test("DSL should handle transformation to and from cats.data.NonEmptyList") {
List("test").transformIntoPartial[NonEmptyList[String]].asOption ==> Some(NonEmptyList.one("test"))
List.empty[String].transformIntoPartial[NonEmptyList[String]].asOption ==> None

NonEmptyList.one("test").transformInto[List[String]] ==> List("test")
}

test("DSL should handle transformation to and from cats.data.NonEmptyMap") {
List("test" -> "test").transformIntoPartial[NonEmptyMap[String, String]].asOption ==> Some(
NonEmptyMap.one("test", "test")
)
List.empty[(String, String)].transformIntoPartial[NonEmptyMap[String, String]].asOption ==> None

NonEmptyMap.one("test", "test").transformInto[List[(String, String)]] ==> List("test" -> "test")
}

test("DSL should handle transformation to and from cats.data.NonEmptySeq") {
List("test").transformIntoPartial[NonEmptySeq[String]].asOption ==> Some(NonEmptySeq.one("test"))
List.empty[String].transformIntoPartial[NonEmptySeq[String]].asOption ==> None

NonEmptySeq.one("test").transformInto[List[String]] ==> List("test")
}

test("DSL should handle transformation to and from cats.data.NonEmptySet") {
List("test").transformIntoPartial[NonEmptySet[String]].asOption ==> Some(NonEmptySet.one("test"))
List.empty[String].transformIntoPartial[NonEmptySet[String]].asOption ==> None

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

test("DSL should handle transformation to and from cats.data.NonEmptyVector") {
List("test").transformIntoPartial[NonEmptyVector[String]].asOption ==> Some(NonEmptyVector.one("test"))
List.empty[String].transformIntoPartial[NonEmptyVector[String]].asOption ==> None

NonEmptyVector.one("test").transformInto[List[String]] ==> List("test")
}
}
13 changes: 12 additions & 1 deletion docs/docs/cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,16 @@ Cats integration module contains the following utilities:
`UnorderedFoldable`, `Invariant`, `Semigriupal`, `NonEmptyAlternative`, `SemigroupK`, `MonoidK`)
- `Parallel[partial.Result]` (implementing also`NonEmptyParallel`)
- `Semigroup[partial.Result.Errors]`
- 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`)

!!! important

Expand Down Expand Up @@ -1498,4 +1508,5 @@ The only 2 difference they make is that:
- they allow usage of `everyMapKey` and `everyMapValue` in paths, just like with standard library's `Maps`.

An example of such collections are `java.util` collections for which support is provided via `TotallyBuildIterable`
and `TotallyBuildMap` in [Java collections' integration](#java-collections-integration).
and `TotallyBuildMap` in [Java collections' integration](#java-collections-integration), or `cats.data` types
provided in [Cats integration](#cats-integration).
Loading