Skip to content

Commit 7b6df5d

Browse files
committed
Split Select(Only)Field(WithImplicits)
1 parent 83d4a7a commit 7b6df5d

File tree

8 files changed

+144
-45
lines changed

8 files changed

+144
-45
lines changed

core/shared/src/main/scala-3.x/monocle/internal/focus/ErrorHandling.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ private[focus] trait ErrorHandling {
66
def errorMessage(error: FocusError): String = error match {
77
case FocusError.NotACaseClass(fromClass, fieldName) =>
88
s"Cannot generate Lens for field '$fieldName', because '$fromClass' is not a case class"
9+
case FocusError.NotACaseField(caseClass, fieldName) =>
10+
s"Can only create lenses for case fields, but '$fieldName' is not a case field of '$caseClass'"
911
case FocusError.NotAConcreteClass(fromClass) =>
1012
s"Expecting a concrete case class in the 'From' position; cannot reify type $fromClass"
1113
case FocusError.NotASimpleLambdaFunction =>

core/shared/src/main/scala-3.x/monocle/internal/focus/FocusBase.scala

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,16 @@ private[focus] trait FocusBase {
1313
case class LambdaConfig(argName: String, lambdaBody: Term)
1414

1515
enum FocusAction {
16-
case SelectField(
17-
fieldName: String,
18-
fromType: TypeRepr,
19-
toType: TypeRepr,
20-
setter: Term
21-
)
16+
case SelectField(fieldName: String, fromType: TypeRepr, fromTypeArgs: List[TypeRepr], toType: TypeRepr)
17+
case SelectFieldWithImplicits(fieldName: String, fromType: TypeRepr, toType: TypeRepr, setter: Term)
2218
case SelectOnlyField(
2319
fieldName: String,
2420
fromType: TypeRepr,
25-
toType: TypeRepr,
26-
reverseGet: Term
21+
fromTypeArgs: List[TypeRepr],
22+
fromCompanion: Term,
23+
toType: TypeRepr
2724
)
25+
case SelectOnlyFieldWithImplicits(fieldName: String, fromType: TypeRepr, toType: TypeRepr, reverseGet: Term)
2826
case KeywordSome(toType: TypeRepr)
2927
case KeywordAs(fromType: TypeRepr, toType: TypeRepr)
3028
case KeywordEach(fromType: TypeRepr, toType: TypeRepr, eachInstance: Term)
@@ -33,10 +31,14 @@ private[focus] trait FocusBase {
3331
case KeywordWithDefault(toType: TypeRepr, defaultValue: Term)
3432

3533
override def toString(): String = this match {
36-
case SelectField(fieldName, fromType, toType, setter) =>
37-
s"SelectField($fieldName, ${fromType.show}, ${toType.show}, ${setter.asExpr.show})"
38-
case SelectOnlyField(fieldName, fromType, toType, reverseGet) =>
39-
s"SelectOnlyField($fieldName, ${fromType.show}, ${toType.show}, ${reverseGet.asExpr.show})"
34+
case SelectField(fieldName, fromType, fromTypeArgs, toType) =>
35+
s"SelectField($fieldName, ${fromType.show}, ${fromTypeArgs.map(_.show)}, ${toType.show})"
36+
case SelectFieldWithImplicits(fieldName, fromType, toType, setter) =>
37+
s"SelectFieldWithImplicits($fieldName, ${fromType.show}, ${toType.show}, ...)"
38+
case SelectOnlyField(fieldName, fromType, fromTypeArgs, _, toType) =>
39+
s"SelectOnlyField($fieldName, ${fromType.show}, ${fromTypeArgs.map(_.show)}, ..., ${toType.show})"
40+
case SelectOnlyFieldWithImplicits(fieldName, fromType, toType, reverseGet) =>
41+
s"SelectOnlyFieldWithImplicits($fieldName, ${fromType.show}, ${toType.show}, ...)"
4042
case KeywordSome(toType) => s"KeywordSome(${toType.show})"
4143
case KeywordAs(fromType, toType) => s"KeywordAs(${fromType.show}, ${toType.show})"
4244
case KeywordEach(fromType, toType, _) => s"KeywordEach(${fromType.show}, ${toType.show}, ...)"
@@ -48,6 +50,7 @@ private[focus] trait FocusBase {
4850

4951
enum FocusError {
5052
case NotACaseClass(className: String, fieldName: String)
53+
case NotACaseField(className: String, fieldName: String)
5154
case NotAConcreteClass(className: String)
5255
case DidNotDirectlyAccessArgument(argName: String)
5356
case NotASimpleLambdaFunction

core/shared/src/main/scala-3.x/monocle/internal/focus/features/GeneratorLoop.scala

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,16 @@ private[focus] trait GeneratorLoop {
3939

4040
private def generateActionCode(action: FocusAction): Term =
4141
action match {
42-
case a: FocusAction.SelectField => generateSelectField(a)
43-
case a: FocusAction.SelectOnlyField => generateSelectOnlyField(a)
44-
case a: FocusAction.KeywordSome => generateSome(a)
45-
case a: FocusAction.KeywordAs => generateAs(a)
46-
case a: FocusAction.KeywordEach => generateEach(a)
47-
case a: FocusAction.KeywordAt => generateAt(a)
48-
case a: FocusAction.KeywordIndex => generateIndex(a)
49-
case a: FocusAction.KeywordWithDefault => generateWithDefault(a)
42+
case a: FocusAction.SelectField => generateSelectField(a)
43+
case a: FocusAction.SelectFieldWithImplicits => generateSelectFieldWithImplicits(a)
44+
case a: FocusAction.SelectOnlyField => generateSelectOnlyField(a)
45+
case a: FocusAction.SelectOnlyFieldWithImplicits => generateSelectOnlyFieldWithImplicits(a)
46+
case a: FocusAction.KeywordSome => generateSome(a)
47+
case a: FocusAction.KeywordAs => generateAs(a)
48+
case a: FocusAction.KeywordEach => generateEach(a)
49+
case a: FocusAction.KeywordAt => generateAt(a)
50+
case a: FocusAction.KeywordIndex => generateIndex(a)
51+
case a: FocusAction.KeywordWithDefault => generateWithDefault(a)
5052
}
5153

5254
private def composeOptics(lens1: Term, lens2: Term): FocusResult[Term] =

core/shared/src/main/scala-3.x/monocle/internal/focus/features/SelectParserBase.scala

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ private[focus] trait SelectParserBase extends ParserBase {
1313

1414
// Match on a term that is an instance of a case class
1515
object CaseClass {
16-
def unapply(term: Term): Option[Term] =
16+
def unapply(term: Term): Option[(Term, Symbol)] =
1717
term.tpe.classSymbol.flatMap { sym =>
18-
Option.when(sym.flags.is(Flags.Case))(term)
18+
Option.when(sym.flags.is(Flags.Case))((term, sym))
1919
}
2020
}
2121

@@ -86,11 +86,17 @@ private[focus] trait SelectParserBase extends ParserBase {
8686
case Block(List(DefDef(_, List(params), _, _)), _) =>
8787
params.params.foldLeft[FocusResult[List[Term]]](Right(List.empty[Term])) {
8888
case (Right(acc), ValDef(_, t, _)) =>
89-
val typeRepr: TypeRepr = t.tpe.dealias
90-
Implicits.search(typeRepr) match {
91-
case success: ImplicitSearchSuccess => Right(success.tree :: acc)
92-
case _ => FocusError.ImplicitNotFound(typeRepr.show).asResult
93-
}
89+
def searchForImplicit(typeRepr: TypeRepr): FocusResult[Term] =
90+
Implicits.search(typeRepr) match {
91+
case success: ImplicitSearchSuccess => Right(success.tree)
92+
case _ => FocusError.ImplicitNotFound(typeRepr.show).asResult
93+
}
94+
searchForImplicit(t.tpe)
95+
.orElse(searchForImplicit(t.tpe.dealias))
96+
.orElse(searchForImplicit(t.tpe.widen))
97+
.orElse(searchForImplicit(t.tpe.widen.dealias))
98+
.map(acc :+ _)
99+
94100
case (Right(acc), other) =>
95101
FocusError.ExpansionFailed(s"Expected value definition but found unexpected ${other.show}").asResult
96102
case (left @ Left(_), _) =>

core/shared/src/main/scala-3.x/monocle/internal/focus/features/selectfield/SelectFieldGenerator.scala

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,32 @@ private[focus] trait SelectFieldGenerator {
88

99
import macroContext.reflect._
1010

11+
private def generateGetter(from: Term, fieldName: String): Term =
12+
Select.unique(from, fieldName) // o.field
13+
1114
def generateSelectField(action: FocusAction.SelectField): Term = {
15+
import action.{fieldName, fromType, fromTypeArgs, toType}
16+
17+
def generateSetter(from: Term, to: Term): Term =
18+
Select.overloaded(from, "copy", fromTypeArgs, NamedArg(fieldName, to) :: Nil) // o.copy(field = value)
19+
20+
(fromType.asType, toType.asType) match {
21+
case ('[f], '[t]) =>
22+
'{
23+
Lens.apply[f, t]((from: f) => ${ generateGetter('{ from }.asTerm, fieldName).asExprOf[t] })((to: t) =>
24+
(from: f) => ${ generateSetter('{ from }.asTerm, '{ to }.asTerm).asExprOf[f] }
25+
)
26+
}.asTerm
27+
}
28+
}
29+
30+
def generateSelectFieldWithImplicits(action: FocusAction.SelectFieldWithImplicits): Term = {
1231
import action.{fieldName, fromType, toType, setter}
1332

1433
(fromType.asType, toType.asType) match {
1534
case ('[f], '[t]) =>
1635
'{
17-
Lens.apply[f, t]((from: f) => ${ Select.unique('{ from }.asTerm, fieldName).asExprOf[t] })(
36+
Lens.apply[f, t]((from: f) => ${ generateGetter('{ from }.asTerm, fieldName).asExprOf[t] })(
1837
${ setter.asExprOf[t => f => f] }
1938
)
2039
}.asTerm

core/shared/src/main/scala-3.x/monocle/internal/focus/features/selectfield/SelectFieldParser.scala

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ private[focus] trait SelectFieldParser {
1212

1313
def unapply(term: Term): Option[FocusResult[(RemainingCode, FocusAction)]] = term match {
1414

15-
case Select(CaseClass(remainingCode), fieldName) =>
16-
val fromType = getType(remainingCode)
17-
val action = getFieldAction(fromType, fieldName)
15+
case Select(CaseClass(remainingCode, classSymbol), fieldName) =>
16+
val fromType = getType(remainingCode)
17+
val action = if (hasOnlyOneParameterList(classSymbol)) {
18+
getFieldAction(fromType, fieldName)
19+
} else {
20+
getFieldActionWithImplicits(fromType, fieldName)
21+
}
1822
val remainingCodeWithAction = action.map(a => (RemainingCode(remainingCode), a))
1923
Some(remainingCodeWithAction)
2024

@@ -25,11 +29,23 @@ private[focus] trait SelectFieldParser {
2529
}
2630
}
2731

32+
private def hasOnlyOneParameterList(classSymbol: Symbol): Boolean =
33+
classSymbol.primaryConstructor.paramSymss match {
34+
case _ :: Nil => true
35+
case (head :: _) :: _ :: Nil if head.isTypeParam => true
36+
case _ => false
37+
}
38+
2839
private def getFieldAction(fromType: TypeRepr, fieldName: String): FocusResult[FocusAction] =
40+
getFieldType(fromType, fieldName).flatMap { toType =>
41+
Right(FocusAction.SelectField(fieldName, fromType, getSuppliedTypeArgs(fromType), toType))
42+
}
43+
44+
private def getFieldActionWithImplicits(fromType: TypeRepr, fieldName: String): FocusResult[FocusAction] =
2945
getFieldType(fromType, fieldName).flatMap { toType =>
3046
val typeArgs = getSuppliedTypeArgs(fromType)
3147
constructSetter(fieldName, fromType, toType, typeArgs).map { setter =>
32-
FocusAction.SelectField(fieldName, fromType, toType, setter)
48+
FocusAction.SelectFieldWithImplicits(fieldName, fromType, toType, setter)
3349
}
3450
}
3551

core/shared/src/main/scala-3.x/monocle/internal/focus/features/selectonlyfield/SelectOnlyFieldGenerator.scala

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,32 @@ private[focus] trait SelectOnlyFieldGenerator {
88

99
import macroContext.reflect._
1010

11+
private def generateGetter(from: Term, fieldName: String): Term =
12+
Select.unique(from, fieldName) // o.field
13+
1114
def generateSelectOnlyField(action: FocusAction.SelectOnlyField): Term = {
15+
import action.{fieldName, fromType, fromTypeArgs, fromCompanion, toType}
16+
17+
def generateReverseGet(to: Term): Term =
18+
Select.overloaded(fromCompanion, "apply", fromTypeArgs, List(to)) // Companion.apply(value)
19+
20+
(fromType.asType, toType.asType) match {
21+
case ('[f], '[t]) =>
22+
'{
23+
Iso.apply[f, t]((from: f) => ${ generateGetter('{ from }.asTerm, fieldName).asExprOf[t] })((to: t) =>
24+
${ generateReverseGet('{ to }.asTerm).asExprOf[f] }
25+
)
26+
}.asTerm
27+
}
28+
}
29+
30+
def generateSelectOnlyFieldWithImplicits(action: FocusAction.SelectOnlyFieldWithImplicits): Term = {
1231
import action.{fieldName, fromType, toType, reverseGet}
1332

1433
(fromType.asType, toType.asType) match {
1534
case ('[f], '[t]) =>
1635
'{
17-
Iso.apply[f, t]((from: f) => ${ Select.unique('{ from }.asTerm, fieldName).asExprOf[t] })(
36+
Iso.apply[f, t]((from: f) => ${ generateGetter('{ from }.asTerm, fieldName).asExprOf[t] })(
1837
${ reverseGet.asExprOf[t => f] }
1938
)
2039
}.asTerm

core/shared/src/main/scala-3.x/monocle/internal/focus/features/selectonlyfield/SelectOnlyFieldParser.scala

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,61 @@ private[focus] trait SelectOnlyFieldParser {
1212

1313
def unapply(term: Term): Option[FocusResult[(RemainingCode, FocusAction)]] = term match {
1414

15-
case Select(CaseClass(remainingCode), fieldName) if hasOnlyOneField(remainingCode) =>
16-
val fromType = getType(remainingCode)
17-
val action = getFieldAction(fromType, fieldName)
15+
case Select(CaseClass(remainingCode, classSymbol), fieldName) if notACaseField(classSymbol, fieldName) =>
16+
Some(FocusError.NotACaseField(remainingCode.tpe.show, fieldName).asResult)
17+
18+
case Select(CaseClass(remainingCode, classSymbol), fieldName) if hasOnlyOneField(classSymbol) =>
19+
val fromType = getType(remainingCode)
20+
val action = if (hasOnlyOneParameterList(classSymbol)) {
21+
getFieldAction(fromType, classSymbol, fieldName)
22+
} else {
23+
getFieldActionWithImplicits(fromType, classSymbol, fieldName)
24+
}
1825
val remainingCodeWithAction = action.map(a => (RemainingCode(remainingCode), a))
1926
Some(remainingCodeWithAction)
2027

2128
case _ => None
2229
}
2330
}
2431

25-
private def getFieldAction(fromType: TypeRepr, fieldName: String): FocusResult[FocusAction] =
32+
private def getFieldAction(
33+
fromType: TypeRepr,
34+
fromClassSymbol: Symbol,
35+
fieldName: String
36+
): FocusResult[FocusAction] =
2637
for {
27-
toType <- getFieldType(fromType, fieldName)
28-
companion <- getCompanionObject(fromType)
29-
supplied = getSuppliedTypeArgs(fromType)
38+
toType <- getFieldType(fromType, fieldName)
39+
companion = getCompanionObject(fromClassSymbol)
40+
supplied = getSuppliedTypeArgs(fromType)
41+
} yield FocusAction.SelectOnlyField(fieldName, fromType, supplied, companion, toType)
42+
43+
private def getFieldActionWithImplicits(
44+
fromType: TypeRepr,
45+
fromClassSymbol: Symbol,
46+
fieldName: String
47+
): FocusResult[FocusAction] =
48+
for {
49+
toType <- getFieldType(fromType, fieldName)
50+
companion = getCompanionObject(fromClassSymbol)
51+
supplied = getSuppliedTypeArgs(fromType)
3052
reverseGet <- constructReverseGet(companion, fromType, toType, supplied)
31-
} yield FocusAction.SelectOnlyField(fieldName, fromType, toType, reverseGet)
53+
} yield FocusAction.SelectOnlyFieldWithImplicits(fieldName, fromType, toType, reverseGet)
54+
55+
private def hasOnlyOneField(classSymbol: Symbol): Boolean =
56+
classSymbol.caseFields.length == 1
3257

33-
private def hasOnlyOneField(fromCode: Term): Boolean =
34-
getType(fromCode).classSymbol.exists(_.caseFields.length == 1)
58+
private def notACaseField(classSymbol: Symbol, fieldName: String): Boolean =
59+
classSymbol.caseFields.forall(_.name != fieldName)
60+
61+
private def hasOnlyOneParameterList(classSymbol: Symbol): Boolean =
62+
classSymbol.primaryConstructor.paramSymss match {
63+
case _ :: Nil => true
64+
case (head :: _) :: _ :: Nil if head.isTypeParam => true
65+
case _ => false
66+
}
3567

36-
private def getCompanionObject(fromType: TypeRepr): FocusResult[Term] =
37-
getClassSymbol(fromType).map(sym => Ref(sym.companionModule))
68+
private def getCompanionObject(classSymbol: Symbol): Term =
69+
Ref(classSymbol.companionModule)
3870

3971
private case class LiftException(error: FocusError) extends Exception
4072

0 commit comments

Comments
 (0)