Skip to content

Commit

Permalink
allow ambiguities to be fixed
Browse files Browse the repository at this point in the history
  • Loading branch information
arainko committed Dec 1, 2024
1 parent 2ec3c8b commit ffde5e4
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,17 @@ private[ducktape] object ErrorMessage {
}
}

final case class AmbiguousFieldTransformations(tpe: Structure.Product, transformationFunction: String => String) extends ErrorMessage {
def render(using Quotes): String = {
val ambNames = tpe.fields.keys.groupBy(transformationFunction).collect {
case (key, names) if names.size >= 2 =>
s" * '$key' maps to more than one field name: ${names.map(name => s"'$name'").mkString(", ")}"
}.mkString(System.lineSeparator(), System.lineSeparator(), "")
s"Field name transformation results in ambiguity for fields in ${tpe.tpe.repr.show}:$ambNames"
}
final case class AmbiguousFieldTransformations(
tpe: Type[?],
fieldName: String,
transformedFieldName: String,
ambiguities: Vector[String],
) extends ErrorMessage {
def render(using Quotes): String =
s"Field '$fieldName' (transformed to '$transformedFieldName') in ${tpe.repr.show} maps to more than one field name: ${ambiguities.map(name => s"'$name'").mkString(", ")}"

val side = Side.Dest
val span = None
val side = Side.Dest
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -153,38 +153,43 @@ private[ducktape] object Planner {
def transformDestName(name: String) = name.toUpperCase()
def transformSrcName(name: String): String = name.toUpperCase()

val transformedDest = dest.fields
.map((destField, destFieldStruct) => transformDestName(destField) -> (destField, destFieldStruct))

val transformedSource = source.fields
.map((srcField, srcFieldStruct) => transformSrcName(srcField) -> (srcField, srcFieldStruct))

if transformedDest.keySet.size != dest.fields.keySet.size then
Plan.Error(source, dest, ErrorMessage.AmbiguousFieldTransformations(dest, transformDestName), None)
else if transformedSource.keySet.size != source.fields.keySet.size then
Plan.Error(source, dest, ErrorMessage.AmbiguousFieldTransformations(source, transformSrcName), None)
else {
val fieldPlans = transformedDest.map {
case transformedDestField -> (destField, destFieldStruct) =>
val plan =
transformedSource
.get(transformedDestField)
.map((srcField, srcStruct) => FieldPlan(srcField, recurse(srcStruct, destFieldStruct)))
.getOrElse(
FieldPlan.empty(
Plan.Error(
Structure.of[Nothing](source.path),
destFieldStruct,
ErrorMessage.NoFieldFound(transformedDestField, destFieldStruct.tpe, source.tpe),
None
)
// keys to transformed keys
val destAmbiguities = dest.fields.keys.groupBy(transformDestName).filter((_, ambs) => ambs.size > 1)
val sourceAmbiguities = source.fields.keys.groupBy(transformSrcName).filter((_, ambs) => ambs.size > 1)

val transformedSource =
source.fields
.map((srcField, srcFieldStruct) => transformSrcName(srcField) -> (srcField, srcFieldStruct))

val fieldPlans = dest.fields.map { (destField, destFieldStruct) =>
val transformedDestField = transformDestName(destField)
val destAmbs = destAmbiguities.getOrElse(transformedDestField, Vector.empty)
val sourceAmbs = sourceAmbiguities.getOrElse(transformedDestField, Vector.empty)

if destAmbs.nonEmpty then
destField -> FieldPlan.empty(Plan.Error(source, destFieldStruct, ErrorMessage.AmbiguousFieldTransformations(dest.tpe, destField, transformedDestField, destAmbs), None))
else if sourceAmbs.nonEmpty then
destField -> FieldPlan.empty(Plan.Error(source, destFieldStruct, ErrorMessage.AmbiguousFieldTransformations(source.tpe, destField, transformSrcName(destField), sourceAmbs), None))
else {
val plan =
transformedSource
.get(transformedDestField)
.map((srcField, srcStruct) => FieldPlan(srcField, recurse(srcStruct, destFieldStruct)))
.getOrElse(
FieldPlan.empty(
Plan.Error(
Structure.of[Nothing](source.path),
destFieldStruct,
ErrorMessage.NoFieldFound(transformedDestField, destFieldStruct.tpe, source.tpe),
None
)
)
)

destField -> plan
destField -> plan
}
Plan.BetweenProducts(source, dest, fieldPlans)
}
Plan.BetweenProducts(source, dest, fieldPlans)
}

private def positionWisePlans[F <: Fallible](
Expand Down
12 changes: 8 additions & 4 deletions ducktape/src/main/scala/io/github/arainko/ducktape/test.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package io.github.arainko.ducktape


case class Source(INT: Int, str: String)
case class Source(INT: Int, int: Int, str: String, STR: String)

case class Dest(int: Int)
case class Dest(str: String, int: Int)

/*
Structure -> INT, int
Expand All @@ -19,8 +19,12 @@ Field.transformFields(FieldName.toUpperCase ~ FieldName.)

object test {

Transformer.Debug.showCode:
Source(1, "a").to[Dest]
// Transformer.Debug.showCode:
Source(1, 2, "a", "b")
.into[Dest]
.transform(
Field.const(_.str, "")
)



Expand Down

0 comments on commit ffde5e4

Please sign in to comment.