Skip to content

Commit

Permalink
Merge pull request #5 from Dwolla/scalafix
Browse files Browse the repository at this point in the history
Scalafix rule for adding necessary implicits to generated code
  • Loading branch information
bpholt authored Oct 5, 2021
2 parents 33c73d6 + dbfa8e7 commit 6084f81
Show file tree
Hide file tree
Showing 21 changed files with 4,852 additions and 15 deletions.
53 changes: 49 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ object FooService {
}
```

These implicit instances can be automatically added to the code generated by Scrooge by running the `AddCatsTaglessInstances` scalafix rule! See below for more detail.

Now you can safely convert your `Future`-based implementation into one in `F[_] : Async`:

```scala
Expand Down Expand Up @@ -64,10 +66,8 @@ trait FooScroogeService[F[_]] {
}

object FooScroogeService {
// these implicits won't be generated by scrooge—but we're working on that!
// for now you'll have to add them yourself, either here or somewhere in the
// implicit scope available to the calls shown below under "Finagle Clients"
// and "Finagle Servers"
// Let Scalafix generate these instances for you!
// Follow the instructions in the `Scalafix Rule` section below.
implicit def FooScroogeServiceReaderT[F[_]]: FooScroogeService[ReaderT[F, FooScroogeService[F], *]] =
Derive.readerT[FooScroogeService, F]
implicit val FooScroogeServiceFunctorK: FunctorK[FooScroogeService] = Derive.functorK[FooScroogeService]
Expand Down Expand Up @@ -100,6 +100,43 @@ val fooImpl: FooScroogeService[IO] = new FooScroogeService[IO] {
val thriftServer: IO[Nothing] = ThriftServer("address", fooImpl)
```

## Scalafix Rule

Add Scalafix to your project's build by [following the instructions](https://scalacenter.github.io/scalafix/docs/users/installation.html#sbt):

1. Add the Scalafix plugin to the project by adding this to `project/plugins.sbt`:
```scala
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.31")
```

2. Enable SemanticDB by adding this to `build.sbt`:
```scala
ThisBuild / semanticdbEnabled := true
ThisBuild / semanticdbVersion := scalafixSemanticdb.revision
ThisBuild / scalafixScalaBinaryVersion := CrossVersion.binaryScalaVersion(scalaVersion.value)
ThisBuild / scalafixDependencies += "com.dwolla" %% "finagle-tagless-scalafix" % "0.0.4"
```

3. Run the Scalafix rule automatically after generating the Thrift sources by adding this to `build.sbt`:
```scala
Compile / scalafix / unmanagedSources := (Compile / sources).value
Compile / compile := Def.taskDyn {
val compileOutput = (Compile / compile).value

Def.task {
(Compile / scalafix).toTask(" AddCatsTaglessInstances").value
compileOutput
}
}.value
libraryDependencies ++= {
val catsTaglessV = "0.14.0"
Seq(
"org.typelevel" %% "cats-tagless-core" % catsTaglessV,
"org.typelevel" %% "cats-tagless-macros" % catsTaglessV,
)
}
```

## Artifacts

The Group ID for each artifact is `"com.dwolla"`. All artifacts are published to Maven Central.
Expand Down Expand Up @@ -153,6 +190,14 @@ The Group ID for each artifact is `"com.dwolla"`. All artifacts are published to
<td align="center">Cats Effect 3</td>
</tr>
<tr>
<td><code>"finagle-tagless-scalafix"</code></td>
<td>Automatically adds implicit instances needed by `asyncMapK` to the companion objects of Finagle services generated by Scrooge</td>
<td align="center">All</td>
<td align="center"><g-emoji class="g-emoji" alias="white_check_mark" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/2705.png">✅</g-emoji></td>
<td align="center"><g-emoji class="g-emoji" alias="white_check_mark" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/2705.png">✅</g-emoji></td>
<td align="center">N/A</td>
</tr>
<tr>
<td><code>"async-utils-twitter-19-4-ce2"</code></td>
<td rowspan="2">Implementation for Twitter <code>Future</code><br>(with <code>"com.twitter" %% "util-core" % "19.4.0"</code>)</td>
<td align="center">Cats Effect 2</td>
Expand Down
74 changes: 66 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,20 @@ inThisBuild(List(
startYear := Option(2021),
))

lazy val CatsEffect2V = "2.5.1"
lazy val CatsEffect3V = "3.1.1"
lazy val TwitterUtilsLatestV = "21.5.0"
lazy val CatsEffect2V = "2.5.4"
lazy val CatsEffect3V = "3.2.9"
lazy val TwitterUtilsLatestV = "21.8.0"
lazy val TwitterUtils19_4V = "19.4.0"
lazy val CatsTaglessV = "0.14.0"
lazy val libthriftV = "0.10.0"

lazy val SCALA_2_13 = "2.13.6"
lazy val SCALA_2_12 = "2.12.14"
lazy val SCALA_3 = "3.0.0"
lazy val SCALA_2_12 = "2.12.15"

lazy val Scala2Versions = Seq(SCALA_2_13, SCALA_2_12)
lazy val Scala2And3 = Seq(SCALA_2_13, SCALA_2_12, SCALA_3)

lazy val scala2CompilerPlugins: Seq[ModuleID] = Seq(
compilerPlugin("org.typelevel" % "kind-projector" % "0.13.0" cross CrossVersion.full)
compilerPlugin("org.typelevel" % "kind-projector" % "0.13.2" cross CrossVersion.full)
)

def dedupKindProjectorOptions(opts: Seq[String]): Seq[String] =
Expand Down Expand Up @@ -246,21 +245,80 @@ lazy val examples = (projectMatrix in file("examples"))
.settings(
libraryDependencies ++= {
Seq(
"org.typelevel" %% "cats-tagless-macros" % "0.14.0",
"org.typelevel" %% "cats-tagless-macros" % CatsTaglessV,
) ++ (if (scalaVersion.value.startsWith("2")) scala2CompilerPlugins else Nil)
},
publish / skip := true,
)
.jvmPlatform(scalaVersions = Scala2Versions)
.dependsOn(`async-utils`)

lazy val `scalafix-rules` = (projectMatrix in file("scalafix/rules"))
.jvmPlatform(scalaVersions = Scala2Versions)
.settings(
moduleName := "finagle-tagless-scalafix",
libraryDependencies ++= Seq(
"ch.epfl.scala" %% "scalafix-core" % _root_.scalafix.sbt.BuildInfo.scalafixVersion,

),
scalacOptions ~= { _.filterNot(_ == "-Xfatal-warnings") },
)

lazy val `scalafix-input` = (project in file("scalafix/input"))
.settings(
publish / skip := true,
scalaVersion := Scala2Versions.head,
libraryDependencies ++= Seq(
"com.twitter" %% "scrooge-core" % TwitterUtilsLatestV,
"com.twitter" %% "finagle-thrift" % TwitterUtilsLatestV,
"org.apache.thrift" % "libthrift" % libthriftV,
),
scalacOptions += "-nowarn",
scalacOptions ~= { _.filterNot(_ == "-Xfatal-warnings") },
semanticdbEnabled := true,
semanticdbVersion := scalafixSemanticdb.revision,
)
.disablePlugins(ScalafixPlugin)

lazy val `scalafix-output` = (project in file("scalafix/output"))
.settings(
publish / skip := true,
scalaVersion := Scala2Versions.head,
libraryDependencies ++= Seq(
"com.twitter" %% "scrooge-core" % TwitterUtilsLatestV,
"com.twitter" %% "finagle-thrift" % TwitterUtilsLatestV,
"org.apache.thrift" % "libthrift" % libthriftV,
"org.typelevel" %% "cats-tagless-core" % CatsTaglessV,
"org.typelevel" %% "cats-tagless-macros" % CatsTaglessV,
),
scalacOptions += "-nowarn",
scalacOptions ~= { _.filterNot(_ == "-Xfatal-warnings") },
)
.disablePlugins(ScalafixPlugin)

lazy val `scalafix-tests` = (projectMatrix in file("scalafix/tests"))
.jvmPlatform(scalaVersions = Scala2Versions)
.settings(
publish / skip := true,
libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % _root_.scalafix.sbt.BuildInfo.scalafixVersion % Test cross CrossVersion.full,
scalafixTestkitOutputSourceDirectories := (`scalafix-output` / Compile / unmanagedSourceDirectories).value,
scalafixTestkitInputSourceDirectories := (`scalafix-input` / Compile / unmanagedSourceDirectories).value,
scalafixTestkitInputClasspath := (`scalafix-input` / Compile / fullClasspath).value,
scalafixTestkitInputScalacOptions := (`scalafix-input` / Compile / scalacOptions).value,
scalafixTestkitInputScalaVersion := (`scalafix-input` / Compile / scalaVersion).value,
)
.dependsOn(`scalafix-rules`)
.enablePlugins(ScalafixTestkitPlugin)

lazy val `async-utils-root` = (project in file("."))
.aggregate(
Seq(
`async-utils-core`,
`async-utils`,
`async-utils-twitter`,
`async-utils-finagle`,
`scalafix-rules`,
`scalafix-tests`,
examples,
).flatMap(_.projectRefs): _*
)
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.5.3
sbt.version=1.5.5
5 changes: 3 additions & 2 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.20")
addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.7")
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.9")
addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.8.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.5.1")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.7.0")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.31")
30 changes: 30 additions & 0 deletions scalafix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Scalafix Notes

## Scrooge Updates

Scrooge updates roughly monthly, and occasionally the updates introduce
breaking changes in the generated code. (For example, between the `21.5.0`
and `21.8.0` versions, [a new abstract method was added to the `ValidatingThriftStructCodec3`](https://github.com/twitter/scrooge/commit/fdb8f8f2d9cc30d6ca06b117fe7a82003f330ba8#diff-1fb7ff7b4c7a64ec907dfa620403b5b190f9829f3d5e6e0d57d07685dd53411f)
trait, which implementations generated by the older version will be missing.)

As a result, the files in `scalafix/input/src/main/scala/example/thrift` and
`scalafix/output/src/main/scala/example/thrift` may need to be regenerated when
the Scrooge version is updated by running the `regenerate-inputs.sh` script with
the new Scrooge version as the first parameter.

```bash
./regenerate-inputs.sh 21.8.0
```

Then edit `scalafix/output/src/main/scala/example/thrift/SimpleService.scala` and replace the
blank line at the end of the `SimpleService` object with these lines:

```scala

implicit def SimpleServiceInReaderT[F[_]]: SimpleService[({type Λ0] = _root_.cats.data.ReaderT[F, SimpleService[F], β0]})#Λ] =
_root_.cats.tagless.Derive.readerT[SimpleService, F]

implicit val SimpleServiceFunctorK: _root_.cats.tagless.FunctorK[SimpleService] = _root_.cats.tagless.Derive.functorK[SimpleService]
```

(Newlines and whitespace matter to the tests!)
Loading

0 comments on commit 6084f81

Please sign in to comment.