diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b916d75c57..9d5ea6fcec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,61 +15,47 @@ on: tags: [v*] env: - PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} - SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }} - SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} - PGP_SECRET: ${{ secrets.PGP_SECRET }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +concurrency: + group: ${{ github.workflow }} @ ${{ github.ref }} + cancel-in-progress: true + jobs: build: name: Build and Test strategy: matrix: os: [ubuntu-latest, macos-latest] - scala: [3.3.0, 2.12.18, 2.13.11] + scala: [2.12, 2.13, 3] java: [temurin@17] project: [rootJS, rootJVM, rootNative] exclude: - - scala: 3.3.0 + - scala: 2.12 os: macos-latest - - scala: 2.12.18 + - scala: 3 os: macos-latest runs-on: ${{ matrix.os }} + timeout-minutes: 60 steps: - name: Checkout current branch (full) uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Download Java (temurin@17) - id: download-java-temurin-17 - if: matrix.java == 'temurin@17' - uses: typelevel/download-java@v2 - with: - distribution: temurin - java-version: 17 - - name: Setup Java (temurin@17) + id: setup-java-temurin-17 if: matrix.java == 'temurin@17' uses: actions/setup-java@v3 with: - distribution: jdkfile + distribution: temurin java-version: 17 - jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} + cache: sbt - - name: Cache sbt - uses: actions/cache@v3 - with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - ~/AppData/Local/Coursier/Cache/v1 - ~/Library/Caches/Coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + - name: sbt update + if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' + run: sbt +update - name: Install brew formulae (ubuntu) if: (matrix.project == 'rootNative') && startsWith(matrix.os, 'ubuntu') @@ -106,21 +92,21 @@ jobs: run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' doc - name: Scalafix tests - if: matrix.scala == '2.13.11' && matrix.project == 'rootJVM' + if: matrix.scala == '2.13' && matrix.project == 'rootJVM' run: | cd scalafix sbt testCI - name: Make target directories - if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/2.5.x') - run: mkdir -p target protocols/js/target io/native/target unidocs/target .js/target core/native/target core/js/target protocols/native/target mdoc/target core/jvm/target .jvm/target .native/target scodec/jvm/target scodec/js/target io/js/target reactive-streams/target io/jvm/target protocols/jvm/target scodec/native/target benchmark/target project/target + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + run: mkdir -p protocols/js/target io/native/target unidocs/target core/native/target core/js/target protocols/native/target core/jvm/target scodec/jvm/target scodec/js/target io/js/target reactive-streams/target io/jvm/target protocols/jvm/target scodec/native/target project/target - name: Compress target directories - if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/2.5.x') - run: tar cf targets.tar target protocols/js/target io/native/target unidocs/target .js/target core/native/target core/js/target protocols/native/target mdoc/target core/jvm/target .jvm/target .native/target scodec/jvm/target scodec/js/target io/js/target reactive-streams/target io/jvm/target protocols/jvm/target scodec/native/target benchmark/target project/target + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + run: tar cf targets.tar protocols/js/target io/native/target unidocs/target core/native/target core/js/target protocols/native/target core/jvm/target scodec/jvm/target scodec/js/target io/js/target reactive-streams/target io/jvm/target protocols/jvm/target scodec/native/target project/target - name: Upload target directories - if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/2.5.x') + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') uses: actions/upload-artifact@v3 with: name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }}-${{ matrix.project }} @@ -129,7 +115,7 @@ jobs: publish: name: Publish Artifacts needs: [build] - if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/2.5.x') + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') strategy: matrix: os: [ubuntu-latest] @@ -141,140 +127,136 @@ jobs: with: fetch-depth: 0 - - name: Download Java (temurin@17) - id: download-java-temurin-17 - if: matrix.java == 'temurin@17' - uses: typelevel/download-java@v2 - with: - distribution: temurin - java-version: 17 - - name: Setup Java (temurin@17) + id: setup-java-temurin-17 if: matrix.java == 'temurin@17' uses: actions/setup-java@v3 with: - distribution: jdkfile + distribution: temurin java-version: 17 - jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} + cache: sbt - - name: Cache sbt - uses: actions/cache@v3 - with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - ~/AppData/Local/Coursier/Cache/v1 - ~/Library/Caches/Coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - - - name: Download target directories (3.3.0, rootJS) + - name: sbt update + if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' + run: sbt +update + + - name: Download target directories (2.12, rootJS) uses: actions/download-artifact@v3 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-3.3.0-rootJS + name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootJS - - name: Inflate target directories (3.3.0, rootJS) + - name: Inflate target directories (2.12, rootJS) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (3.3.0, rootJVM) + - name: Download target directories (2.12, rootJVM) uses: actions/download-artifact@v3 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-3.3.0-rootJVM + name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootJVM - - name: Inflate target directories (3.3.0, rootJVM) + - name: Inflate target directories (2.12, rootJVM) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (3.3.0, rootNative) + - name: Download target directories (2.12, rootNative) uses: actions/download-artifact@v3 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-3.3.0-rootNative + name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootNative - - name: Inflate target directories (3.3.0, rootNative) + - name: Inflate target directories (2.12, rootNative) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.12.18, rootJS) + - name: Download target directories (2.13, rootJS) uses: actions/download-artifact@v3 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.12.18-rootJS + name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootJS - - name: Inflate target directories (2.12.18, rootJS) + - name: Inflate target directories (2.13, rootJS) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.12.18, rootJVM) + - name: Download target directories (2.13, rootJVM) uses: actions/download-artifact@v3 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.12.18-rootJVM + name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootJVM - - name: Inflate target directories (2.12.18, rootJVM) + - name: Inflate target directories (2.13, rootJVM) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.12.18, rootNative) + - name: Download target directories (2.13, rootNative) uses: actions/download-artifact@v3 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.12.18-rootNative + name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootNative - - name: Inflate target directories (2.12.18, rootNative) + - name: Inflate target directories (2.13, rootNative) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.13.11, rootJS) + - name: Download target directories (3, rootJS) uses: actions/download-artifact@v3 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.13.11-rootJS + name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJS - - name: Inflate target directories (2.13.11, rootJS) + - name: Inflate target directories (3, rootJS) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.13.11, rootJVM) + - name: Download target directories (3, rootJVM) uses: actions/download-artifact@v3 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.13.11-rootJVM + name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJVM - - name: Inflate target directories (2.13.11, rootJVM) + - name: Inflate target directories (3, rootJVM) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.13.11, rootNative) + - name: Download target directories (3, rootNative) uses: actions/download-artifact@v3 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.13.11-rootNative + name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootNative - - name: Inflate target directories (2.13.11, rootNative) + - name: Inflate target directories (3, rootNative) run: | tar xf targets.tar rm targets.tar - name: Import signing key if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE == '' + env: + PGP_SECRET: ${{ secrets.PGP_SECRET }} + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} run: echo $PGP_SECRET | base64 -di | gpg --import - name: Import signing key and strip passphrase if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE != '' + env: + PGP_SECRET: ${{ secrets.PGP_SECRET }} + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} run: | echo "$PGP_SECRET" | base64 -di > /tmp/signing-key.gpg echo "$PGP_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --import /tmp/signing-key.gpg (echo "$PGP_PASSPHRASE"; echo; echo) | gpg --command-fd 0 --pinentry-mode loopback --change-passphrase $(gpg --list-secret-keys --with-colons 2> /dev/null | grep '^sec:' | cut --delimiter ':' --fields 5 | tail -n 1) - name: Publish + env: + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }} run: sbt tlCiRelease - site: - name: Generate Site + dependency-submission: + name: Submit Dependencies + if: github.event_name != 'pull_request' strategy: matrix: os: [ubuntu-latest] @@ -286,40 +268,57 @@ jobs: with: fetch-depth: 0 - - name: Download Java (temurin@17) - id: download-java-temurin-17 + - name: Setup Java (temurin@17) + id: setup-java-temurin-17 if: matrix.java == 'temurin@17' - uses: typelevel/download-java@v2 + uses: actions/setup-java@v3 with: distribution: temurin java-version: 17 + cache: sbt + + - name: sbt update + if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' + run: sbt +update + + - name: Submit Dependencies + uses: scalacenter/sbt-dependency-submission@v2 + with: + modules-ignore: integration_2.12 integration_2.13 integration_3 rootjs_2.12 rootjs_2.13 rootjs_3 microsite_2.12 microsite_2.13 microsite_3 rootjvm_2.12 rootjvm_2.13 rootjvm_3 rootnative_2.12 rootnative_2.13 rootnative_3 fs2-benchmark_2.12 fs2-benchmark_2.13 fs2-benchmark_3 + configs-ignore: test scala-tool scala-doc-tool test-internal + + site: + name: Generate Site + strategy: + matrix: + os: [ubuntu-latest] + java: [temurin@17] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout current branch (full) + uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Setup Java (temurin@17) + id: setup-java-temurin-17 if: matrix.java == 'temurin@17' uses: actions/setup-java@v3 with: - distribution: jdkfile + distribution: temurin java-version: 17 - jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} + cache: sbt - - name: Cache sbt - uses: actions/cache@v3 - with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - ~/AppData/Local/Coursier/Cache/v1 - ~/Library/Caches/Coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + - name: sbt update + if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' + run: sbt +update - name: Generate site run: sbt microsite/tlSite - name: Publish site if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' - uses: peaceiris/actions-gh-pages@v3.9.0 + uses: peaceiris/actions-gh-pages@v3.9.3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: mdoc/target/docs/site diff --git a/benchmark/src/main/scala/fs2/benchmark/LinesBenchmark.scala b/benchmark/src/main/scala/fs2/benchmark/LinesBenchmark.scala index 2ab40fd610..8a5f54c0dd 100644 --- a/benchmark/src/main/scala/fs2/benchmark/LinesBenchmark.scala +++ b/benchmark/src/main/scala/fs2/benchmark/LinesBenchmark.scala @@ -43,7 +43,8 @@ class LinesBenchmark { else { val rng = new java.util.Random(7919) val line = Array.fill(asciiLineSize)((rng.nextInt(126 - 32) + 32).toByte) - val List(string) = Stream.emits(line).through(text.utf8.decode).foldMonoid.toList + val List(string) = + Stream.emits(line).through(text.utf8.decode).foldMonoid.toList: @unchecked ((string + "\n") * lines) .grouped(chunkSize) diff --git a/build.sbt b/build.sbt index 3bd6be94e8..b9471ed22c 100644 --- a/build.sbt +++ b/build.sbt @@ -8,9 +8,10 @@ ThisBuild / organization := "co.fs2" ThisBuild / organizationName := "Functional Streams for Scala" ThisBuild / startYear := Some(2013) -val NewScala = "2.13.11" +val Scala213 = "2.13.11" -ThisBuild / crossScalaVersions := Seq("3.3.0", "2.12.18", NewScala) +ThisBuild / scalaVersion := Scala213 +ThisBuild / crossScalaVersions := Seq("2.12.18", Scala213, "3.3.0") ThisBuild / tlVersionIntroduced := Map("3" -> "3.0.3") ThisBuild / githubWorkflowOSes := Seq("ubuntu-latest", "macos-latest") @@ -18,18 +19,16 @@ ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("17")) ThisBuild / githubWorkflowBuildPreamble ++= nativeBrewInstallWorkflowSteps.value ThisBuild / nativeBrewInstallCond := Some("matrix.project == 'rootNative'") -ThisBuild / tlCiReleaseBranches := List("main", "series/2.5.x") - ThisBuild / githubWorkflowBuild ++= Seq( WorkflowStep.Run( List("cd scalafix", "sbt testCI"), name = Some("Scalafix tests"), - cond = Some(s"matrix.scala == '$NewScala' && matrix.project == 'rootJVM'") + cond = Some(s"matrix.scala == '2.13' && matrix.project == 'rootJVM'") ) ) ThisBuild / githubWorkflowBuildMatrixExclusions ++= - crossScalaVersions.value.filterNot(Set(scalaVersion.value)).map { scala => + List("2.12", "3").map { scala => MatrixExclude(Map("scala" -> scala, "os" -> "macos-latest")) } @@ -229,12 +228,11 @@ lazy val root = tlCrossRootProject scodec, protocols, reactiveStreams, + integration, unidocs, benchmark ) -lazy val IntegrationTest = config("it").extend(Test) - lazy val commonNativeSettings = Seq[Setting[_]]( tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "3.2.15").toMap, Test / nativeBrewFormulas += "openssl" @@ -242,13 +240,6 @@ lazy val commonNativeSettings = Seq[Setting[_]]( lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform) .in(file("core")) - .configs(IntegrationTest) - .settings(Defaults.itSettings: _*) - .settings( - inConfig(IntegrationTest)(org.scalafmt.sbt.ScalafmtPlugin.scalafmtConfigSettings), - IntegrationTest / fork := true, - IntegrationTest / javaOptions += "-Dcats.effect.tracing.mode=none" - ) .settings( name := "fs2-core", libraryDependencies ++= Seq( @@ -290,12 +281,26 @@ lazy val coreNative = core.native .disablePlugins(DoctestPlugin) .settings(commonNativeSettings) +lazy val integration = project + .in(file("integration")) + .settings( + fork := true, + javaOptions += "-Dcats.effect.tracing.mode=none", + libraryDependencies ++= Seq( + "org.typelevel" %%% "munit-cats-effect" % "2.0.0-M3" % Test + ) + ) + .enablePlugins(NoPublishPlugin) + .disablePlugins(DoctestPlugin) + .dependsOn(coreJVM) + lazy val io = crossProject(JVMPlatform, JSPlatform, NativePlatform) .in(file("io")) .settings( name := "fs2-io", tlVersionIntroduced ~= { _.updated("3", "3.1.0") }, - libraryDependencies += "com.comcast" %%% "ip4s-core" % "3.3.0" + libraryDependencies += "com.comcast" %%% "ip4s-core" % "3.3.0", + tlJdkRelease := None ) .jvmSettings( Test / fork := true, @@ -362,7 +367,10 @@ lazy val io = crossProject(JVMPlatform, JSPlatform, NativePlatform) ProblemFilters.exclude[IncompatibleResultTypeProblem]( "fs2.io.net.tls.SecureContext#SecureVersion#TLSv1.3.toJS" ), - ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.io.net.tls.TLSSocket.forAsync") + ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.io.net.tls.TLSSocket.forAsync"), + ProblemFilters.exclude[IncompatibleResultTypeProblem]( + "fs2.io.net.tls.TLSParameters#DefaultTLSParameters.toTLSConnectOptions" + ) ) ) @@ -418,6 +426,7 @@ lazy val unidocs = project .enablePlugins(TypelevelUnidocPlugin) .settings( name := "fs2-docs", + tlJdkRelease := None, tlFatalWarnings := false, ScalaUnidoc / unidoc / unidocProjectFilter := inProjects( core.jvm, @@ -446,7 +455,8 @@ lazy val microsite = project sbt.IO.copyDirectory(mdocOut.value, (laikaSite / target).value) Set.empty }, - tlFatalWarningsInCi := false, + tlJdkRelease := None, + tlFatalWarnings := false, tlSiteApiPackage := Some("fs2") ) .dependsOn(coreJVM, io.jvm, reactiveStreams, scodec.jvm) diff --git a/core/js/src/main/scala/fs2/hash.scala b/core/js/src/main/scala/fs2/hash.scala index e7b3f8d072..8e466a7ed7 100644 --- a/core/js/src/main/scala/fs2/hash.scala +++ b/core/js/src/main/scala/fs2/hash.scala @@ -21,6 +21,8 @@ package fs2 +import org.typelevel.scalaccompat.annotation._ + import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.typedarray.Uint8Array @@ -67,9 +69,11 @@ object hash { @js.native @JSImport("crypto", "createHash") + @nowarn212("cat=unused") private[fs2] def createHash(algorithm: String): Hash = js.native @js.native + @nowarn212("cat=unused") private[fs2] trait Hash extends js.Object { def update(data: Uint8Array): Unit = js.native def digest(): Uint8Array = js.native diff --git a/core/native/src/main/scala/fs2/hash.scala b/core/native/src/main/scala/fs2/hash.scala index d5872e7e2a..76be35a6da 100644 --- a/core/native/src/main/scala/fs2/hash.scala +++ b/core/native/src/main/scala/fs2/hash.scala @@ -22,6 +22,7 @@ package fs2 import cats.effect.kernel.Sync +import org.typelevel.scalaccompat.annotation._ import scala.scalanative.unsafe._ import scala.scalanative.unsigned._ @@ -94,6 +95,7 @@ object hash { @link("crypto") @extern + @nowarn212("cat=unused") private[fs2] object openssl { final val EVP_MAX_MD_SIZE = 64 diff --git a/core/shared/src/main/scala/fs2/Chunk.scala b/core/shared/src/main/scala/fs2/Chunk.scala index 469d707b01..9ba1a26b9b 100644 --- a/core/shared/src/main/scala/fs2/Chunk.scala +++ b/core/shared/src/main/scala/fs2/Chunk.scala @@ -33,6 +33,7 @@ import java.{util => ju} import cats._ import cats.data.{Chain, NonEmptyList} import cats.syntax.all._ +import org.typelevel.scalaccompat.annotation._ import fs2.internal._ @@ -323,6 +324,7 @@ abstract class Chunk[+O] extends Serializable with ChunkPlatform[O] with ChunkRu * @note that even "read-only" interaction with a `ByteBuffer` may increment its `position`, * so this method should be considered as unsafely allocating mutable state. */ + @nowarn212("cat=unused") def toByteBuffer[B >: O](implicit ev: B =:= Byte): JByteBuffer = { val slice = this.asInstanceOf[Chunk[Byte]].toArraySlice JByteBuffer.wrap(slice.values, slice.offset, slice.length) @@ -332,6 +334,7 @@ abstract class Chunk[+O] extends Serializable with ChunkPlatform[O] with ChunkRu * @note that even "read-only" interaction with a `CharBuffer` may increment its position, * so this method should be considered as unsafely allocating mutable state. */ + @nowarn212("cat=unused") def toCharBuffer[C >: O](implicit ev: C =:= Char): JCharBuffer = { val slice = this.asInstanceOf[Chunk[Char]].toArraySlice JCharBuffer.wrap(slice.values, slice.offset, slice.length) @@ -521,7 +524,7 @@ abstract class Chunk[+O] extends Serializable with ChunkPlatform[O] with ChunkRu override def equals(a: Any): Boolean = a match { case c: Chunk[_] => - size == c.size && iterator.sameElements(c.iterator) + size == c.size && iterator.sameElements(c.iterator: Iterator[Any]) case _ => false } @@ -997,7 +1000,7 @@ object Chunk new ByteBuffer(buf, buf.position, buf.remaining) } - case class ByteBuffer private ( + case class ByteBuffer private[Chunk] ( buf: JByteBuffer, override val offset: Int, override val size: Int diff --git a/core/shared/src/main/scala/fs2/Pull.scala b/core/shared/src/main/scala/fs2/Pull.scala index ffdcbed7f6..6067c5fa0f 100644 --- a/core/shared/src/main/scala/fs2/Pull.scala +++ b/core/shared/src/main/scala/fs2/Pull.scala @@ -742,7 +742,7 @@ object Pull extends PullLowPriority { del: Bind[F, O, X, Unit] ): Pull[F, O, Unit] = py match { - case ty: Terminal[_] => + case ty: Terminal[X] => del match { case cici: BindBind[F, O, _, X] => bindBindAux(cici.bb.cont(ty), cici.del) @@ -887,14 +887,15 @@ object Pull extends PullLowPriority { case e: Action[G, X, Unit] => contP = IdContP e - case b: Bind[G, X, _, Unit] => + case b: Bind[G, X, y, Unit] => + type Y = y b.step match { - case c: Bind[G, X, _, _] => - viewL(new BindBind(c.step, c.delegate, b.delegate)) + case c: Bind[G, X, z, Y] => + viewL(new BindBind[G, X, z, Y](c.step, c.delegate, b.delegate)) case e: Action[G, X, _] => contP = b.delegate e - case r: Terminal[_] => viewL(b.cont(r)) + case r: Terminal[Y] => viewL(b.cont(r)) } case r: Terminal[Unit] => r } diff --git a/core/shared/src/main/scala/fs2/Stream.scala b/core/shared/src/main/scala/fs2/Stream.scala index c3a48dc780..21f2b843f4 100644 --- a/core/shared/src/main/scala/fs2/Stream.scala +++ b/core/shared/src/main/scala/fs2/Stream.scala @@ -35,6 +35,7 @@ import cats.syntax.all._ import fs2.compat._ import fs2.concurrent._ import fs2.internal._ +import org.typelevel.scalaccompat.annotation._ import Pull.StreamPullOps /** A stream producing output of type `O` and which may evaluate `F` effects. @@ -2508,6 +2509,7 @@ final class Stream[+F[_], +O] private[fs2] (private[fs2] val underlying: Pull[F, * res0: List[Int] = List(1, 2, -1) * }}} */ + @nowarn212("cat=unused") def rethrow[F2[x] >: F[x], O2](implicit ev: O <:< Either[Throwable, O2], rt: RaiseThrowable[F2] @@ -4120,6 +4122,7 @@ object Stream extends StreamLowPriority { * If either of `left` or `right` fails, then resulting stream will fail. * If either `halts` the evaluation will halt too. */ + @nowarn212("cat=unused") def observeEither[L, R]( left: Pipe[F, L, Nothing], right: Pipe[F, R, Nothing] @@ -5042,6 +5045,7 @@ object Stream extends StreamLowPriority { * res0: String = Hello world! * }}} */ + @nowarn212("cat=unused") def string(implicit ev: O <:< String): G[String] = new Stream(underlying).asInstanceOf[Stream[F, String]].compile.to(Collector.string) diff --git a/core/shared/src/main/scala/fs2/compression/DeflateParams.scala b/core/shared/src/main/scala/fs2/compression/DeflateParams.scala index 32c966bc29..12787e9cde 100644 --- a/core/shared/src/main/scala/fs2/compression/DeflateParams.scala +++ b/core/shared/src/main/scala/fs2/compression/DeflateParams.scala @@ -88,7 +88,7 @@ object DeflateParams { sealed abstract class Level(private[fs2] val juzDeflaterLevel: Int) case object Level { private[fs2] def apply(level: Int): Level = - level match { + (level: @unchecked) match { case DEFAULT.juzDeflaterLevel => Level.DEFAULT case ZERO.juzDeflaterLevel => Level.ZERO case ONE.juzDeflaterLevel => Level.ONE @@ -121,7 +121,7 @@ object DeflateParams { sealed abstract class Strategy(private[fs2] val juzDeflaterStrategy: Int) case object Strategy { private[fs2] def apply(strategy: Int): Strategy = - strategy match { + (strategy: @unchecked) match { case DEFAULT.juzDeflaterStrategy => Strategy.DEFAULT case FILTERED.juzDeflaterStrategy => Strategy.FILTERED case HUFFMAN_ONLY.juzDeflaterStrategy => Strategy.HUFFMAN_ONLY @@ -137,7 +137,7 @@ object DeflateParams { sealed abstract class FlushMode(private[fs2] val juzDeflaterFlushMode: Int) case object FlushMode { private[fs2] def apply(flushMode: Int): FlushMode = - flushMode match { + (flushMode: @unchecked) match { case DEFAULT.juzDeflaterFlushMode => FlushMode.NO_FLUSH case SYNC_FLUSH.juzDeflaterFlushMode => FlushMode.SYNC_FLUSH case FULL_FLUSH.juzDeflaterFlushMode => FlushMode.FULL_FLUSH diff --git a/core/shared/src/test/scala/fs2/BracketSuite.scala b/core/shared/src/test/scala/fs2/BracketSuite.scala index 724af3266e..8cce63297b 100644 --- a/core/shared/src/test/scala/fs2/BracketSuite.scala +++ b/core/shared/src/test/scala/fs2/BracketSuite.scala @@ -53,7 +53,7 @@ class BracketSuite extends Fs2Suite { withBracketEventRecorder { recorder => (recorder.recordBracketEvents.evalMap(_ => recorder.assertHistoryIs(Acquired)) >> testCase).compile.drain - .handleError { case _: Err => () } >> + .recover { case _: Err => () } >> recorder.assertHistoryIs(Acquired, Released) } @@ -74,7 +74,7 @@ class BracketSuite extends Fs2Suite { .append(recorder.recordBracketEvents >> use2) .compile .drain - .handleError { case _: Err => () } >> + .recover { case _: Err => () } >> recorder.assertHistoryIs(Acquired, Released, Acquired, Released) } diff --git a/core/shared/src/test/scala/fs2/StreamSwitchMapSuite.scala b/core/shared/src/test/scala/fs2/StreamSwitchMapSuite.scala index b974943f3a..9b83c4c0ab 100644 --- a/core/shared/src/test/scala/fs2/StreamSwitchMapSuite.scala +++ b/core/shared/src/test/scala/fs2/StreamSwitchMapSuite.scala @@ -26,7 +26,6 @@ import scala.concurrent.duration._ import cats.effect.IO import cats.effect.kernel.{Deferred, Ref} import cats.effect.std.Semaphore -import cats.syntax.all._ import org.scalacheck.effect.PropF.forAllF class StreamSwitchMapSuite extends Fs2Suite { diff --git a/core/shared/src/test/scala/fs2/TimedPullsSuite.scala b/core/shared/src/test/scala/fs2/TimedPullsSuite.scala index f703365d7f..41e4709ddc 100644 --- a/core/shared/src/test/scala/fs2/TimedPullsSuite.scala +++ b/core/shared/src/test/scala/fs2/TimedPullsSuite.scala @@ -23,7 +23,6 @@ package fs2 import cats.effect.IO import cats.effect.testkit.TestControl -import cats.syntax.all._ import org.scalacheck.effect.PropF.forAllF import scala.concurrent.duration._ diff --git a/core/shared/src/test/scala/fs2/timeseries/TimeStampedSuite.scala b/core/shared/src/test/scala/fs2/timeseries/TimeStampedSuite.scala index a6b3928fc2..c52d6e96fa 100644 --- a/core/shared/src/test/scala/fs2/timeseries/TimeStampedSuite.scala +++ b/core/shared/src/test/scala/fs2/timeseries/TimeStampedSuite.scala @@ -28,7 +28,6 @@ import scala.concurrent.duration._ import cats.effect._ import cats.effect.testkit.TestControl -import cats.syntax.all._ import scodec.bits._ import munit.Location diff --git a/core/jvm/src/it/scala/fs2/MemoryLeakSpec.scala b/integration/src/test/scala/fs2/MemoryLeakSpec.scala similarity index 86% rename from core/jvm/src/it/scala/fs2/MemoryLeakSpec.scala rename to integration/src/test/scala/fs2/MemoryLeakSpec.scala index f44c86d812..7bb6e97ad2 100644 --- a/core/jvm/src/it/scala/fs2/MemoryLeakSpec.scala +++ b/integration/src/test/scala/fs2/MemoryLeakSpec.scala @@ -1,6 +1,26 @@ +/* + * Copyright (c) 2013 Functional Streams for Scala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + package fs2 -import scala.annotation.nowarn import scala.concurrent.duration._ import java.lang.management.ManagementFactory @@ -10,14 +30,17 @@ import cats.~> import cats.effect.IO import cats.effect.unsafe.implicits.global import cats.syntax.all._ +import org.typelevel.scalaccompat.annotation._ import munit.{FunSuite, TestOptions} import fs2.concurrent._ -@nowarn("cat=w-flag-dead-code") +@nowarn2("cat=w-flag-dead-code") class MemoryLeakSpec extends FunSuite { + override def munitIgnore = Option(System.getenv("CI")).contains("true") + override def munitFlakyOK = true case class LeakTestParams( @@ -126,7 +149,7 @@ class MemoryLeakSpec extends FunSuite { leakTest("groupWithin --- Issue 2328") { Stream - .range(0, 1_000_000) + .range(0, 1000000) .covary[IO] .groupWithin(256, 1.second) } diff --git a/io/js/src/main/scala/fs2/io/NodeStream.scala b/io/js/src/main/scala/fs2/io/NodeStream.scala index ae5fef52a5..99213a666f 100644 --- a/io/js/src/main/scala/fs2/io/NodeStream.scala +++ b/io/js/src/main/scala/fs2/io/NodeStream.scala @@ -22,6 +22,7 @@ package fs2.io import fs2.io.internal.facade.events.EventEmitter +import org.typelevel.scalaccompat.annotation._ import scala.scalajs.js @@ -29,6 +30,7 @@ import scala.scalajs.js * @see [[https://nodejs.org/api/stream.html]] */ @js.native +@nowarn212("cat=unused") trait Readable extends EventEmitter { protected[io] def read(): js.typedarray.Uint8Array = js.native @@ -45,6 +47,7 @@ trait Readable extends EventEmitter { * @see [[https://nodejs.org/api/stream.html]] */ @js.native +@nowarn212("cat=unused") trait Writable extends EventEmitter { protected[io] def destroy(): this.type = js.native @@ -64,6 +67,7 @@ trait Writable extends EventEmitter { * @see [[https://nodejs.org/api/stream.html]] */ @js.native +@nowarn212("cat=unused") trait Duplex extends Readable with Writable { protected[io] override def destroy(): this.type = js.native } diff --git a/io/js/src/main/scala/fs2/io/file/Path.scala b/io/js/src/main/scala/fs2/io/file/Path.scala index 8cc2d3eadc..a47f9ebf67 100644 --- a/io/js/src/main/scala/fs2/io/file/Path.scala +++ b/io/js/src/main/scala/fs2/io/file/Path.scala @@ -27,7 +27,7 @@ import fs2.io.internal.facade import scala.annotation.tailrec -final case class Path private (override val toString: String) extends PathApi { +final case class Path private[file] (override val toString: String) extends PathApi { def /(name: String): Path = if (toString.isEmpty & name.isEmpty) diff --git a/io/js/src/main/scala/fs2/io/internal/MicrotaskExecutor.scala b/io/js/src/main/scala/fs2/io/internal/MicrotaskExecutor.scala index a16253964e..ded5c883c4 100644 --- a/io/js/src/main/scala/fs2/io/internal/MicrotaskExecutor.scala +++ b/io/js/src/main/scala/fs2/io/internal/MicrotaskExecutor.scala @@ -21,6 +21,8 @@ package fs2.io.internal +import org.typelevel.scalaccompat.annotation._ + import scala.scalajs.js import scala.scalajs.js.annotation.JSGlobal import scala.concurrent.ExecutionContext @@ -33,6 +35,7 @@ private[io] object MicrotaskExecutor extends ExecutionContext { @JSGlobal("queueMicrotask") @js.native + @nowarn212("cat=unused") private def queueMicrotask(function: js.Function0[Any]): Unit = js.native } diff --git a/io/js/src/main/scala/fs2/io/internal/facade/child_process.scala b/io/js/src/main/scala/fs2/io/internal/facade/child_process.scala index 468b258b63..a719b0345f 100644 --- a/io/js/src/main/scala/fs2/io/internal/facade/child_process.scala +++ b/io/js/src/main/scala/fs2/io/internal/facade/child_process.scala @@ -21,27 +21,26 @@ package fs2.io.internal.facade +import org.typelevel.scalaccompat.annotation._ + import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import events.EventEmitter -package object child_process { +@nowarn212("cat=unused") +private[io] object child_process { @js.native @JSImport("child_process", "spawn") - private[io] def spawn( + def spawn( command: String, args: js.Array[String], options: SpawnOptions ): ChildProcess = js.native -} - -package child_process { - - private[io] trait SpawnOptions extends js.Object { + trait SpawnOptions extends js.Object { var cwd: js.UndefOr[String] = js.undefined @@ -50,7 +49,7 @@ package child_process { } @js.native - private[io] trait ChildProcess extends EventEmitter { + trait ChildProcess extends EventEmitter { def stdin: fs2.io.Writable = js.native diff --git a/io/js/src/main/scala/fs2/io/internal/facade/dgram.scala b/io/js/src/main/scala/fs2/io/internal/facade/dgram.scala index df434cb277..8b5875b123 100644 --- a/io/js/src/main/scala/fs2/io/internal/facade/dgram.scala +++ b/io/js/src/main/scala/fs2/io/internal/facade/dgram.scala @@ -21,25 +21,25 @@ package fs2.io.internal.facade +import org.typelevel.scalaccompat.annotation._ + import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.typedarray.Uint8Array import events.EventEmitter -package object dgram { +@nowarn212("cat=unused") +private[io] object dgram { @js.native @JSImport("dgram", "createSocket") - private[io] def createSocket(`type`: String): Socket = + @nowarn212("cat=unused") + def createSocket(`type`: String): Socket = js.native -} - -package dgram { - @js.native - private[io] trait Socket extends EventEmitter { + trait Socket extends EventEmitter { def address(): AddressInfo = js.native @@ -83,19 +83,19 @@ package dgram { } @js.native - private[io] trait AddressInfo extends js.Object { + trait AddressInfo extends js.Object { def address: String = js.native def family: Int = js.native def port: Int = js.native } - private[io] trait BindOptions extends js.Object { + trait BindOptions extends js.Object { var port: js.UndefOr[Int] = js.undefined var address: js.UndefOr[String] = js.undefined } @js.native - private[io] trait RemoteInfo extends js.Object { + trait RemoteInfo extends js.Object { def address: String = js.native def family: Int = js.native def port: Int = js.native diff --git a/io/js/src/main/scala/fs2/io/internal/facade/events.scala b/io/js/src/main/scala/fs2/io/internal/facade/events.scala index 5aee246317..8cdb1041db 100644 --- a/io/js/src/main/scala/fs2/io/internal/facade/events.scala +++ b/io/js/src/main/scala/fs2/io/internal/facade/events.scala @@ -21,6 +21,8 @@ package fs2.io.internal.facade.events +import org.typelevel.scalaccompat.annotation._ + import cats.effect.kernel.Resource import cats.effect.kernel.Sync import cats.effect.std.Dispatcher @@ -29,6 +31,7 @@ import cats.syntax.all._ import scala.scalajs.js @js.native +@nowarn212("cat=unused") private[io] trait EventEmitter extends js.Object { protected[io] def on[E](eventName: String, listener: js.Function1[E, Unit]): this.type = js.native diff --git a/io/js/src/main/scala/fs2/io/internal/facade/fs.scala b/io/js/src/main/scala/fs2/io/internal/facade/fs.scala index 3cc25c34a4..73e9b0e1d2 100644 --- a/io/js/src/main/scala/fs2/io/internal/facade/fs.scala +++ b/io/js/src/main/scala/fs2/io/internal/facade/fs.scala @@ -21,34 +21,37 @@ package fs2.io.internal.facade +import org.typelevel.scalaccompat.annotation._ + import scala.annotation.nowarn import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.typedarray.Uint8Array -package object fs { +@nowarn212("cat=unused") +private[io] object fs { @js.native @JSImport("fs", "constants") - private[io] def constants: FsConstants = js.native + def constants: FsConstants = js.native @js.native @JSImport("fs", "promises") - private[io] def promises: FsPromises = js.native + def promises: FsPromises = js.native @js.native @JSImport("fs", "createReadStream") - private[io] def createReadStream(path: String, options: ReadStreamOptions): fs2.io.Readable = + def createReadStream(path: String, options: ReadStreamOptions): fs2.io.Readable = js.native @js.native @JSImport("fs", "createWriteStream") - private[io] def createWriteStream(path: String, options: WriteStreamOptions): fs2.io.Writable = + def createWriteStream(path: String, options: WriteStreamOptions): fs2.io.Writable = js.native @js.native @JSImport("fs", "read") - private[io] def read( + def read( fd: Int, buffer: Uint8Array, offset: Int, @@ -59,7 +62,7 @@ package object fs { @js.native @JSImport("fs", "write") - private[io] def write( + def write( fd: Int, buffer: Uint8Array, offset: Int, @@ -68,11 +71,7 @@ package object fs { cb: js.Function3[js.Error, Int, Uint8Array, Unit] ): Unit = js.native -} - -package fs { - - private[io] trait ReadStreamOptions extends js.Object { + trait ReadStreamOptions extends js.Object { var flags: js.UndefOr[Double] = js.undefined @@ -84,7 +83,7 @@ package fs { } - private[io] trait WriteStreamOptions extends js.Object { + trait WriteStreamOptions extends js.Object { var flags: js.UndefOr[Double] = js.undefined @@ -92,7 +91,7 @@ package fs { @js.native @nowarn - private[io] trait FsConstants extends js.Object { + trait FsConstants extends js.Object { val COPYFILE_EXCL: Double = js.native @@ -128,7 +127,7 @@ package fs { @js.native @nowarn - private[io] trait FsPromises extends js.Object { + trait FsPromises extends js.Object { def access(path: String, mode: Double = js.native): js.Promise[Unit] = js.native @@ -167,7 +166,7 @@ package fs { } - private[io] trait MkdirOptions extends js.Object { + trait MkdirOptions extends js.Object { var recursive: js.UndefOr[Boolean] = js.undefined @@ -175,7 +174,7 @@ package fs { } - private[io] trait RmOptions extends js.Object { + trait RmOptions extends js.Object { var force: js.UndefOr[Boolean] = js.undefined @@ -183,14 +182,14 @@ package fs { } - private[io] trait StatOptions extends js.Object { + trait StatOptions extends js.Object { var bigint: js.UndefOr[Boolean] = js.undefined } @js.native - private[io] trait Dir extends js.Object { + trait Dir extends js.Object { def close(): js.Promise[Unit] = js.native @@ -199,14 +198,14 @@ package fs { } @js.native - private[io] trait DirEnt extends js.Object { + trait DirEnt extends js.Object { def name: String = js.native } @js.native - private[io] trait BigIntStats extends js.Object { + trait BigIntStats extends js.Object { def dev: js.BigInt = js.native @@ -237,7 +236,7 @@ package fs { } @js.native - private[io] trait FileHandle extends js.Object { + trait FileHandle extends js.Object { def fd: Int = js.native @@ -252,7 +251,7 @@ package fs { } @js.native - private[io] trait FileHandleReadResult extends js.Object { + trait FileHandleReadResult extends js.Object { def bytesRead: Int = js.native @@ -261,7 +260,7 @@ package fs { } @js.native - private[io] trait FileHandleWriteResult extends js.Object { + trait FileHandleWriteResult extends js.Object { def bytesWritten: Int = js.native diff --git a/io/js/src/main/scala/fs2/io/internal/facade/net.scala b/io/js/src/main/scala/fs2/io/internal/facade/net.scala index 1c498edb34..3df32775b7 100644 --- a/io/js/src/main/scala/fs2/io/internal/facade/net.scala +++ b/io/js/src/main/scala/fs2/io/internal/facade/net.scala @@ -21,25 +21,25 @@ package fs2.io.internal.facade +import org.typelevel.scalaccompat.annotation._ + import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import events.EventEmitter -package object net { +@nowarn212("cat=unused") +private[io] object net { @js.native @JSImport("net", "createServer") - private[io] def createServer( + def createServer( options: ServerOptions, connectionListener: js.Function1[Socket, Unit] ): Server = js.native -} - -package net { @js.native - private[io] trait Server extends EventEmitter { + trait Server extends EventEmitter { def address(): ServerAddress = js.native @@ -56,23 +56,23 @@ package net { } @js.native - private[io] trait ServerAddress extends js.Object { + trait ServerAddress extends js.Object { def address: String = js.native def port: Int = js.native } - private[io] trait ServerOptions extends js.Object { + trait ServerOptions extends js.Object { var allowHalfOpen: js.UndefOr[Boolean] = js.undefined var pauseOnConnect: js.UndefOr[Boolean] = js.undefined } - private[io] trait ListenOptions extends js.Object { + trait ListenOptions extends js.Object { var path: js.UndefOr[String] = js.undefined } - private[io] trait SocketOptions extends js.Object { + trait SocketOptions extends js.Object { var allowHalfOpen: js.UndefOr[Boolean] = js.undefined @@ -80,7 +80,7 @@ package net { @JSImport("net", "Socket") @js.native - private[io] class Socket extends fs2.io.Duplex { + class Socket extends fs2.io.Duplex { def this(options: SocketOptions) = this() diff --git a/io/js/src/main/scala/fs2/io/internal/facade/os.scala b/io/js/src/main/scala/fs2/io/internal/facade/os.scala index a3da76d69e..63c5bd0176 100644 --- a/io/js/src/main/scala/fs2/io/internal/facade/os.scala +++ b/io/js/src/main/scala/fs2/io/internal/facade/os.scala @@ -24,34 +24,30 @@ package fs2.io.internal.facade import scala.scalajs.js import scala.scalajs.js.annotation.JSImport -package object os { +private[io] object os { @js.native @JSImport("os", "tmpdir") - private[io] def tmpdir(): String = js.native + def tmpdir(): String = js.native @js.native @JSImport("os", "homedir") - private[io] def homedir(): String = js.native + def homedir(): String = js.native @js.native @JSImport("os", "type") - private[io] def `type`(): String = js.native + def `type`(): String = js.native @js.native @JSImport("os", "networkInterfaces") - private[io] def networkInterfaces(): js.Dictionary[js.Array[NetworkInterfaceInfo]] = js.native + def networkInterfaces(): js.Dictionary[js.Array[NetworkInterfaceInfo]] = js.native @js.native @JSImport("os", "EOL") - private[io] def EOL: String = js.native - -} - -package os { + def EOL: String = js.native @js.native - private[io] trait NetworkInterfaceInfo extends js.Object { + trait NetworkInterfaceInfo extends js.Object { def family: String = js.native } diff --git a/io/js/src/main/scala/fs2/io/internal/facade/path.scala b/io/js/src/main/scala/fs2/io/internal/facade/path.scala index f04386a2a5..c657165c2e 100644 --- a/io/js/src/main/scala/fs2/io/internal/facade/path.scala +++ b/io/js/src/main/scala/fs2/io/internal/facade/path.scala @@ -21,52 +21,52 @@ package fs2.io.internal.facade +import org.typelevel.scalaccompat.annotation._ + import scala.scalajs.js import scala.scalajs.js.annotation.JSImport -package object path { +@nowarn212("cat=unused") +private[io] object path { @js.native @JSImport("path", "sep") - private[io] def sep: String = js.native + def sep: String = js.native @js.native @JSImport("path", "join") - private[io] def join(paths: String*): String = js.native + def join(paths: String*): String = js.native @js.native @JSImport("path", "resolve") - private[io] def resolve(paths: String*): String = js.native + def resolve(paths: String*): String = js.native @js.native @JSImport("path", "relative") - private[io] def relative(from: String, to: String): String = js.native + def relative(from: String, to: String): String = js.native @js.native @JSImport("path", "normalize") - private[io] def normalize(path: String): String = js.native + def normalize(path: String): String = js.native @js.native @JSImport("path", "isAbsolute") - private[io] def isAbsolute(path: String): Boolean = js.native + def isAbsolute(path: String): Boolean = js.native @js.native @JSImport("path", "basename") - private[io] def basename(path: String): String = js.native + def basename(path: String): String = js.native @js.native @JSImport("path", "extname") - private[io] def extname(path: String): String = js.native + def extname(path: String): String = js.native @js.native @JSImport("path", "parse") - private[io] def parse(path: String): ParsedPath = js.native - -} + def parse(path: String): ParsedPath = js.native -package path { @js.native - private[io] trait ParsedPath extends js.Object { + trait ParsedPath extends js.Object { def dir: String = js.native def root: String = js.native def base: String = js.native diff --git a/io/js/src/main/scala/fs2/io/internal/facade/tls.scala b/io/js/src/main/scala/fs2/io/internal/facade/tls.scala index 07238036c5..7cc28eeb1b 100644 --- a/io/js/src/main/scala/fs2/io/internal/facade/tls.scala +++ b/io/js/src/main/scala/fs2/io/internal/facade/tls.scala @@ -22,32 +22,33 @@ package fs2.io.internal.facade import fs2.io.net.tls.SecureContext +import org.typelevel.scalaccompat.annotation._ import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.typedarray.Uint8Array -import scala.scalajs.js.| import net.Socket -package object tls { +@nowarn212("cat=unused") +@nowarn3("msg=unused import") +private[io] object tls { + + import scala.scalajs.js.| @js.native @JSImport("tls", "connect") - private[io] def connect(options: TLSConnectOptions): TLSSocket = + def connect(options: TLSConnectOptions): TLSSocket = js.native @js.native @JSImport("tls", "createSecureContext") - private[io] def createSecureContext( + def createSecureContext( options: js.UndefOr[SecureContextOptions] = js.undefined ): SecureContext = js.native -} - -package tls { - private[io] trait SecureContextOptions extends js.Object { + trait SecureContextOptions extends js.Object { var ca: js.UndefOr[js.Array[String | Uint8Array]] = js.undefined @@ -91,7 +92,7 @@ package tls { } - private[io] trait Key extends js.Object { + trait Key extends js.Object { val pem: String | Uint8Array @@ -99,7 +100,7 @@ package tls { } - private[io] trait Pfx extends js.Object { + trait Pfx extends js.Object { val buf: String | Uint8Array @@ -107,7 +108,7 @@ package tls { } - private[io] trait TLSConnectOptions extends js.Object { + trait TLSConnectOptions extends js.Object { var secureContext: js.UndefOr[SecureContext] = js.undefined @@ -139,7 +140,7 @@ package tls { } - private[io] trait PSKCallbackNegotation extends js.Object { + trait PSKCallbackNegotation extends js.Object { var psk: js.UndefOr[Uint8Array] = js.undefined @@ -148,13 +149,13 @@ package tls { } @js.native - private[io] trait PeerCertificate extends js.Object { + trait PeerCertificate extends js.Object { def raw: Uint8Array = js.native } - private[io] trait TLSSocketOptions extends js.Object { + trait TLSSocketOptions extends js.Object { var secureContext: js.UndefOr[SecureContext] = js.undefined @@ -180,7 +181,7 @@ package tls { @JSImport("tls", "TLSSocket") @js.native - private[io] class TLSSocket extends Socket { + class TLSSocket extends Socket { def this(socket: fs2.io.Duplex, options: TLSSocketOptions) = this() @@ -193,7 +194,7 @@ package tls { } @js.native - private[io] trait SSL extends js.Object { + trait SSL extends js.Object { def verifyError(): js.Error = js.native } diff --git a/io/js/src/main/scala/fs2/io/internal/facade/zlib.scala b/io/js/src/main/scala/fs2/io/internal/facade/zlib.scala index ce56660dee..6d53d976f0 100644 --- a/io/js/src/main/scala/fs2/io/internal/facade/zlib.scala +++ b/io/js/src/main/scala/fs2/io/internal/facade/zlib.scala @@ -21,66 +21,65 @@ package fs2.io.internal.facade +import org.typelevel.scalaccompat.annotation._ + import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.typedarray.Uint8Array -package object zlib { +@nowarn212("cat=unused") +private[io] object zlib { @js.native @JSImport("zlib", "createDeflate") - private[io] def createDeflate(options: Options): Zlib = js.native + def createDeflate(options: Options): Zlib = js.native @js.native @JSImport("zlib", "createDeflateRaw") - private[io] def createDeflateRaw(options: Options): Zlib = js.native + def createDeflateRaw(options: Options): Zlib = js.native @js.native @JSImport("zlib", "deflateSync") - private[io] def deflateSync(buffer: Uint8Array, options: Options): Uint8Array = js.native + def deflateSync(buffer: Uint8Array, options: Options): Uint8Array = js.native @js.native @JSImport("zlib", "createGzip") - private[io] def createGzip(options: Options): Zlib = js.native + def createGzip(options: Options): Zlib = js.native @js.native @JSImport("zlib", "deflateRawSync") - private[io] def deflateRawSync(buffer: Uint8Array, options: Options): Uint8Array = js.native + def deflateRawSync(buffer: Uint8Array, options: Options): Uint8Array = js.native @js.native @JSImport("zlib", "createGunzip") - private[io] def createGunzip(options: Options): Zlib = js.native + def createGunzip(options: Options): Zlib = js.native @js.native @JSImport("zlib", "inflateRawSync") - private[io] def inflateRawSync(buffer: Uint8Array, options: Options): Uint8Array = js.native + def inflateRawSync(buffer: Uint8Array, options: Options): Uint8Array = js.native @js.native @JSImport("zlib", "createInflate") - private[io] def createInflate(options: Options): Zlib = js.native + def createInflate(options: Options): Zlib = js.native @js.native @JSImport("zlib", "createInflateRaw") - private[io] def createInflateRaw(options: Options): Zlib = js.native + def createInflateRaw(options: Options): Zlib = js.native @js.native @JSImport("zlib", "inflateSync") - private[io] def inflateSync(buffer: Uint8Array, options: Options): Uint8Array = js.native + def inflateSync(buffer: Uint8Array, options: Options): Uint8Array = js.native @js.native @JSImport("zlib", "gunzipSync") - private[io] def gunzipSync(buffer: Uint8Array): Uint8Array = js.native - -} - -package zlib { + def gunzipSync(buffer: Uint8Array): Uint8Array = js.native @js.native - private[io] trait Zlib extends fs2.io.Duplex { + trait Zlib extends fs2.io.Duplex { def close(cb: js.Function0[Unit]): Unit = js.native } - private[io] trait Options extends js.Object { + trait Options extends js.Object { var chunkSize: js.UndefOr[Int] = js.undefined diff --git a/io/js/src/main/scala/fs2/io/net/tls/SecureContext.scala b/io/js/src/main/scala/fs2/io/net/tls/SecureContext.scala index 2463914270..6b193d7f6c 100644 --- a/io/js/src/main/scala/fs2/io/net/tls/SecureContext.scala +++ b/io/js/src/main/scala/fs2/io/net/tls/SecureContext.scala @@ -24,10 +24,10 @@ package io.net.tls import cats.syntax.all._ import fs2.io.internal.facade +import org.typelevel.scalaccompat.annotation._ import scala.concurrent.duration.FiniteDuration import scala.scalajs.js -import scala.scalajs.js.| import scala.scalajs.js.JSConverters._ import scala.scalajs.js.typedarray.Uint8Array @@ -35,7 +35,9 @@ import scala.scalajs.js.typedarray.Uint8Array @js.native sealed trait SecureContext extends js.Object +@nowarn3("msg=unused import") object SecureContext { + import scala.scalajs.js.| def default: SecureContext = fromJS(facade.tls.createSecureContext()) diff --git a/io/js/src/test/scala/fs2/io/IoPlatformSuite.scala b/io/js/src/test/scala/fs2/io/IoPlatformSuite.scala index 026cd663c7..bf0826a7b9 100644 --- a/io/js/src/test/scala/fs2/io/IoPlatformSuite.scala +++ b/io/js/src/test/scala/fs2/io/IoPlatformSuite.scala @@ -23,7 +23,6 @@ package fs2 package io import cats.effect.IO -import fs2.Fs2Suite import fs2.io.internal.facade import org.scalacheck.effect.PropF.forAllF diff --git a/io/js/src/test/scala/fs2/io/NodeJSCompressionSuite.scala b/io/js/src/test/scala/fs2/io/NodeJSCompressionSuite.scala index bd697abc37..dd067cce78 100644 --- a/io/js/src/test/scala/fs2/io/NodeJSCompressionSuite.scala +++ b/io/js/src/test/scala/fs2/io/NodeJSCompressionSuite.scala @@ -23,7 +23,6 @@ package fs2 package io import cats.effect._ -import fs2.CompressionSuite import fs2.compression._ import fs2.io.compression._ import fs2.io.internal.facade diff --git a/io/jvm-native/src/main/scala/fs2/io/file/DeprecatedFilesApi.scala b/io/jvm-native/src/main/scala/fs2/io/file/DeprecatedFilesApi.scala index 3c447607a8..7935a487ad 100644 --- a/io/jvm-native/src/main/scala/fs2/io/file/DeprecatedFilesApi.scala +++ b/io/jvm-native/src/main/scala/fs2/io/file/DeprecatedFilesApi.scala @@ -28,7 +28,7 @@ import scala.concurrent.duration._ import cats.effect.kernel.{Async, Resource, Sync} import cats.syntax.all._ -import java.nio.file.{Files => JFiles, Path => JPath, _} +import java.nio.file.{Files => JFiles, Path => JPath, NoSuchFileException => _, _} import java.nio.file.attribute.{FileAttribute, PosixFilePermission} import java.util.stream.{Stream => JStream} diff --git a/io/jvm-native/src/main/scala/fs2/io/file/PermissionsPlatform.scala b/io/jvm-native/src/main/scala/fs2/io/file/PermissionsPlatform.scala index 149b925e8e..8c319d6fbd 100644 --- a/io/jvm-native/src/main/scala/fs2/io/file/PermissionsPlatform.scala +++ b/io/jvm-native/src/main/scala/fs2/io/file/PermissionsPlatform.scala @@ -26,7 +26,7 @@ package file import java.nio.file.attribute.{FileAttribute, PosixFilePermissions} private[file] trait PermissionsPlatform { - def toNioFileAttribute: FileAttribute[_] = this match { + def toNioFileAttribute: FileAttribute[_] = (this: @unchecked) match { case p: PosixPermissions => PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString(p.toString)) } diff --git a/io/jvm-native/src/main/scala/fs2/io/file/file.scala b/io/jvm-native/src/main/scala/fs2/io/file/file.scala index e196b79934..ea1c4b9163 100644 --- a/io/jvm-native/src/main/scala/fs2/io/file/file.scala +++ b/io/jvm-native/src/main/scala/fs2/io/file/file.scala @@ -26,7 +26,6 @@ import java.nio.file.{Files => _, Path => JPath, _} import java.nio.file.attribute.{FileAttribute, PosixFilePermission} import cats.effect.kernel.{Async, Resource} -import cats.syntax.all._ import scala.concurrent.duration._ diff --git a/io/jvm-native/src/main/scala/fs2/io/iojvmnative.scala b/io/jvm-native/src/main/scala/fs2/io/iojvmnative.scala index 900361e568..f9d4c43b3e 100644 --- a/io/jvm-native/src/main/scala/fs2/io/iojvmnative.scala +++ b/io/jvm-native/src/main/scala/fs2/io/iojvmnative.scala @@ -24,7 +24,6 @@ package io import cats._ import cats.effect.kernel.Sync -import cats.effect.kernel.implicits._ import cats.syntax.all._ import java.nio.charset.Charset diff --git a/io/jvm/src/main/scala/fs2/io/net/AsynchronousDatagramSocketGroup.scala b/io/jvm/src/main/scala/fs2/io/net/AsynchronousDatagramSocketGroup.scala index 9057cc09a1..8ccfa506d1 100644 --- a/io/jvm/src/main/scala/fs2/io/net/AsynchronousDatagramSocketGroup.scala +++ b/io/jvm/src/main/scala/fs2/io/net/AsynchronousDatagramSocketGroup.scala @@ -321,7 +321,10 @@ private[net] object AsynchronousDatagramSocketGroup { while (success && attachment.hasReaders) { val reader = attachment.peekReader.get success = read1(channel, reader) - if (success) attachment.dequeueReader + if (success) { + attachment.dequeueReader + () + } } } if (key.isWritable) { @@ -329,13 +332,17 @@ private[net] object AsynchronousDatagramSocketGroup { while (success && attachment.hasWriters) { val (p, writer) = attachment.peekWriter.get success = write1(channel, p, writer) - if (success) attachment.dequeueWriter + if (success) { + attachment.dequeueWriter + () + } } } key.interestOps( (if (attachment.hasReaders) SelectionKey.OP_READ else 0) | (if (attachment.hasWriters) SelectionKey.OP_WRITE else 0) ) + () } catch { case _: CancelledKeyException => // Ignore; key was closed diff --git a/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala b/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala index d541f927c4..66ecb83612 100644 --- a/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala +++ b/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala @@ -31,8 +31,6 @@ import cats.syntax.all._ import com.comcast.ip4s.{IpAddress, SocketAddress} -import fs2.io.net.Socket - private[tls] trait TLSSocketPlatform[F[_]] { /** Initiates handshaking -- either the initial or a renegotiation. */ diff --git a/io/jvm/src/test/scala/fs2/io/IoPlatformSuite.scala b/io/jvm/src/test/scala/fs2/io/IoPlatformSuite.scala index 84165127bb..38fb5cafee 100644 --- a/io/jvm/src/test/scala/fs2/io/IoPlatformSuite.scala +++ b/io/jvm/src/test/scala/fs2/io/IoPlatformSuite.scala @@ -25,7 +25,6 @@ package io import cats.data.EitherT import cats.effect.{IO, Resource} import cats.effect.unsafe.{IORuntime, IORuntimeConfig} -import fs2.{Err, Fs2Suite} import org.scalacheck.{Arbitrary, Gen, Shrink} import org.scalacheck.effect.PropF.forAllF diff --git a/io/native/src/main/scala/fs2/io/net/tls/s2n.scala b/io/native/src/main/scala/fs2/io/net/tls/s2n.scala index 45e9100ddd..daf733c0f3 100644 --- a/io/native/src/main/scala/fs2/io/net/tls/s2n.scala +++ b/io/native/src/main/scala/fs2/io/net/tls/s2n.scala @@ -21,10 +21,13 @@ package fs2.io.net.tls +import org.typelevel.scalaccompat.annotation._ + import scala.scalanative.unsafe._ @extern @link("s2n") +@nowarn212("cat=unused") private[tls] object s2n { final val S2N_SUCCESS = 0 diff --git a/io/shared/src/main/scala/fs2/io/file/Files.scala b/io/shared/src/main/scala/fs2/io/file/Files.scala index 728ff97530..09f25dd536 100644 --- a/io/shared/src/main/scala/fs2/io/file/Files.scala +++ b/io/shared/src/main/scala/fs2/io/file/Files.scala @@ -32,7 +32,6 @@ import cats.syntax.all._ import scala.concurrent.duration._ import cats.Traverse -import fs2.text /** Provides operations related to working with files in the effect `F`. * diff --git a/io/shared/src/main/scala/fs2/io/io.scala b/io/shared/src/main/scala/fs2/io/io.scala index 600f4f28cd..079c4f8043 100644 --- a/io/shared/src/main/scala/fs2/io/io.scala +++ b/io/shared/src/main/scala/fs2/io/io.scala @@ -20,17 +20,20 @@ */ package fs2 +package io import cats.effect.kernel.Async import cats.effect.kernel.Sync import cats.effect.syntax.all._ import cats.syntax.all._ +import org.typelevel.scalaccompat.annotation._ import java.io.{InputStream, OutputStream} /** Provides various ways to work with streams that perform IO. */ -package object io extends ioplatform { +@nowarn213("msg=package object inheritance is deprecated") +object `package` extends ioplatform { type IOException = java.io.IOException /** Reads all bytes from the specified `InputStream` with a buffer size of `chunkSize`. diff --git a/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala b/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala index 22aaf888a8..3665195fbd 100644 --- a/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala +++ b/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala @@ -24,8 +24,6 @@ package io package net package tls -import fs2.io.net.Socket - /** TCP socket that supports encryption via TLS. * * To construct a `TLSSocket`, use the `client` and `server` methods on `TLSContext`. diff --git a/io/shared/src/test/scala/fs2/io/file/PosixPermissionsSuite.scala b/io/shared/src/test/scala/fs2/io/file/PosixPermissionsSuite.scala index 17df211606..95692655a5 100644 --- a/io/shared/src/test/scala/fs2/io/file/PosixPermissionsSuite.scala +++ b/io/shared/src/test/scala/fs2/io/file/PosixPermissionsSuite.scala @@ -34,7 +34,7 @@ class PosixPermissionsSuite extends Fs2IoSuite { ) cases.foreach { case (octal, str) => assertEquals(PosixPermissions.fromOctal(octal), PosixPermissions.fromString(str)) - assertEquals(PosixPermissions.fromOctal(octal).getOrElse("").toString, str) + assertEquals(PosixPermissions.fromOctal(octal).fold("")(_.toString), str) assertEquals( PosixPermissions.fromOctal(octal).map(_.value), PosixPermissions.fromString(str).map(_.value) diff --git a/project/plugins.sbt b/project/plugins.sbt index 4faf5bf121..9093250b49 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,8 +3,7 @@ addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtTypelevelVersion) addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % sbtTypelevelVersion) addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.2") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.14") -addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") -addSbtPlugin("com.armanbilge" % "sbt-scala-native-config-brew-github-actions" % "0.1.3") +addSbtPlugin("com.armanbilge" % "sbt-scala-native-config-brew-github-actions" % "0.2.0-RC1") addSbtPlugin("com.github.tkawachi" % "sbt-doctest" % "0.10.0") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.5") addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.7") diff --git a/protocols/shared/src/main/scala/fs2/protocols/mpeg/transport/Demultiplexer.scala b/protocols/shared/src/main/scala/fs2/protocols/mpeg/transport/Demultiplexer.scala index 4d120517ba..7791330e93 100644 --- a/protocols/shared/src/main/scala/fs2/protocols/mpeg/transport/Demultiplexer.scala +++ b/protocols/shared/src/main/scala/fs2/protocols/mpeg/transport/Demultiplexer.scala @@ -54,7 +54,7 @@ object Demultiplexer { */ case class ResetDecodeState(context: List[String]) extends Err { def message = "reset decode state" - def pushContext(ctx: String) = ResetDecodeState(ctx :: context) + def pushContext(ctx: String): ResetDecodeState = ResetDecodeState(ctx :: context) } final case class State(byPid: Map[Pid, DecodeState]) diff --git a/protocols/shared/src/main/scala/fs2/protocols/mpeg/transport/DemultiplexerError.scala b/protocols/shared/src/main/scala/fs2/protocols/mpeg/transport/DemultiplexerError.scala index 562fb0ee4c..631eb9cc30 100644 --- a/protocols/shared/src/main/scala/fs2/protocols/mpeg/transport/DemultiplexerError.scala +++ b/protocols/shared/src/main/scala/fs2/protocols/mpeg/transport/DemultiplexerError.scala @@ -41,11 +41,11 @@ object DemultiplexerError { with MpegError { def message = s"pid discontinuity: $last to $current with adaptation field control $adaptationFieldControl" - def toMpegError = this + def toMpegError: Discontinuity = this } case class Decoding(data: BitVector, decodingError: Err) extends DemultiplexerError { def message = s"decoding error ($decodingError) while decoding ${data.toHex}" - def toMpegError = MpegError.Decoding(data, decodingError) + def toMpegError: MpegError.Decoding = MpegError.Decoding(data, decodingError) } } diff --git a/protocols/shared/src/main/scala/fs2/protocols/pcapng/CaptureFile.scala b/protocols/shared/src/main/scala/fs2/protocols/pcapng/CaptureFile.scala index 3e708bd148..90e8824ea4 100644 --- a/protocols/shared/src/main/scala/fs2/protocols/pcapng/CaptureFile.scala +++ b/protocols/shared/src/main/scala/fs2/protocols/pcapng/CaptureFile.scala @@ -48,7 +48,7 @@ object CaptureFile { go(idbs :+ idb, tail) case Some((epb: EnhancedPacketBlock, tail)) => val idb = idbs(epb.interfaceId.toInt) - val ts = ((epb.timestampHigh << 32) | epb.timestampLow) * idb.if_tsresol + val ts = idb.if_tsresol * ((epb.timestampHigh << 32) | epb.timestampLow) val timeStamped = f(idb.linkType, epb.packetData).map(TimeStamped(ts, _)) Pull.outputOption1(timeStamped) >> go(idbs, tail) case Some((_, tail)) => go(idbs, tail) diff --git a/protocols/shared/src/test/scala/fs2/protocols/mpeg/DescriptorTest.scala b/protocols/shared/src/test/scala/fs2/protocols/mpeg/DescriptorTest.scala index 4481f4d6eb..7c6737540a 100644 --- a/protocols/shared/src/test/scala/fs2/protocols/mpeg/DescriptorTest.scala +++ b/protocols/shared/src/test/scala/fs2/protocols/mpeg/DescriptorTest.scala @@ -47,7 +47,8 @@ class DescriptorTest extends Fs2Suite { private def roundtrip[A](codec: Codec[A], value: A) = { val encoded = codec.encode(value) - val Attempt.Successful(DecodeResult(decoded, remainder)) = codec.decode(encoded.require) + val Attempt.Successful(DecodeResult(decoded, remainder)) = + codec.decode(encoded.require): @unchecked assertEquals(remainder, BitVector.empty) assertEquals(decoded, value) } @@ -300,10 +301,7 @@ object DescriptorTestData { } yield UnknownDescriptor(tag, length, ByteVector(data: _*)) lazy val genDescriptor: Gen[Descriptor] = - Gen.oneOf(genKnownDescriptor, genUnknownDescriptor).map { - case known: KnownDescriptor => Right(known) - case unknown: UnknownDescriptor => Left(unknown) - } + Gen.oneOf(genKnownDescriptor.map(Right(_)), genUnknownDescriptor.map(Left(_))) implicit lazy val arbitraryDescriptor: Arbitrary[Descriptor] = Arbitrary(genDescriptor) }