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

More nested-path functionalities #493

Merged
merged 20 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
561124b
Start using stubbed Paths for recursion rules (without testing yet)
MateuszKubuszok Apr 5, 2024
ae63c21
Rewrite a few usages of tpe.Underlying into Tpe, when import tpe.Unde…
MateuszKubuszok Apr 5, 2024
8a94eaa
Prototype of a nested-paths for subtypes and collections (internally,…
MateuszKubuszok Apr 5, 2024
9b9b749
Create extension methods for onSubtype/on.../eachItem/each... in DSL
MateuszKubuszok Apr 7, 2024
2159ce8
Scala 3 DSL parsing implementation, the first test for new nested con…
MateuszKubuszok Apr 8, 2024
6d06130
Fix more issues on Scala 3
MateuszKubuszok Apr 8, 2024
0828c0f
Modify implicits and DSL on Scala 2 to make DSL extensions for nested…
MateuszKubuszok Apr 8, 2024
68ffa2e
Change everyItem, everyMapKey and everyMapValue implementation in Sca…
MateuszKubuszok Apr 9, 2024
19cb771
Add implicitNotFound to IsOption, IsEither, IsCollection, IsMap type …
MateuszKubuszok Apr 9, 2024
68e6cb1
Revert some unnecessary changes (in benchmarks, unneeded renames, Fro…
MateuszKubuszok Apr 10, 2024
78c4e6e
Restore old behavior for computing error paths for converting pair co…
MateuszKubuszok Apr 10, 2024
5b2c5e5
Limit the number of pointless asInstanceOf in generated code
MateuszKubuszok Apr 10, 2024
bc8c212
More tests in Total Product spec
MateuszKubuszok Apr 10, 2024
d53cc6e
Merge widenExpr and upcastExpr into upcastToExprOf since they are bas…
MateuszKubuszok Apr 10, 2024
7330cc8
Happy-path nested-paths tests for Products
MateuszKubuszok Apr 10, 2024
820357f
Check nested-paths DSL in Product specs
MateuszKubuszok Apr 10, 2024
9542222
Refine nested-path DSL error messages
MateuszKubuszok Apr 10, 2024
2878c21
Test nested-paths DSL with std lib types, document nested-paths DSL
MateuszKubuszok Apr 10, 2024
99551ff
Initial documentation for nested paths in Chimney DSL
MateuszKubuszok Apr 10, 2024
d1a0d40
Document evidence types and suppress their coverage
MateuszKubuszok Apr 10, 2024
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
Prev Previous commit
Next Next commit
Change everyItem, everyMapKey and everyMapValue implementation in Sca…
…la 3 to something which works better in IDE, add compileTimeOnly/inline+compiletime.error for DSL methods intended to use only within DSL
  • Loading branch information
MateuszKubuszok committed Apr 9, 2024
commit 68ffa2eeeb44003c56fc4b48a7ea43c594877224
34 changes: 19 additions & 15 deletions chimney/src/main/scala-2/io/scalaland/chimney/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package io.scalaland.chimney

import io.scalaland.chimney.internal.runtime.{IsCollection, IsEither, IsMap, IsOption}

import scala.annotation.unused
import scala.annotation.{compileTimeOnly, unused}
import scala.util.Try

/** Imports only extension methods for summoning and using Transformer, PartialTransformer or Patcher
Expand Down Expand Up @@ -181,34 +181,38 @@ package object syntax {
partial.Result.fromTry(`try`)
}

// $COVERAGE-OFF$methods used only within macro-erased expressions

// TODO: docs
implicit final class TransformationMatchingPathOps[A](@unused private val a: A) extends AnyVal {

def matching[B <: A]: B =
sys.error(".matching should be only called within Chimney DSL")
@compileTimeOnly(".matching should be only called within Chimney DSL")
def matching[B <: A]: B = sys.error("")

def matchingSome[SV, S](implicit @unused ev: IsOption.Of[A, SV, S]): SV =
sys.error(".matchingSome should be only called within Chimney DSL")
@compileTimeOnly(".matchingSome should be only called within Chimney DSL")
def matchingSome[SV, S](implicit @unused ev: IsOption.Of[A, SV, S]): SV = sys.error("")

def matchingLeft[LV, RV, L, R](implicit @unused ev: IsEither.Of[A, LV, RV, L, R]): LV =
sys.error(".matchingLeft should be only called within Chimney DSL")
@compileTimeOnly(".matchingLeft should be only called within Chimney DSL")
def matchingLeft[LV, RV, L, R](implicit @unused ev: IsEither.Of[A, LV, RV, L, R]): LV = sys.error("")

def matchingRight[LV, RV, L, R](implicit @unused ev: IsEither.Of[A, LV, RV, L, R]): RV =
sys.error(".matchingRight should be only called within Chimney DSL")
@compileTimeOnly(".matchingRight should be only called within Chimney DSL")
def matchingRight[LV, RV, L, R](implicit @unused ev: IsEither.Of[A, LV, RV, L, R]): RV = sys.error("")
}

implicit final class TransformationCollectionPathOps[C[_], I](@unused private val cc: C[I]) extends AnyVal {

def everyItem(implicit @unused ev: IsCollection.Of[C[I], I]): I =
sys.error(".everyItem should be only called within Chimney DSL")
@compileTimeOnly(".everyItem should be only called within Chimney DSL")
def everyItem(implicit @unused ev: IsCollection.Of[C[I], I]): I = sys.error("")
}

implicit final class TransformationMapPathOps[M[_, _], K, V](@unused private val cc: M[K, V]) extends AnyVal {

def everyMapKey(implicit @unused ev: IsMap.Of[M[K, V], K, V]): K =
sys.error(".everyMapKey should be only called within Chimney DSL")
@compileTimeOnly(".everyMapKey should be only called within Chimney DSL")
def everyMapKey(implicit @unused ev: IsMap.Of[M[K, V], K, V]): K = sys.error("")

def everyMapValue(implicit @unused ev: IsMap.Of[M[K, V], K, V]): V =
sys.error(".everyMapValue should be only called within Chimney DSL")
@compileTimeOnly(".everyMapValue should be only called within Chimney DSL")
def everyMapValue(implicit @unused ev: IsMap.Of[M[K, V], K, V]): V = sys.error("")
}

// $COVERAGE-ON$
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
private object IsOptionOf {
def unapply(term: Term): Option[(ExistentialType, ExistentialType, ExistentialType)] = term.tpe.asType match {
case '[runtime.IsOption.Of[o, sv, s]] => Some((ExistentialType[o], ExistentialType[sv], ExistentialType[s]))
case _ => None

Check warning on line 24 in chimney/src/main/scala-3/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala

View check run for this annotation

Codecov / codecov/patch

chimney/src/main/scala-3/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala#L24

Added line #L24 was not covered by tests
}
}

Expand All @@ -32,7 +32,7 @@
term.tpe.asType match {
case '[runtime.IsEither.Of[e, lv, rv, l, r]] =>
Some((ExistentialType[e], ExistentialType[lv], ExistentialType[rv], ExistentialType[l], ExistentialType[r]))
case _ => None

Check warning on line 35 in chimney/src/main/scala-3/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala

View check run for this annotation

Codecov / codecov/patch

chimney/src/main/scala-3/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala#L35

Added line #L35 was not covered by tests
}
}

Expand All @@ -40,7 +40,7 @@
def unapply(term: Term): Option[(ExistentialType, ExistentialType)] =
term.tpe.asType match {
case '[runtime.IsCollection.Of[c, a]] => Some((ExistentialType[c], ExistentialType[a]))
case _ => None

Check warning on line 43 in chimney/src/main/scala-3/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala

View check run for this annotation

Codecov / codecov/patch

chimney/src/main/scala-3/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala#L43

Added line #L43 was not covered by tests
}
}

Expand All @@ -48,7 +48,7 @@
def unapply(term: Term): Option[(ExistentialType, ExistentialType, ExistentialType)] =
term.tpe.asType match {
case '[runtime.IsMap.Of[m, k, v]] => Some((ExistentialType[m], ExistentialType[k], ExistentialType[v]))
case _ => None

Check warning on line 51 in chimney/src/main/scala-3/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala

View check run for this annotation

Codecov / codecov/patch

chimney/src/main/scala-3/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala#L51

Added line #L51 was not covered by tests
}
}

Expand Down Expand Up @@ -155,7 +155,7 @@
}
}
// matches `.everyItem`
case Apply(TypeApply(Apply(TypeApply(Ident("everyItem"), _), List(t2)), _), List(IsCollectionOf(_, _))) =>
case Apply(Apply(TypeApply(Ident("everyItem"), _), List(t2)), List(IsCollectionOf(_, _))) =>
unpackSelects(t2).map { init =>
import init.Underlying as Init
new ExistentialPath {
Expand All @@ -164,7 +164,7 @@
}
}
// matches `.everyMapKey`
case Apply(TypeApply(Apply(TypeApply(Ident("everyMapKey"), _), List(t2)), _), List(IsMapOf(_, _, _))) =>
case Apply(Apply(TypeApply(Ident("everyMapKey"), _), List(t2)), List(IsMapOf(_, _, _))) =>
unpackSelects(t2).map { init =>
import init.Underlying as Init
new ExistentialPath {
Expand All @@ -173,7 +173,7 @@
}
}
// matches `.everyMapValue`
case Apply(TypeApply(Apply(TypeApply(Ident("everyMapValue"), _), List(t2)), _), List(IsMapOf(_, _, _))) =>
case Apply(Apply(TypeApply(Ident("everyMapValue"), _), List(t2)), List(IsMapOf(_, _, _))) =>
unpackSelects(t2).map { init =>
import init.Underlying as Init
new ExistentialPath {
Expand All @@ -191,7 +191,6 @@
}

private def invalidSelectorErrorMessage(t: Tree): String =
println(t.show(using Printer.TreeStructure))
s"Invalid selector expression: ${t.show(using Printer.TreeAnsiCode)}"

private def arbitraryFunctionNotAllowed(t: Tree): String =
Expand Down
39 changes: 24 additions & 15 deletions chimney/src/main/scala-3/io/scalaland/chimney/syntax/syntax.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.scalaland.chimney.syntax
import io.scalaland.chimney.{partial, PartialTransformer, Patcher, Transformer}
import io.scalaland.chimney.internal.runtime.{IsCollection, IsEither, IsMap, IsOption}

import scala.annotation.unused
import scala.annotation.{compileTimeOnly, unused}
import scala.util.Try

// Extension methods in dsl.* summon TypeClass.AutoDerived while extension methods in syntax.* summon TypeClass.
Expand Down Expand Up @@ -175,27 +175,36 @@ extension [T](`try`: Try[T]) {
partial.Result.fromTry(`try`)
}

// $COVERAGE-OFF$methods used only within macro-erased expressions

// TODO docs
extension [A](@unused a: A) {

def matching[B <: A]: B =
sys.error(".matching should be only called within Chimney DSL")
inline def matching[B <: A]: B = compiletime.error(".matching should be only called within Chimney DSL")

inline def matchingSome[SV, S](implicit @unused ev: IsOption.Of[A, SV, S]): SV =
compiletime.error(".matchingSome should be only called within Chimney DSL")

inline def matchingLeft[LV, RV, L, R](implicit @unused ev: IsEither.Of[A, LV, RV, L, R]): LV =
compiletime.error(".matchingLeft should be only called within Chimney DSL")

def matchingSome[SV, S](implicit @unused ev: IsOption.Of[A, SV, S]): SV =
sys.error(".matchingSome should be only called within Chimney DSL")
inline def matchingRight[LV, RV, L, R](implicit @unused ev: IsEither.Of[A, LV, RV, L, R]): RV =
compiletime.error(".matchingRight should be only called within Chimney DSL")
}

def matchingLeft[LV, RV, L, R](implicit @unused ev: IsEither.Of[A, LV, RV, L, R]): LV =
sys.error(".matchingLeft should be only called within Chimney DSL")
extension [C[_], I](@unused cc: C[I]) {

def matchingRight[LV, RV, L, R](implicit @unused ev: IsEither.Of[A, LV, RV, L, R]): RV =
sys.error(".matchingRight should be only called within Chimney DSL")
inline def everyItem(implicit @unused ev: IsCollection.Of[C[I], I]): I =
compiletime.error(".everyItem should be only called within Chimney DSL")
}

def everyItem[I](implicit @unused ev: IsCollection.Of[A, I]): I =
sys.error(".everyItem should be only called within Chimney DSL")
extension [M[_, _], K, V](@unused cc: M[K, V]) {

def everyMapKey[K, V](implicit @unused ev: IsMap.Of[A, K, V]): K =
sys.error(".everyMapKey should be only called within Chimney DSL")
inline def everyMapKey(implicit @unused ev: IsMap.Of[M[K, V], K, V]): K =
compiletime.error(".everyMapKey should be only called within Chimney DSL")

def everyMapValue[K, V](implicit @unused ev: IsMap.Of[A, K, V]): V =
sys.error(".everyMapValue should be only called within Chimney DSL")
inline def everyMapValue(implicit @unused ev: IsMap.Of[M[K, V], K, V]): V =
compiletime.error(".everyMapValue should be only called within Chimney DSL")
}

// $COVERAGE-ON$
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.scalaland.chimney.dsl.*
import io.scalaland.chimney.fixtures.*

import scala.annotation.unused
import scala.collection.immutable.ListMap

class TotalTransformerProductSpec extends ChimneySpec {

Expand Down Expand Up @@ -158,17 +159,13 @@ class TotalTransformerProductSpec extends ChimneySpec {
.withFieldComputed(_.value.age, _.getValue.age * 2)
.transform ==> NestedProduct(User("John", 20, 140))

val _ = implicitly[io.scalaland.chimney.internal.runtime.IsOption[Option[User]]]
NestedComplex(
Some(Person("John", 10, 140)),
Right(Person("John", 10, 140)),
List(Person("John", 10, 140)),
scala.collection.immutable.ListMap(Person("John", 10, 140) -> Person("John", 10, 140))
ListMap(Person("John", 10, 140) -> Person("John", 10, 140))
).into[NestedComplex[User]]
.withFieldComputed(
_.option.matchingSome(io.scalaland.chimney.internal.runtime.IsOption.optionIsOption).age,
_ => 15
)
.withFieldComputed(_.option.matchingSome.age, _ => 15)
.withFieldComputed(_.either.matchingLeft.age, _ => 20)
.withFieldComputed(_.either.matchingRight.age, _ => 30)
.withFieldComputed(_.collection.everyItem.age, _ => 40)
Expand All @@ -178,7 +175,7 @@ class TotalTransformerProductSpec extends ChimneySpec {
Some(User("John", 15, 140)),
Right(User("John", 30, 140)),
List(User("John", 40, 140)),
scala.collection.immutable.ListMap(User("John", 50, 140) -> User("John", 60, 140))
ListMap(User("John", 50, 140) -> User("John", 60, 140))
)

List[NestedADT[Person]](NestedADT.Foo(Person("John", 10, 140)), NestedADT.Bar(Person("John", 10, 140)))
Expand Down
Loading