Skip to content

Commit

Permalink
Merge very similar Select(Only)FieldGenerator & Parser
Browse files Browse the repository at this point in the history
  • Loading branch information
NTPape committed Mar 9, 2022
1 parent 7b6df5d commit 5204ee7
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package monocle.internal.focus.features

import monocle.internal.focus.FocusBase
import monocle.internal.focus.features.selectfield.SelectFieldGenerator
import monocle.internal.focus.features.selectonlyfield.SelectOnlyFieldGenerator
import monocle.internal.focus.features.some.SomeGenerator
import monocle.internal.focus.features.as.AsGenerator
import monocle.internal.focus.features.each.EachGenerator
Expand All @@ -15,7 +14,6 @@ import scala.quoted.Type
private[focus] trait AllFeatureGenerators
extends FocusBase
with SelectFieldGenerator
with SelectOnlyFieldGenerator
with SomeGenerator
with AsGenerator
with EachGenerator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package monocle.internal.focus.features
import scala.quoted.Type
import monocle.internal.focus.FocusBase
import monocle.internal.focus.features.selectfield.SelectFieldParser
import monocle.internal.focus.features.selectonlyfield.SelectOnlyFieldParser
import monocle.internal.focus.features.some.SomeParser
import monocle.internal.focus.features.as.AsParser
import monocle.internal.focus.features.each.EachParser
Expand All @@ -16,7 +15,6 @@ private[focus] trait AllFeatureParsers
with SelectParserBase
with KeywordParserBase
with SelectFieldParser
with SelectOnlyFieldParser
with SomeParser
with AsParser
with EachParser
Expand Down Expand Up @@ -53,9 +51,6 @@ private[focus] trait ParserLoop {
case KeywordWithDefault(Right(remainingCode, action)) => loop(remainingCode, action :: listSoFar)
case KeywordWithDefault(Left(error)) => Left(error)

case SelectOnlyField(Right(remainingCode, action)) => loop(remainingCode, action :: listSoFar)
case SelectOnlyField(Left(error)) => Left(error)

case SelectField(Right(remainingCode, action)) => loop(remainingCode, action :: listSoFar)
case SelectField(Left(error)) => Left(error)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package monocle.internal.focus.features.selectfield

import monocle.internal.focus.FocusBase
import monocle.Iso
import monocle.Lens

private[focus] trait SelectFieldGenerator {
Expand Down Expand Up @@ -39,4 +40,33 @@ private[focus] trait SelectFieldGenerator {
}.asTerm
}
}

def generateSelectOnlyField(action: FocusAction.SelectOnlyField): Term = {
import action.{fieldName, fromType, fromTypeArgs, fromCompanion, toType}

def generateReverseGet(to: Term): Term =
Select.overloaded(fromCompanion, "apply", fromTypeArgs, List(to)) // Companion.apply(value)

(fromType.asType, toType.asType) match {
case ('[f], '[t]) =>
'{
Iso.apply[f, t]((from: f) => ${ generateGetter('{ from }.asTerm, fieldName).asExprOf[t] })((to: t) =>
${ generateReverseGet('{ to }.asTerm).asExprOf[f] }
)
}.asTerm
}
}

def generateSelectOnlyFieldWithImplicits(action: FocusAction.SelectOnlyFieldWithImplicits): Term = {
import action.{fieldName, fromType, toType, reverseGet}

(fromType.asType, toType.asType) match {
case ('[f], '[t]) =>
'{
Iso.apply[f, t]((from: f) => ${ generateGetter('{ from }.asTerm, fieldName).asExprOf[t] })(
${ reverseGet.asExprOf[t => f] }
)
}.asTerm
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@ private[focus] trait SelectFieldParser {
def unapply(term: Term): Option[FocusResult[(RemainingCode, FocusAction)]] = term match {

case Select(CaseClass(remainingCode, classSymbol), fieldName) =>
val fromType = getType(remainingCode)
val action = if (hasOnlyOneParameterList(classSymbol)) {
getFieldAction(fromType, fieldName)
if (isCaseField(classSymbol, fieldName)) {
val fromType = getType(remainingCode)
val action = (hasOnlyOneParameterList(classSymbol), hasOnlyOneField(classSymbol)) match {
case (true, false) => getSelectFieldAction(fromType, fieldName)
case (false, false) => getSelectFieldActionWithImplicits(fromType, fieldName)
case (true, true) => getSelectOnlyFieldAction(fromType, classSymbol, fieldName)
case (false, true) => getSelectOnlyFieldActionWithImplicits(fromType, classSymbol, fieldName)
}
val remainingCodeWithAction = action.map(a => (RemainingCode(remainingCode), a))
Some(remainingCodeWithAction)
} else {
getFieldActionWithImplicits(fromType, fieldName)
Some(FocusError.NotACaseField(remainingCode.tpe.show, fieldName).asResult)
}
val remainingCodeWithAction = action.map(a => (RemainingCode(remainingCode), a))
Some(remainingCodeWithAction)

case Select(remainingCode, fieldName) =>
Some(FocusError.NotACaseClass(remainingCode.tpe.show, fieldName).asResult)
Expand All @@ -29,47 +34,100 @@ private[focus] trait SelectFieldParser {
}
}

private def isCaseField(classSymbol: Symbol, fieldName: String): Boolean =
classSymbol.caseFields.exists(_.name == fieldName)

private def hasOnlyOneField(classSymbol: Symbol): Boolean =
classSymbol.caseFields.length == 1

private def hasOnlyOneParameterList(classSymbol: Symbol): Boolean =
classSymbol.primaryConstructor.paramSymss match {
case _ :: Nil => true
case (head :: _) :: _ :: Nil if head.isTypeParam => true
case _ => false
}

private def getFieldAction(fromType: TypeRepr, fieldName: String): FocusResult[FocusAction] =
private def getCompanionObject(classSymbol: Symbol): Term =
Ref(classSymbol.companionModule)

private def getSelectFieldAction(fromType: TypeRepr, fieldName: String): FocusResult[FocusAction] =
getFieldType(fromType, fieldName).flatMap { toType =>
Right(FocusAction.SelectField(fieldName, fromType, getSuppliedTypeArgs(fromType), toType))
}

private def getFieldActionWithImplicits(fromType: TypeRepr, fieldName: String): FocusResult[FocusAction] =
private def getSelectFieldActionWithImplicits(fromType: TypeRepr, fieldName: String): FocusResult[FocusAction] =
getFieldType(fromType, fieldName).flatMap { toType =>
val typeArgs = getSuppliedTypeArgs(fromType)
constructSetter(fieldName, fromType, toType, typeArgs).map { setter =>
FocusAction.SelectFieldWithImplicits(fieldName, fromType, toType, setter)
}
}

private def getSelectOnlyFieldAction(
fromType: TypeRepr,
fromClassSymbol: Symbol,
fieldName: String
): FocusResult[FocusAction] =
for {
toType <- getFieldType(fromType, fieldName)
companion = getCompanionObject(fromClassSymbol)
supplied = getSuppliedTypeArgs(fromType)
} yield FocusAction.SelectOnlyField(fieldName, fromType, supplied, companion, toType)

private def getSelectOnlyFieldActionWithImplicits(
fromType: TypeRepr,
fromClassSymbol: Symbol,
fieldName: String
): FocusResult[FocusAction] =
for {
toType <- getFieldType(fromType, fieldName)
companion = getCompanionObject(fromClassSymbol)
supplied = getSuppliedTypeArgs(fromType)
reverseGet <- constructReverseGet(companion, fromType, toType, supplied)
} yield FocusAction.SelectOnlyFieldWithImplicits(fieldName, fromType, toType, reverseGet)

private case class LiftException(error: FocusError) extends Exception

private def liftEtaExpansionResult(term: => Term): FocusResult[Term] =
scala.util.Try(term) match {
case scala.util.Success(term) => Right(term)
case scala.util.Failure(LiftException(error)) => Left(error)
case scala.util.Failure(other) => Left(FocusError.ExpansionFailed(other.toString))
}

private def constructSetter(
fieldName: String,
fromType: TypeRepr,
toType: TypeRepr,
fromTypeArgs: List[TypeRepr]
): FocusResult[Term] =
// Companion.copy(value)(implicits)*
// from.copy(value)(implicits)+
(fromType.asType, toType.asType) match {
case ('[f], '[t]) =>
scala.util.Try('{ (to: t) => (from: f) =>
liftEtaExpansionResult('{ (to: t) => (from: f) =>
${
etaExpandIfNecessary(
Select.overloaded('{ from }.asTerm, "copy", fromTypeArgs, List(NamedArg(fieldName, '{ to }.asTerm)))
).fold(error => throw new LiftException(error), _.asExprOf[f])
}
}.asTerm) match {
case scala.util.Success(term) => Right(term)
case scala.util.Failure(LiftException(error)) => Left(error)
case scala.util.Failure(other) => Left(FocusError.ExpansionFailed(other.toString))
}
}.asTerm)
}

private def constructReverseGet(
companion: Term,
fromType: TypeRepr,
toType: TypeRepr,
fromTypeArgs: List[TypeRepr]
): FocusResult[Term] =
// Companion.apply(value)(implicits)+
(fromType.asType, toType.asType) match {
case ('[f], '[t]) =>
liftEtaExpansionResult('{ (to: t) =>
${
etaExpandIfNecessary(
Select.overloaded(companion, "apply", fromTypeArgs, List('{ to }.asTerm))
).fold(error => throw new LiftException(error), _.asExprOf[f])
}
}.asTerm)
}
}

This file was deleted.

This file was deleted.

0 comments on commit 5204ee7

Please sign in to comment.