8000 [Issue #75] Add support for field/case renames via flags by arainko · Pull Request #252 · arainko/ducktape · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[Issue #75] Add support for field/case renames via flags #252

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

Merged
merged 30 commits into from
Mar 11, 2025

Conversation

arainko
Copy link
Owner
@arainko arainko commented Mar 8, 2025

Closes #75

Field and case renames are now possible with the newly introduced config options:

  • Field.modifySourceNames
  • Field.modifyDestNames
  • Case.modifySourceNames
  • Case.modifyDestNames

All of them can be further configured in 3 ways:

  • .regional(_.path.to.field) - this will apply renames to all transformations 'below' the path you've picked (this is the default)
  • .typeSpecific[SomeType] - this will apply the renames to subtypes of a type you've picked
  • .local(_.path.to.field) - this will apply renames to the field corresponding to that field/enum subcase (but not the field and the enum subcase themselves!)

Examples of regional configs:

 test("dest field regional flag covers the selected case class and everything below it") {
    case class Source(int: Int, str: String, level1: SourceLevel1)
    case class SourceLevel1(INT: Int, STR: String, LEVEL2: SourceLevel2)
    case class SourceLevel2(INT: Int, STR: String)

    case class Dest(int: Int, str: String, level1: DestLevel1)
    case class DestLevel1(INT: Int, STR: String, LEVEL2: DestLevel2)
    case class DestLevel2(INT: Int, STR: String)

    val source = Source(1, "1", SourceLevel1(2, "2", SourceLevel2(3, "3")))
    val expected = Dest(1, "1", DestLevel1(2, "2", DestLevel2(3, "3")))

    assertTransformsConfigured(source, expected)(
      Field.modifyDestNames(_.toUpperCase).regional(_.level1)
    )
  }
  test("source case regional flag covers the selected subtype and everything below (picked as a field in case class)") {
    case class Source(int: Int, level1: SourceEnum)
    case class Dest(int: Int, level1: DestEnum)

    enum DestEnum {
      case one(int: Int, str: String)
      case two(int: Int, str: String, level1: DestLevel1, level2: DestLevel1Enum)
      case three(int: Int, str: String)
    }

    enum SourceEnum {
      case ONE(int: Int, str: String)
      case TWO(int: Int, str: String, level1: SourceLevel1, level2: SourceLevel1Enum)
      case THREE(int: Int, str: String)
    }

    enum SourceLevel1Enum {
      case One
      case Two
    }

    enum DestLevel1Enum {
      case one
      case two
    }

    case class SourceLevel1(int: Int)
    case class DestLevel1(int: Int)

    assertTransformsConfigured(
      Source(1, SourceEnum.TWO(2, "2", SourceLevel1(3), SourceLevel1Enum.Two)),
      Dest(1, DestE
8000
num.two(2, "2", DestLevel1(3), DestLevel1Enum.two))
    )(
      Case.modifySourceNames(_.toLowerCase).regional(_.level1)
    )
  }

Examples of local flags:

test("dest field local flag covers the selected case class and nothing else") {
    case class Source(int: Int, str: String, level1: SourceLevel1)
    case class SourceLevel1(INT: Int, STR: String, LEVEL2: SourceLevel2)
    case class SourceLevel2(int: Int, str: String)

    case class Dest(int: Int, str: String, level1: DestLevel1)
    case class DestLevel1(int: Int, str: String, level2: DestLevel2)
    case class DestLevel2(int: Int, str: String)

    val source = Source(1, "1", SourceLevel1(2, "2", SourceLevel2(3, "3")))
    val expected = Dest(1, "1", DestLevel1(2, "2", DestLevel2(3, "3")))

    assertTransformsConfigured(source, expected)(
      Field.modifyDestNames(_.toUpperCase).local(_.level1)
    )
  }

and an example of a type specific flags:

 test("source field type specific flag covers all subtypes of an enum and nothing else (even when the enum is nested)") {
    case class Source(int: Int, level1: SourceEnum)
    case class Dest(int: Int, level1: DestEnum)

    sealed trait SourceEnum

    object SourceEnum {
      sealed trait NestLevel1 extends SourceEnum
      sealed trait NestLevel2 extends SourceEnum

      case class One(int: Int, str: String) extends NestLevel2
      case class Two(int: Int, str: String, level1: SourceLevel1) extends NestLevel1
      case class Three(int: Int, str: String) extends NestLevel2
    }

    sealed trait DestEnum

    object DestEnum {
      sealed trait NestLevel1 extends DestEnum
      sealed trait NestLevel2 extends DestEnum

      case class One(INT: Int, STR: String) extends NestLevel2
      case class Two(INT: Int, STR: String, LEVEL1: DestLevel1) extends NestLevel1
      case class Three(INT: Int, STR: String) extends NestLevel2
    }

    case class SourceLevel1(int: Int)
    case class DestLevel1(int: Int)

    assertTransformsConfigured(
      Source(1, SourceEnum.Two(2, "2", SourceLevel1(3))),
      Dest(1, DestEnum.Two(2, "2", DestLevel1(3)))
    )(
      Field.modifySourceNames(_.toUpperCase).typeSpecific[SourceEnum]
    )
  }

The renames themselves can be mixed and matched with the limitation of being expressible at compiletime using this DSL:

sealed trait Renamer {
  def toUpperCase: Renamer

  def toLowerCase: Renamer

  def rename(from: String, to: String): Renamer

  def replace(target: String, replacement: String): Renamer

  def regexReplace(pattern: String, replacement: String): Renamer
}

(adding new operations is pretty trivial)

…getOrElse to work on a partial function instead
…ng and wire it up with flags and the config parser
…n if it's a sealed trait with next sealed traits underneath)
@arainko arainko merged commit 23911b4 into series/0.2.x Mar 11, 2025
13 checks passed
@arainko arainko deleted the planner-flags-poc branch March 11, 2025 06:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mapping field names based on a constant function
1 participant
0