From 1d4f89033caa59f3a282751ccc0e36fce19b174e Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Tue, 22 Dec 2020 11:57:54 +0100 Subject: [PATCH] Allow updates.limit option to be zero This allows the repo config option `updates.limit` to be zero so that it can now be zero or positive instead of positive only. A zero `updates.limit` means that Scala Steward will not create or update PRs. It can be used to temporarily disable Scala Steward in a repo or if it is used in the default repo config it would be similar to a hypothetical dry-run option. --- .../core/nurture/NurtureAlg.scala | 6 +++--- .../core/repoconfig/UpdatesConfig.scala | 6 +++--- .../org/scalasteward/core/util/package.scala | 20 ++++++++++--------- .../core/nurture/NurtureAlgTest.scala | 4 ++-- .../core/repoconfig/RepoConfigAlgTest.scala | 4 ++-- .../core/repoconfig/UpdatesConfigTest.scala | 10 +++++----- .../org/scalasteward/core/util/utilTest.scala | 16 +++++++++++++-- 7 files changed, 40 insertions(+), 26 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala index 368a1855e8..86d03a2eb6 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala @@ -19,7 +19,7 @@ package org.scalasteward.core.nurture import cats.Applicative import cats.effect.BracketThrow import cats.implicits._ -import eu.timepit.refined.types.numeric.PosInt +import eu.timepit.refined.types.numeric.NonNegInt import fs2.Stream import io.chrisdavenport.log4cats.Logger import org.http4s.Uri @@ -239,7 +239,7 @@ object NurtureAlg { def processUpdates[F[_]]( updates: List[Update], updateF: Update => F[ProcessResult], - updatesLimit: Option[PosInt] + updatesLimit: Option[NonNegInt] )(implicit streamCompiler: Stream.Compiler[F, F], F: Applicative[F]): F[Unit] = updatesLimit match { case None => updates.traverse_(updateF) @@ -247,7 +247,7 @@ object NurtureAlg { Stream .emits(updates) .evalMap(updateF) - .through(util.takeUntil(limit.value) { + .through(util.takeUntil(0, limit.value) { case Ignored => 0 case Updated => 1 }) diff --git a/modules/core/src/main/scala/org/scalasteward/core/repoconfig/UpdatesConfig.scala b/modules/core/src/main/scala/org/scalasteward/core/repoconfig/UpdatesConfig.scala index eb8f66ff45..0ead31c585 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/repoconfig/UpdatesConfig.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/repoconfig/UpdatesConfig.scala @@ -18,7 +18,7 @@ package org.scalasteward.core.repoconfig import cats.implicits._ import cats.kernel.Semigroup -import eu.timepit.refined.types.numeric.PosInt +import eu.timepit.refined.types.numeric.NonNegInt import io.circe.generic.extras.Configuration import io.circe.generic.extras.semiauto._ import io.circe.refined._ @@ -36,7 +36,7 @@ final case class UpdatesConfig( pin: List[UpdatePattern] = List.empty, allow: List[UpdatePattern] = List.empty, ignore: List[UpdatePattern] = List.empty, - limit: Option[PosInt] = None, + limit: Option[NonNegInt] = None, includeScala: Option[Boolean] = None, fileExtensions: Option[List[String]] = None ) { @@ -186,5 +186,5 @@ object UpdatesConfig { combineOptions(x, y)(_.intersect(_)) // prevent IntelliJ from removing the import of io.circe.refined._ - locally(refinedDecoder: Decoder[PosInt]) + locally(refinedDecoder: Decoder[NonNegInt]) } diff --git a/modules/core/src/main/scala/org/scalasteward/core/util/package.scala b/modules/core/src/main/scala/org/scalasteward/core/util/package.scala index 64847df961..c42597c39b 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/util/package.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/util/package.scala @@ -68,21 +68,23 @@ package object util { fa.exists(a => ga.exists(b => a === b)) /** Adds a weight to each element and cuts the stream when the total - * weight is greater or equal to `limit`. + * weight is greater or equal to `limit`. `init` is the initial weight. * * @example {{{ - * scala> fs2.Stream.emits("Hello, world!").through(takeUntil(3) { + * scala> fs2.Stream.emits("Hello, world!").through(takeUntil(0, 3) { * | case 'a' | 'e' | 'i' | 'o' | 'u' => 1 * | case _ => 0 * | }).toList.mkString * res1: String = Hello, wo * }}} */ - def takeUntil[F[_], A, N](limit: N)(weight: A => N)(implicit N: Numeric[N]): Pipe[F, A, A] = { - import N._ - _.map(a => (a, weight(a))) - .scan1[(A, N)] { case ((_, total), (a, i)) => (a, total + i) } - .takeThrough { case (_, total) => total < limit } - .map { case (a, _) => a } - } + def takeUntil[F[_], A, N](init: N, limit: N)(weight: A => N)(implicit + N: Numeric[N] + ): Pipe[F, A, A] = + if (N.gteq(init, limit)) + _ => fs2.Stream.empty + else + _.mapAccumulate(init) { case (i, a) => (N.plus(i, weight(a)), a) } + .takeThrough { case (total, _) => N.lt(total, limit) } + .map { case (_, a) => a } } diff --git a/modules/core/src/test/scala/org/scalasteward/core/nurture/NurtureAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/nurture/NurtureAlgTest.scala index 81bdc2e532..cdc80497d5 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/nurture/NurtureAlgTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/nurture/NurtureAlgTest.scala @@ -2,7 +2,7 @@ package org.scalasteward.core.nurture import cats.data.StateT import cats.effect.IO -import eu.timepit.refined.types.numeric.PosInt +import eu.timepit.refined.types.numeric.NonNegInt import munit.ScalaCheckSuite import org.scalacheck.Prop._ import org.scalasteward.core.TestInstances._ @@ -35,7 +35,7 @@ class NurtureAlgTest extends ScalaCheckSuite { .processUpdates( ignorableUpdates ++ appliableUpdates, f, - PosInt.unapply(appliableUpdates.size) + NonNegInt.unapply(appliableUpdates.size) ) .runS(0) .unsafeRunSync() diff --git a/modules/core/src/test/scala/org/scalasteward/core/repoconfig/RepoConfigAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/repoconfig/RepoConfigAlgTest.scala index 314a19ad7d..b0785197f5 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/repoconfig/RepoConfigAlgTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/repoconfig/RepoConfigAlgTest.scala @@ -1,7 +1,7 @@ package org.scalasteward.core.repoconfig import better.files.File -import eu.timepit.refined.types.numeric.PosInt +import eu.timepit.refined.types.numeric.NonNegInt import munit.FunSuite import org.scalasteward.core.TestSyntax._ import org.scalasteward.core.data.{GroupId, Update} @@ -62,7 +62,7 @@ class RepoConfigAlgTest extends FunSuite { ignore = List( UpdatePattern(GroupId("org.acme"), None, Some(UpdatePattern.Version(Some("1.0"), None))) ), - limit = Some(PosInt.unsafeFrom(4)), + limit = Some(NonNegInt.unsafeFrom(4)), includeScala = Some(true), fileExtensions = Some(List(".txt")) ), diff --git a/modules/core/src/test/scala/org/scalasteward/core/repoconfig/UpdatesConfigTest.scala b/modules/core/src/test/scala/org/scalasteward/core/repoconfig/UpdatesConfigTest.scala index 989fe70218..c542accf63 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/repoconfig/UpdatesConfigTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/repoconfig/UpdatesConfigTest.scala @@ -1,7 +1,7 @@ package org.scalasteward.core.repoconfig import cats.syntax.semigroup._ -import eu.timepit.refined.types.numeric.PosInt +import eu.timepit.refined.types.numeric.NonNegInt import munit.FunSuite import org.scalasteward.core.data.GroupId import org.scalasteward.core.repoconfig.UpdatePattern.Version @@ -30,7 +30,7 @@ class UpdatesConfigTest extends FunSuite { pin = List(aa0), allow = List(aa0), ignore = List(aa0), - limit = Some(PosInt.unsafeFrom(10)), + limit = Some(NonNegInt.unsafeFrom(10)), includeScala = Some(false), fileExtensions = Some(List(".txt", ".scala", ".sbt")) ) @@ -43,7 +43,7 @@ class UpdatesConfigTest extends FunSuite { pin = List(ab0), allow = List(ab0), ignore = List(ab0), - limit = Some(PosInt.unsafeFrom(20)), + limit = Some(NonNegInt.unsafeFrom(20)), includeScala = Some(true), fileExtensions = Some(List(".sbt", ".scala")) ) @@ -54,7 +54,7 @@ class UpdatesConfigTest extends FunSuite { pin = List(aa0, ab0), allow = UpdatesConfig.nonExistingUpdatePattern, ignore = List(aa0, ab0), - limit = Some(PosInt.unsafeFrom(10)), + limit = Some(NonNegInt.unsafeFrom(10)), includeScala = Some(false), fileExtensions = Some(List(".scala", ".sbt")) ) @@ -66,7 +66,7 @@ class UpdatesConfigTest extends FunSuite { pin = List(ab0, aa0), allow = UpdatesConfig.nonExistingUpdatePattern, ignore = List(ab0, aa0), - limit = Some(PosInt.unsafeFrom(20)), + limit = Some(NonNegInt.unsafeFrom(20)), includeScala = Some(true), fileExtensions = Some(List(".sbt", ".scala")) ) diff --git a/modules/core/src/test/scala/org/scalasteward/core/util/utilTest.scala b/modules/core/src/test/scala/org/scalasteward/core/util/utilTest.scala index e1d6b10d25..8ee8d4987f 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/util/utilTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/util/utilTest.scala @@ -1,9 +1,12 @@ package org.scalasteward.core.util -import munit.FunSuite +import cats.effect.IO +import munit.ScalaCheckSuite +import org.scalacheck.Gen +import org.scalacheck.Prop._ import scala.collection.mutable.ListBuffer -class utilTest extends FunSuite { +class utilTest extends ScalaCheckSuite { test("appendBounded") { val lb = new ListBuffer[Int] lb.appendAll(List(1, 2, 3)) @@ -35,4 +38,13 @@ class utilTest extends FunSuite { assert(!intersects(List(1, 3, 5), Vector(2, 4, 6))) assert(intersects(List(1, 3, 5), Vector(2, 3, 6))) } + + test("takeUntil") { + forAll(Gen.choose(0, 16)) { (n: Int) => + var count = 0 + val s = fs2.Stream.eval(IO(count += 1)).repeat.through(takeUntil(0, n)(_ => 1)) + s.compile.drain.unsafeRunSync() + assertEquals(count, n) + } + } }