Skip to content

Commit

Permalink
format: Use scalafmt to automate format and check format
Browse files Browse the repository at this point in the history
  • Loading branch information
tmccombs committed Sep 27, 2023
1 parent fd73b7a commit 3afeea8
Show file tree
Hide file tree
Showing 24 changed files with 1,182 additions and 444 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- name: Build
run: sbt package
- name: Test
run: sbt test
run: sbt test scalafmtCheckAll
- uses: actions/upload-artifact@v3
with:
name: jars
Expand Down
20 changes: 20 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version = 3.7.14
runner.dialect = scala213source3
align = none
align.openParenCallSite = false
align.openParenDefnSite = false
continuationIndent.defnSite = 2
danglingParentheses.preset = true
docstrings.style = Asterisk
maxColumn = 120
importSelectors = singleLine
rewrite.redundantBraces.stringInterpolation = true
rewrite.rules = [
RedundantParens,
PreferCurlyFors,
SortImports,
]
runner.fatalWarnings = true
# old versions of scala don't support trailing commas
trailingCommas = never
newlines.afterCurlyLambdaParams = keep
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,17 @@ class RowParserImpl(val c: Context) {

val opts: AnnotOpts = c.prefix.tree match {
case q"new Record(..$params)" =>
val paramTrees: Map[String, Tree] = params.map {
case q"$optNameAst -> $optValueAst" =>
val optName = optNameAst match {
case Literal(Constant(optName: String)) => optName
case name => c.abort(name.pos, "Keys must be literal strings")
}

if (!validOptions.contains(optName)) {
c.abort(optNameAst.pos, s"$optName is an invalid option. Valid options: ${validOptions.mkString(", ")}")
}

optName -> optValueAst
val paramTrees: Map[String, Tree] = params.map { case q"$optNameAst -> $optValueAst" =>
val optName = optNameAst match {
case Literal(Constant(optName: String)) => optName
case name => c.abort(name.pos, "Keys must be literal strings")
}

if (!validOptions.contains(optName)) {
c.abort(optNameAst.pos, s"$optName is an invalid option. Valid options: ${validOptions.mkString(", ")}")
}

optName -> optValueAst
}.toMap

if (paramTrees.contains("colMapping") && paramTrees.contains("snakeCase")) {
Expand All @@ -46,15 +45,17 @@ class RowParserImpl(val c: Context) {

paramTrees.foldLeft(AnnotOpts(false, Map.empty)) { case (opts, (optName, optValueAst)) =>
optName match {
case "colMapping" => optValueAst match {
case q"Map[..$tpts](..$params)" =>
opts.copy(remapping = getRemapping(params))
}
case "snakeCase" => optValueAst match {
case q"true" => opts.copy(snakeCase = true)
case q"false" => opts.copy(snakeCase = false)
case value => c.abort(value.pos, "snakeCase requires a literal true or false value")
}
case "colMapping" =>
optValueAst match {
case q"Map[..$tpts](..$params)" =>
opts.copy(remapping = getRemapping(params))
}
case "snakeCase" =>
optValueAst match {
case q"true" => opts.copy(snakeCase = true)
case q"false" => opts.copy(snakeCase = false)
case value => c.abort(value.pos, "snakeCase requires a literal true or false value")
}
}
}
case q"new Record()" => AnnotOpts(false, Map.empty)
Expand All @@ -64,7 +65,6 @@ class RowParserImpl(val c: Context) {

val result: List[Tree] = inputs match {
case target @ q"case class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>

val params = paramss.head

val paramNames = params.map(_.name.toString).toSet
Expand Down Expand Up @@ -134,13 +134,13 @@ class RowParserImpl(val c: Context) {
private def tupleValueString(tupleTree: Tree): String = {
val remapAst = tupleTree match {
case q"$aa($colLit).$arrow[..$tpts]($remapAst)" => remapAst
case q"$col -> $remapAst" => remapAst
case q"($col, $remapAst)" => remapAst
case q"$col -> $remapAst" => remapAst
case q"($col, $remapAst)" => remapAst
}

remapAst match {
case Literal(Constant(remap: String)) => remap
case value => c.abort(value.pos, "Remappings must be literal strings")
case value => c.abort(value.pos, "Remappings must be literal strings")
}
}

Expand Down Expand Up @@ -189,11 +189,16 @@ class RowParserImpl(val c: Context) {
}
}

private def toSnakeCase(s: String): String = s.replaceAll(
"([A-Z]+)([A-Z][a-z])", "$1_$2"
).replaceAll(
"([a-z\\d])([A-Z])", "$1_$2"
).toLowerCase
private def toSnakeCase(s: String): String = s
.replaceAll(
"([A-Z]+)([A-Z][a-z])",
"$1_$2"
)
.replaceAll(
"([a-z\\d])([A-Z])",
"$1_$2"
)
.toLowerCase

private def findCaseClassFields(ty: Type): List[(TermName, Type)] = {
ty.members.sorted.collect {
Expand All @@ -204,17 +209,17 @@ class RowParserImpl(val c: Context) {
private def expand(colLit: Tree, tree: Tree): (String, Tree) = {
val col = colLit match {
case Literal(Constant(col: String)) => col
case _ => c.abort(colLit.pos, "Column names must be literal strings")
case _ => c.abort(colLit.pos, "Column names must be literal strings")
}
col -> tree
}

private def getRemapping(params: List[Tree]): Map[String, Tree] = {
params.map {
case tree @ q"$aa($colLit).$arrow[..$tpts]($remapLit)" => expand(colLit, tree)
case tree @ q"$colLit -> $remapLit" => expand(colLit, tree)
case tree @ q"($colLit, $remapLit)" => expand(colLit, tree)
case tree => c.abort(tree.pos, "Remappings must be literal tuples")
case tree @ q"$colLit -> $remapLit" => expand(colLit, tree)
case tree @ q"($colLit, $remapLit)" => expand(colLit, tree)
case tree => c.abort(tree.pos, "Remappings must be literal tuples")
}.toMap
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@ case class Big(
a22: Int,
a23: Int,
a24: Option[Int],
a25: Int
a25: Int,
) {
val m1: Int = 0
def m2: Int = 0
}


class RowParserTest extends Specification with Mockito {
class MockableRow extends SqlRow(null) {
final override def apply[A: ColReader](col: String): A = super.apply(col)
Expand All @@ -57,7 +56,7 @@ class RowParserTest extends Specification with Mockito {

val p = generateParser[Thing]

p.parse(row) mustEqual(Thing("hi", Some(20)))
p.parse(row) mustEqual (Thing("hi", Some(20)))
}

"generate parser w/snake_case columns" in {
Expand All @@ -67,14 +66,16 @@ class RowParserTest extends Specification with Mockito {

val p = generateSnakeParser[Thing]

p.parse(row) mustEqual(Thing("gregg", Some(20)))
p.parse(row) mustEqual (Thing("gregg", Some(20)))
}

"remap column names" in {
val p = generateParser[User](Map(
"firstName" -> "fname",
"lastName" -> "lname"
))
val p = generateParser[User](
Map(
"firstName" -> "fname",
"lastName" -> "lname",
),
)

val row = mock[MockableRow]
row.stringOption("fname") returns Some("gregg")
Expand All @@ -84,10 +85,12 @@ class RowParserTest extends Specification with Mockito {
}

"remap column names w/normal tuple syntax" in {
val p = generateParser[User](Map(
("firstName", "fname"),
("lastName", "lname")
))
val p = generateParser[User](
Map(
("firstName", "fname"),
("lastName", "lname"),
),
)

val row = mock[MockableRow]
row.stringOption("fname") returns Some("gregg")
Expand All @@ -97,9 +100,11 @@ class RowParserTest extends Specification with Mockito {
}

"remap some column names" in {
val p = generateParser[User](Map(
"firstName" -> "fname"
))
val p = generateParser[User](
Map(
"firstName" -> "fname",
),
)

val row = mock[MockableRow]
row.stringOption("fname") returns Some("gregg")
Expand All @@ -110,29 +115,50 @@ class RowParserTest extends Specification with Mockito {

"generate parser for a case class > 22 fields" in {
val row = mock[MockableRow]
for (i <- (1 to 9)) { row.intOption(s"f${i}") returns Some(i) }
for (i <- (10 to 19)) { row.intOption(s"z${i}") returns Some(i) }
for (i <- (20 to 25)) { row.intOption(s"a${i}") returns Some(i) }

for (i <- 1 to 9) { row.intOption(s"f${i}") returns Some(i) }
for (i <- 10 to 19) { row.intOption(s"z${i}") returns Some(i) }
for (i <- 20 to 25) { row.intOption(s"a${i}") returns Some(i) }

val p = generateParser[Big]

p.parse(row) mustEqual(Big(
1, Some(2), 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, Some(24), 25)
)
p.parse(row) mustEqual (Big(
1,
Some(2),
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
Some(24),
25,
))
}

"fail to compile with non-literals" in {
illTyped(
"""val name = "newName"; generateParser[User](Map("firstName" -> name))""",
"Remappings must be literal strings"
"Remappings must be literal strings",
)

illTyped(
"""val col = "col"; generateParser[User](Map(col -> "name"))""",
"Column names must be literal strings"
"Column names must be literal strings",
)

success
Expand All @@ -141,7 +167,7 @@ class RowParserTest extends Specification with Mockito {
"fail to compile with invalid columns" in {
illTyped(
"""generateParser[User](Map("badCol" -> "name"))""",
"badCol is not a member of com.lucidchart.relate.macros.User"
"badCol is not a member of com.lucidchart.relate.macros.User",
)

success
Expand Down Expand Up @@ -223,7 +249,7 @@ class RowParserTest extends Specification with Mockito {
"fail to compile when passing in colMapping and snakeCase" in {
illTyped(
"""@Record("snakeCase" -> true, "colMapping" -> Map[String, String]()) case class Thing(a: String)""",
"Only one of snakeCase or colMapping can be supplied"
"Only one of snakeCase or colMapping can be supplied",
)

success
Expand All @@ -232,17 +258,17 @@ class RowParserTest extends Specification with Mockito {
"fail to compile with non-literals" in {
illTyped(
"""val sc = true; @Record("snakeCase" -> sc) case class Thing(a: String)""",
"snakeCase requires a literal true or false value"
"snakeCase requires a literal true or false value",
)

illTyped(
"""val col = "col"; @Record("colMapping" -> Map(col -> "thing")) case class Thing(col: String)""",
"Column names must be literal strings"
"Column names must be literal strings",
)

illTyped(
"""val name = "name"; @Record("colMapping" -> Map("col" -> name)) case class Thing(col: String)""",
"Remappings must be literal strings"
"Remappings must be literal strings",
)

success
Expand All @@ -251,7 +277,7 @@ class RowParserTest extends Specification with Mockito {
"fail to compile with invalid columns" in {
illTyped(
"""@Record("colMapping" -> Map("badCol" -> "name")) case class Thing(col: String)""",
"badCol is not a member of Thing"
"badCol is not a member of Thing",
)

success
Expand Down
Loading

0 comments on commit 3afeea8

Please sign in to comment.