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 0036182
Show file tree
Hide file tree
Showing 23 changed files with 1,148 additions and 410 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 @@ -42,7 +42,6 @@ case class Big(
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,18 +115,39 @@ 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 {
Expand Down
44 changes: 25 additions & 19 deletions relate/src/main/scala/com/lucidchart/relate/ColReader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ trait ColReader[A] { self =>
def flatMap[B](f: A => ColReader[B]): ColReader[B] = ColReader[B] { (col, rs) =>
self.read(col, rs) match {
case Some(a) => f(a).read(col, rs)
case None => None
case None => None
}
}

Expand All @@ -34,25 +34,31 @@ object ColReader {
def read(col: String, rs: SqlRow): Option[A] = f(col, rs)
}

implicit val jbigDecimalReader: ColReader[java.math.BigDecimal] = ColReader { (col, row) => row.javaBigDecimalOption(col)}
implicit val bigDecimalReader: ColReader[BigDecimal] = ColReader { (col, row) => row.bigDecimalOption(col)}
implicit val jBigIntReader: ColReader[java.math.BigInteger] = ColReader { (col, row) => row.javaBigIntegerOption(col)}
implicit val bigIntReader: ColReader[BigInt] = ColReader { (col, row) => row.bigIntOption(col)}
implicit val boolReader: ColReader[Boolean] = ColReader { (col, row) => row.boolOption(col)}
implicit val byteArrayReader: ColReader[Array[Byte]] = ColReader { (col, row) => row.byteArrayOption(col)}
implicit val byteReader: ColReader[Byte] = ColReader { (col, row) => row.byteOption(col)}
implicit val dateReader: ColReader[Date] = ColReader { (col, row) => row.dateOption(col)}
implicit val instantReader: ColReader[Instant] = ColReader { (col, row) => row.instantOption(col)}
implicit val doubleReader: ColReader[Double] = ColReader { (col, row) => row.doubleOption(col)}
implicit val intReader: ColReader[Int] = ColReader { (col, row) => row.intOption(col)}
implicit val longReader: ColReader[Long] = ColReader { (col, row) => row.longOption(col)}
implicit val shortReader: ColReader[Short] = ColReader { (col, row) => row.shortOption(col)}
implicit val stringReader: ColReader[String] = ColReader { (col, row) => row.stringOption(col)}
implicit val uuidReader: ColReader[UUID] = ColReader[UUID] { (col, row) => row.uuidOption(col)}
implicit val jbigDecimalReader: ColReader[java.math.BigDecimal] = ColReader { (col, row) =>
row.javaBigDecimalOption(col)
}
implicit val bigDecimalReader: ColReader[BigDecimal] = ColReader { (col, row) => row.bigDecimalOption(col) }
implicit val jBigIntReader: ColReader[java.math.BigInteger] = ColReader { (col, row) =>
row.javaBigIntegerOption(col)
}
implicit val bigIntReader: ColReader[BigInt] = ColReader { (col, row) => row.bigIntOption(col) }
implicit val boolReader: ColReader[Boolean] = ColReader { (col, row) => row.boolOption(col) }
implicit val byteArrayReader: ColReader[Array[Byte]] = ColReader { (col, row) => row.byteArrayOption(col) }
implicit val byteReader: ColReader[Byte] = ColReader { (col, row) => row.byteOption(col) }
implicit val dateReader: ColReader[Date] = ColReader { (col, row) => row.dateOption(col) }
implicit val instantReader: ColReader[Instant] = ColReader { (col, row) => row.instantOption(col) }
implicit val doubleReader: ColReader[Double] = ColReader { (col, row) => row.doubleOption(col) }
implicit val intReader: ColReader[Int] = ColReader { (col, row) => row.intOption(col) }
implicit val longReader: ColReader[Long] = ColReader { (col, row) => row.longOption(col) }
implicit val shortReader: ColReader[Short] = ColReader { (col, row) => row.shortOption(col) }
implicit val stringReader: ColReader[String] = ColReader { (col, row) => row.stringOption(col) }
implicit val uuidReader: ColReader[UUID] = ColReader[UUID] { (col, row) => row.uuidOption(col) }

def enumReader[A <: Enumeration](e: A): ColReader[e.Value] = {
intReader.flatMap(id => ColReader[e.Value] { (_, _) =>
e.values.find(_.id == id)
})
intReader.flatMap(id =>
ColReader[e.Value] { (_, _) =>
e.values.find(_.id == id)
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import scala.collection.compat._
import scala.language.higherKinds

trait CollectionsParser {
def limitedCollection[B: RowParser, Col[_]](maxRows: Long)(implicit factory: Factory[B, Col[B]]) =
def limitedCollection[B: RowParser, Col[_]](maxRows: Long)(implicit factory: Factory[B, Col[B]]) =
RowParser { result =>
val builder = factory.newBuilder

Expand All @@ -24,8 +24,9 @@ trait CollectionsParser {
implicit def collection[B: RowParser, Col[_]](implicit factory: Factory[B, Col[B]]) =
limitedCollection[B, Col](Long.MaxValue)

implicit def pairCollection[Key: RowParser, Value: RowParser, PairCol[_, _]]
(implicit factory: Factory[(Key, Value), PairCol[Key, Value]]) =
implicit def pairCollection[Key: RowParser, Value: RowParser, PairCol[_, _]](implicit
factory: Factory[(Key, Value), PairCol[Key, Value]]
) =
RowParser { result =>

val builder = factory.newBuilder
Expand All @@ -38,4 +39,4 @@ trait CollectionsParser {

builder.result
}
}
}
Loading

0 comments on commit 0036182

Please sign in to comment.