From 4aef0fdca45fdc12cd699e8f3beceec7f0859a5b Mon Sep 17 00:00:00 2001 From: i10416 Date: Tue, 27 Jun 2023 02:09:03 +0900 Subject: [PATCH 01/16] draft: cross compile scala 3 --- build.sbt | 98 +++++----- .../cron4s/datetime/PredicateReducer.scala | 0 .../cron4s/datetime/Stepper.scala | 0 .../cron4s/datetime/package.scala | 0 .../cron4s/expr/CronExpr.scala | 17 +- .../cron4s/expr/FieldSelector.scala | 0 .../cron4s/expr/NodeConversions.scala | 0 .../cron4s/expr/WrapperInstances.scala} | 39 +--- .../{scala => scala-2}/cron4s/expr/ops.scala | 0 .../cron4s/expr/package.scala | 0 .../cron4s/expr/parts.scala | 22 +-- .../main/scala-2/cron4s/expr/wrappers.scala | 55 ++++++ .../cron4s/validation/NodeValidator.scala | 0 .../cron4s/validation/ops.scala | 0 .../scala-3/cron4s/datetime/package.scala | 37 ++++ .../main/scala-3/cron4s/expr/CronExpr.scala | 53 +++++ .../scala-3/cron4s/expr/FieldSelector.scala_ | 154 +++++++++++++++ .../scala-3/cron4s/expr/NodeConversions.scala | 83 ++++++++ .../cron4s/expr/WrapperInstances.scala | 138 +++++++++++++ .../src/main/scala-3/cron4s/expr/ops.scala | 60 ++++++ .../main/scala-3/cron4s/expr/package.scala | 51 +++++ .../src/main/scala-3/cron4s/expr/parts.scala | 46 +++++ .../main/scala-3/cron4s/expr/wrappers.scala | 50 +++++ .../cron4s/validation/NodeValidators.scala | 183 ++++++++++++++++++ .../main/scala-3/cron4s/validation/ops.scala | 42 ++++ .../src/main/scala-3/shapelessCompat.scala | 19 ++ .../scala/cron4s/expr/CronExprInstances.scala | 35 ++++ .../cron4s/expr/DateCronExprInstances.scala | 30 +++ .../cron4s/expr/TimeCronExprInstances.scala | 30 +++ project/Dependencies.scala | 9 +- project/build.properties | 2 +- 31 files changed, 1133 insertions(+), 120 deletions(-) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/datetime/PredicateReducer.scala (100%) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/datetime/Stepper.scala (100%) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/datetime/package.scala (100%) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/expr/CronExpr.scala (75%) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/expr/FieldSelector.scala (100%) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/expr/NodeConversions.scala (100%) rename modules/core/shared/src/main/{scala/cron4s/expr/wrappers.scala => scala-2/cron4s/expr/WrapperInstances.scala} (85%) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/expr/ops.scala (100%) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/expr/package.scala (100%) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/expr/parts.scala (68%) create mode 100644 modules/core/shared/src/main/scala-2/cron4s/expr/wrappers.scala rename modules/core/shared/src/main/{scala => scala-2}/cron4s/validation/NodeValidator.scala (100%) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/validation/ops.scala (100%) create mode 100644 modules/core/shared/src/main/scala-3/cron4s/datetime/package.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala_ create mode 100644 modules/core/shared/src/main/scala-3/cron4s/expr/NodeConversions.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/expr/WrapperInstances.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/expr/package.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/expr/parts.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/validation/NodeValidators.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/validation/ops.scala create mode 100644 modules/core/shared/src/main/scala-3/shapelessCompat.scala create mode 100644 modules/core/shared/src/main/scala/cron4s/expr/CronExprInstances.scala create mode 100644 modules/core/shared/src/main/scala/cron4s/expr/DateCronExprInstances.scala create mode 100644 modules/core/shared/src/main/scala/cron4s/expr/TimeCronExprInstances.scala diff --git a/build.sbt b/build.sbt index af348a7a..9a02f7b7 100644 --- a/build.sbt +++ b/build.sbt @@ -16,11 +16,11 @@ Global / onChangedBuildSource := ReloadOnSourceChanges inThisBuild( Seq( libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always, - organization := "com.github.alonsodomin.cron4s", - organizationName := "Antonio Alonso Dominguez", - description := "CRON expression parser for Scala", - startYear := Some(2017), - crossScalaVersions := Seq("2.13.10", "2.12.17"), + organization := "com.github.alonsodomin.cron4s", + organizationName := "Antonio Alonso Dominguez", + description := "CRON expression parser for Scala", + startYear := Some(2017), + crossScalaVersions := Seq("2.13.10", "2.12.17"), homepage := Some(url("https://github.com/alonsodomin/cron4s")), licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.txt")), scmInfo := Some( @@ -46,13 +46,17 @@ val commonSettings = Def.settings( "-unchecked", "-deprecation", "-explaintypes", - "-Xlint:-unused,_", "-Xfatal-warnings", "-language:postfixOps", "-language:implicitConversions", "-language:higherKinds", "-language:existentials" ), + scalacOptions ++= (if (scalaVersion.value.startsWith("2.")) { + Seq( + "-Xlint:-unused,_" + ), + } else { Seq.empty }), scalacOptions ++= { CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, n)) if n == 12 => Seq("-Ypartial-unification") @@ -70,10 +74,10 @@ val commonSettings = Def.settings( Set("-Xlint:-unused,_", "-Xfatal-warnings") ), Test / console / scalacOptions := (Compile / console / scalacOptions).value, - apiURL := Some(url("https://alonsodomin.github.io/cron4s/api/")), - autoAPIMappings := true, - Test / parallelExecution := false, - consoleImports := Seq("cron4s._"), + apiURL := Some(url("https://alonsodomin.github.io/cron4s/api/")), + autoAPIMappings := true, + Test / parallelExecution := false, + consoleImports := Seq("cron4s._"), console / initialCommands := consoleImports.value .map(s => s"import $s") .mkString("\n") @@ -96,7 +100,7 @@ lazy val commonJsSettings = Seq( s"-P:scalajs:mapSourceURI:$a->$g/" }, scalaJSLinkerConfig := scalaJSLinkerConfig.value.withModuleKind(ModuleKind.CommonJSModule), - jsEnv := new org.scalajs.jsenv.nodejs.NodeJSEnv() + jsEnv := new org.scalajs.jsenv.nodejs.NodeJSEnv() ) lazy val consoleSettings = Seq( @@ -104,8 +108,8 @@ lazy val consoleSettings = Seq( ) lazy val publishSettings = Seq( - sonatypeProfileName := "com.github.alonsodomin", - publishMavenStyle := true, + sonatypeProfileName := "com.github.alonsodomin", + publishMavenStyle := true, Test / publishArtifact := false, // don't include scoverage as a dependency in the pom // see issue #980 @@ -125,17 +129,17 @@ lazy val publishSettings = Seq( ) lazy val noPublishSettings = publishSettings ++ Seq( - publish / skip := true, - publishArtifact := false, + publish / skip := true, + publishArtifact := false, mimaFailOnNoPrevious := false ) lazy val coverageSettings = Seq( - coverageMinimumStmtTotal := 90, + coverageMinimumStmtTotal := 90, coverageMinimumBranchTotal := 80, - coverageFailOnMinimum := true, - coverageHighlighting := true, - coverageExcludedPackages := "cron4s\\.bench\\..*" + coverageFailOnMinimum := true, + coverageHighlighting := true, + coverageExcludedPackages := "cron4s\\.bench\\..*" ) def mimaSettings(module: String): Seq[Setting[_]] = @@ -178,19 +182,19 @@ lazy val docsMappingsAPIDir = settingKey[String]("Name of subdirectory in site target directory for api docs") lazy val docSettings = Seq( - micrositeName := "Cron4s", - micrositeDescription := "Scala CRON Parser", - micrositeHighlightTheme := "atom-one-light", - micrositeAuthor := "Antonio Alonso Dominguez", - micrositeGithubOwner := "alonsodomin", - micrositeGithubRepo := "cron4s", - micrositeGitterChannel := true, - micrositeUrl := "https://www.alonsodomin.me", - micrositeBaseUrl := "/cron4s", - micrositeHomepage := "https://www.alonsodomin.me/cron4s/", - micrositeDocumentationUrl := "/cron4s/api/cron4s/index.html", + micrositeName := "Cron4s", + micrositeDescription := "Scala CRON Parser", + micrositeHighlightTheme := "atom-one-light", + micrositeAuthor := "Antonio Alonso Dominguez", + micrositeGithubOwner := "alonsodomin", + micrositeGithubRepo := "cron4s", + micrositeGitterChannel := true, + micrositeUrl := "https://www.alonsodomin.me", + micrositeBaseUrl := "/cron4s", + micrositeHomepage := "https://www.alonsodomin.me/cron4s/", + micrositeDocumentationUrl := "/cron4s/api/cron4s/index.html", micrositeDocumentationLabelDescription := "API Documentation", - micrositeTwitterCreator := "@_alonsodomin_", + micrositeTwitterCreator := "@_alonsodomin_", micrositeExtraMdFiles := Map( file("CHANGELOG.md") -> ExtraMdFileConfig( "changelog.md", @@ -218,15 +222,15 @@ lazy val docSettings = Seq( "momentjsVersion" -> Dependencies.version.momentjs ) ), - mdocIn := sourceDirectory.value / "main" / "mdoc", - Test / fork := true, + mdocIn := sourceDirectory.value / "main" / "mdoc", + Test / fork := true, docsMappingsAPIDir := "api", addMappingsToSiteDir( ScalaUnidoc / packageDoc / mappings, docsMappingsAPIDir ), ghpagesNoJekyll := false, - git.remoteRepo := "https://github.com/alonsodomin/cron4s.git", + git.remoteRepo := "https://github.com/alonsodomin/cron4s.git", ScalaUnidoc / unidoc / unidocProjectFilter := inProjects( core.jvm, circe.jvm, @@ -257,7 +261,7 @@ lazy val cron4s = (project in file(".")) lazy val cron4sJS = (project in file(".js")) .settings( - name := "js", + name := "js", moduleName := "cron4s-js" ) .settings(commonSettings: _*) @@ -269,7 +273,7 @@ lazy val cron4sJS = (project in file(".js")) lazy val cron4sJVM = (project in file(".jvm")) .settings( - name := "jvm", + name := "jvm", moduleName := "cron4s-jvm" ) .settings(commonSettings) @@ -296,8 +300,10 @@ lazy val docs = project lazy val core = (crossProject(JSPlatform, JVMPlatform) in file("modules/core")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin, MimaPlugin) .settings( - name := "core", - moduleName := "cron4s-core" + scalaVersion := "3.3.0", + crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0"), + name := "core", + moduleName := "cron4s-core" ) .settings(commonSettings) .settings(publishSettings) @@ -313,7 +319,7 @@ lazy val testkit = (crossProject(JSPlatform, JVMPlatform) in file("modules/testkit")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin, MimaPlugin) .settings( - name := "testkit", + name := "testkit", moduleName := "cron4s-testkit" ) .settings(commonSettings) @@ -328,7 +334,7 @@ lazy val testkit = lazy val tests = (crossProject(JSPlatform, JVMPlatform) in file("tests")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin) .settings( - name := "tests", + name := "tests", moduleName := "cron4s-tests" ) .settings(commonSettings) @@ -343,7 +349,7 @@ lazy val tests = (crossProject(JSPlatform, JVMPlatform) in file("tests")) lazy val bench = (project in file("bench")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin) .settings( - name := "bench", + name := "bench", moduleName := "cron4s-bench" ) .settings(commonSettings) @@ -360,7 +366,7 @@ lazy val bench = (project in file("bench")) lazy val joda = (project in file("modules/joda")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin, MimaPlugin) .settings( - name := "joda", + name := "joda", moduleName := "cron4s-joda", consoleImports ++= Seq("org.joda.time._", "cron4s.lib.joda._") ) @@ -377,7 +383,7 @@ lazy val momentjs = (project in file("modules/momentjs")) .settings(commonJsSettings) .settings(publishSettings) .settings( - name := "momentjs", + name := "momentjs", moduleName := "cron4s-momentjs" ) .settings(Dependencies.momentjs) @@ -391,7 +397,7 @@ lazy val circe = (crossProject(JSPlatform, JVMPlatform).crossType(CrossType.Pure) in file("modules/circe")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin, MimaPlugin) .settings( - name := "circe", + name := "circe", moduleName := "cron4s-circe" ) .settings(commonSettings) @@ -406,7 +412,7 @@ lazy val decline = (crossProject(JSPlatform, JVMPlatform).crossType(CrossType.Pure) in file("modules/decline")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin, MimaPlugin) .settings( - name := "decline", + name := "decline", moduleName := "cron4s-decline" ) .settings(commonSettings) @@ -420,7 +426,7 @@ lazy val decline = lazy val doobie = (project in file("modules/doobie")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin, MimaPlugin) .settings( - name := "doobie", + name := "doobie", moduleName := "cron4s-doobie" ) .settings(commonSettings) diff --git a/modules/core/shared/src/main/scala/cron4s/datetime/PredicateReducer.scala b/modules/core/shared/src/main/scala-2/cron4s/datetime/PredicateReducer.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/datetime/PredicateReducer.scala rename to modules/core/shared/src/main/scala-2/cron4s/datetime/PredicateReducer.scala diff --git a/modules/core/shared/src/main/scala/cron4s/datetime/Stepper.scala b/modules/core/shared/src/main/scala-2/cron4s/datetime/Stepper.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/datetime/Stepper.scala rename to modules/core/shared/src/main/scala-2/cron4s/datetime/Stepper.scala diff --git a/modules/core/shared/src/main/scala/cron4s/datetime/package.scala b/modules/core/shared/src/main/scala-2/cron4s/datetime/package.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/datetime/package.scala rename to modules/core/shared/src/main/scala-2/cron4s/datetime/package.scala diff --git a/modules/core/shared/src/main/scala/cron4s/expr/CronExpr.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/CronExpr.scala similarity index 75% rename from modules/core/shared/src/main/scala/cron4s/expr/CronExpr.scala rename to modules/core/shared/src/main/scala-2/cron4s/expr/CronExpr.scala index 93840af5..576d2db6 100644 --- a/modules/core/shared/src/main/scala/cron4s/expr/CronExpr.scala +++ b/modules/core/shared/src/main/scala-2/cron4s/expr/CronExpr.scala @@ -16,8 +16,6 @@ package cron4s.expr -import cats.{Eq, Show} -import cats.implicits._ import shapeless._ @@ -51,17 +49,4 @@ final case class CronExpr( raw.map(_root_.cron4s.expr.ops.show).toList.mkString(" ") } -object CronExpr { - implicit val CronExprEq: Eq[CronExpr] = - Eq.instance { (lhs, rhs) => - lhs.seconds === rhs.seconds && - lhs.minutes === rhs.minutes && - lhs.hours === rhs.hours && - lhs.daysOfMonth === rhs.daysOfMonth && - lhs.months === rhs.months && - lhs.daysOfWeek === rhs.daysOfWeek - } - - implicit val CronExprShow: Show[CronExpr] = - Show.fromToString[CronExpr] -} +object CronExpr extends Cron4sInstances diff --git a/modules/core/shared/src/main/scala/cron4s/expr/FieldSelector.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/FieldSelector.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/expr/FieldSelector.scala rename to modules/core/shared/src/main/scala-2/cron4s/expr/FieldSelector.scala diff --git a/modules/core/shared/src/main/scala/cron4s/expr/NodeConversions.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/NodeConversions.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/expr/NodeConversions.scala rename to modules/core/shared/src/main/scala-2/cron4s/expr/NodeConversions.scala diff --git a/modules/core/shared/src/main/scala/cron4s/expr/wrappers.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/WrapperInstances.scala similarity index 85% rename from modules/core/shared/src/main/scala/cron4s/expr/wrappers.scala rename to modules/core/shared/src/main/scala-2/cron4s/expr/WrapperInstances.scala index 3395a1b7..3920be91 100644 --- a/modules/core/shared/src/main/scala/cron4s/expr/wrappers.scala +++ b/modules/core/shared/src/main/scala-2/cron4s/expr/WrapperInstances.scala @@ -15,28 +15,17 @@ */ package cron4s.expr - import cats.{Eq, Show} +import shapeless._ import cron4s.{CronField, CronUnit} import cron4s.base.Predicate -import shapeless._ - -/** - * Created by alonsodomin on 23/01/2017. - */ -final class FieldNode[F <: CronField](private[cron4s] val raw: RawFieldNode[F]) extends AnyVal { - override def toString: String = raw.fold(_root_.cron4s.expr.ops.show) -} - -object FieldNode { +private[cron4s] trait FieldNodeInstances { implicit def fieldNodeEq[F <: CronField]: Eq[FieldNode[F]] = Eq.fromUniversalEquals - implicit def fieldNodeShow[F <: CronField]: Show[FieldNode[F]] = Show.fromToString[FieldNode[F]] - implicit def fieldNodeInstance[F <: CronField]: FieldExpr[FieldNode, F] = new FieldExpr[FieldNode, F] { def matches(node: FieldNode[F]): Predicate[Int] = @@ -56,18 +45,12 @@ object FieldNode { case Inr(Inr(Inr(Inr(Inl(every))))) => every.implies(ee) case _ => sys.error("Impossible!") } - def unit(node: FieldNode[F]): CronUnit[F] = node.raw.fold(_root_.cron4s.expr.ops.unit) } } -final class FieldNodeWithAny[F <: CronField](private[cron4s] val raw: RawFieldNodeWithAny[F]) - extends AnyVal { - override def toString: String = raw.fold(_root_.cron4s.expr.ops.show) -} - -object FieldNodeWithAny { +private[cron4s] trait FieldNodeWithAnyInstances { implicit def fieldNodeWithAnyEq[F <: CronField]: Eq[FieldNodeWithAny[F]] = Eq.fromUniversalEquals @@ -81,7 +64,6 @@ object FieldNodeWithAny { def range(node: FieldNodeWithAny[F]): IndexedSeq[Int] = node.raw.fold(_root_.cron4s.expr.ops.range) - def implies[EE[_ <: CronField]]( node: FieldNodeWithAny[F] )(ee: EE[F])(implicit EE: FieldExpr[EE, F]): Boolean = @@ -95,12 +77,7 @@ object FieldNodeWithAny { } } -final class EnumerableNode[F <: CronField](private[cron4s] val raw: RawEnumerableNode[F]) - extends AnyVal { - override def toString: String = raw.fold(_root_.cron4s.expr.ops.show) -} - -object EnumerableNode { +private[cron4s] trait EnumerableNodeInstances { implicit def enumerableNodeEq[F <: CronField]: Eq[EnumerableNode[F]] = Eq.fromUniversalEquals @@ -120,7 +97,6 @@ object EnumerableNode { case Inr(Inl(between)) => between.implies(ee) case _ => sys.error("Impossible!") } - def range(node: EnumerableNode[F]): IndexedSeq[Int] = node.raw.fold(_root_.cron4s.expr.ops.range) @@ -129,12 +105,7 @@ object EnumerableNode { } } -final class DivisibleNode[F <: CronField](private[cron4s] val raw: RawDivisibleNode[F]) - extends AnyVal { - override def toString: String = raw.fold(_root_.cron4s.expr.ops.show) -} - -object DivisibleNode { +private[cron4s] trait DivisibleNodeInstances { implicit def divisibleNodeEq[F <: CronField]: Eq[DivisibleNode[F]] = Eq.fromUniversalEquals diff --git a/modules/core/shared/src/main/scala/cron4s/expr/ops.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/ops.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/expr/ops.scala rename to modules/core/shared/src/main/scala-2/cron4s/expr/ops.scala diff --git a/modules/core/shared/src/main/scala/cron4s/expr/package.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/package.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/expr/package.scala rename to modules/core/shared/src/main/scala-2/cron4s/expr/package.scala diff --git a/modules/core/shared/src/main/scala/cron4s/expr/parts.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/parts.scala similarity index 68% rename from modules/core/shared/src/main/scala/cron4s/expr/parts.scala rename to modules/core/shared/src/main/scala-2/cron4s/expr/parts.scala index 6b76eab2..9758baac 100644 --- a/modules/core/shared/src/main/scala/cron4s/expr/parts.scala +++ b/modules/core/shared/src/main/scala-2/cron4s/expr/parts.scala @@ -32,16 +32,7 @@ final case class DateCronExpr( raw.map(_root_.cron4s.expr.ops.show).toList.mkString(" ") } -object DateCronExpr { - implicit val dateCronEq: Eq[DateCronExpr] = Eq.instance { (lhs, rhs) => - lhs.daysOfMonth === rhs.daysOfMonth && - lhs.months === rhs.months && - lhs.daysOfWeek === rhs.daysOfWeek - } - - implicit val dateCronShow: Show[DateCronExpr] = - Show.fromToString[DateCronExpr] -} +object DateCronExpr extends DateCronExprInstances final case class TimeCronExpr( seconds: SecondsNode, @@ -54,13 +45,4 @@ final case class TimeCronExpr( raw.map(_root_.cron4s.expr.ops.show).toList.mkString(" ") } -object TimeCronExpr { - implicit val timeCronEq: Eq[TimeCronExpr] = Eq.instance { (lhs, rhs) => - lhs.seconds === rhs.seconds && - lhs.minutes === rhs.minutes && - lhs.hours === rhs.hours - } - - implicit val timeCronShow: Show[TimeCronExpr] = - Show.fromToString[TimeCronExpr] -} +object TimeCronExpr extends TimeCronExprInstances diff --git a/modules/core/shared/src/main/scala-2/cron4s/expr/wrappers.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/wrappers.scala new file mode 100644 index 00000000..0213fcf0 --- /dev/null +++ b/modules/core/shared/src/main/scala-2/cron4s/expr/wrappers.scala @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr + +import cats.{Eq, Show} + +import cron4s.{CronField, CronUnit} +import cron4s.base.Predicate + +import shapeless._ + +/** + * Created by alonsodomin on 23/01/2017. + */ +final class FieldNode[F <: CronField](private[cron4s] val raw: RawFieldNode[F]) extends AnyVal { + override def toString: String = raw.fold(_root_.cron4s.expr.ops.show) +} + +object FieldNode extends FieldNodeInstances + + +final class FieldNodeWithAny[F <: CronField](private[cron4s] val raw: RawFieldNodeWithAny[F]) + extends AnyVal { + override def toString: String = raw.fold(_root_.cron4s.expr.ops.show) +} + +object FieldNodeWithAny extends FieldNodeWithAnyInstances + +final class EnumerableNode[F <: CronField](private[cron4s] val raw: RawEnumerableNode[F]) + extends AnyVal { + override def toString: String = raw.fold(_root_.cron4s.expr.ops.show) +} + +object EnumerableNode extends EnumerableNodeInstances + +final class DivisibleNode[F <: CronField](private[cron4s] val raw: RawDivisibleNode[F]) + extends AnyVal { + override def toString: String = raw.fold(_root_.cron4s.expr.ops.show) +} + +object DivisibleNode extends DivisibleNodeInstances diff --git a/modules/core/shared/src/main/scala/cron4s/validation/NodeValidator.scala b/modules/core/shared/src/main/scala-2/cron4s/validation/NodeValidator.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/validation/NodeValidator.scala rename to modules/core/shared/src/main/scala-2/cron4s/validation/NodeValidator.scala diff --git a/modules/core/shared/src/main/scala/cron4s/validation/ops.scala b/modules/core/shared/src/main/scala-2/cron4s/validation/ops.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/validation/ops.scala rename to modules/core/shared/src/main/scala-2/cron4s/validation/ops.scala diff --git a/modules/core/shared/src/main/scala-3/cron4s/datetime/package.scala b/modules/core/shared/src/main/scala-3/cron4s/datetime/package.scala new file mode 100644 index 00000000..765cb3ed --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/datetime/package.scala @@ -0,0 +1,37 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s + +import cron4s.expr.{DateCronExpr, TimeCronExpr} + + +import scala.language.implicitConversions + +/** + * Created by alonsodomin on 24/01/2017. + */ +package object datetime { + import CronField._ + + private[datetime] type AnyCron = + CronExpr | TimeCronExpr | DateCronExpr + + + private[datetime] type FieldSeq = + Second *: Minute *: Hour *: DayOfMonth *: Month *: DayOfWeek *: EmptyTuple + val FieldSeq: FieldSeq = Second *: Minute *: Hour *: DayOfMonth *: Month *: DayOfWeek *: EmptyTuple +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala new file mode 100644 index 00000000..ec1776e6 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr + +import cats.syntax.all._ +/** + * Representation of a valid CRON expression as an AST + * + * @author Antonio Alonso Dominguez + */ +final case class CronExpr( + seconds: SecondsNode, + minutes: MinutesNode, + hours: HoursNode, + daysOfMonth: DaysOfMonthNode, + months: MonthsNode, + daysOfWeek: DaysOfWeekNode +) { + private[cron4s] lazy val raw: RawCronExpr = (seconds,minutes,hours,daysOfMonth,months,daysOfWeek) + + /** + * Time part of the CRON expression + */ + lazy val timePart: TimeCronExpr = TimeCronExpr(seconds, minutes, hours) + + /** + * Date part of the CRON expression + */ + lazy val datePart: DateCronExpr = + DateCronExpr(daysOfMonth, months, daysOfWeek) + + override lazy val toString: String = + //raw.map([T:Show]=>> (a:T) => _root_.cron4s.expr.ops.show).toList.mkString(" ") + raw match { + case (sec,min,hs,d,m,dw) => List[cron4s.expr.FieldNode[cron4s.CronField]](sec,min,hs,d,m,dw).map(_root_.cron4s.expr.ops.show).mkString(" ") + } +} + +object CronExpr extends Cron4sInstances diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala_ b/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala_ new file mode 100644 index 00000000..9b323cfd --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala_ @@ -0,0 +1,154 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr + +import cron4s.CronField + +import shapeless.{HList, Lazy} +import shapeless.ops.hlist.Selector + +import scala.annotation.implicitNotFound + +/** + * Created by alonsodomin on 10/02/2017. + */ +@implicitNotFound("Field ${F} is not a member of expression ${A}") +sealed trait FieldSelector[A, F <: CronField] { + type Raw <: HList + type Out[X <: CronField] + + protected implicit def hlistSelect: Lazy[Selector[Raw, Out[F]]] + + def selectFrom(expr: A): Out[F] +} + +object FieldSelector { + import CronField._ + + def apply[A, F <: CronField](implicit ev: FieldSelector[A, F]): FieldSelector[A, F] = ev + + implicit val SecondsFromCronExpr: FieldSelector[CronExpr, Second] = + new FullCronFieldNodeSelector[Second] { + implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Second]]] = + Selector[RawCronExpr, FieldNode[Second]] + } + implicit val SecondsFromTimeExpr: FieldSelector[TimeCronExpr, Second] = + new TimeCronFieldNodeSelector[Second] { + implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Second]]] = + Selector[RawTimeCronExpr, FieldNode[Second]] + } + + implicit val MinutesFromCronExpr: FieldSelector[CronExpr, Minute] = + new FullCronFieldNodeSelector[Minute] { + implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Minute]]] = + Selector[RawCronExpr, FieldNode[Minute]] + } + implicit val MinutesFromTimeExpr: FieldSelector[TimeCronExpr, Minute] = + new TimeCronFieldNodeSelector[Minute] { + implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Minute]]] = + Selector[RawTimeCronExpr, FieldNode[Minute]] + } + + implicit val HoursFromCronExpr: FieldSelector[CronExpr, Hour] = + new FullCronFieldNodeSelector[Hour] { + implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Hour]]] = + Selector[RawCronExpr, FieldNode[Hour]] + } + implicit val HoursFromTimeExpr: FieldSelector[TimeCronExpr, Hour] = + new TimeCronFieldNodeSelector[Hour] { + implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Hour]]] = + Selector[RawTimeCronExpr, FieldNode[Hour]] + } + + implicit val DayOfMonthFromCronExpr: FieldSelector[CronExpr, DayOfMonth] = + new FullCronFieldNodeWithAnySelector[DayOfMonth] { + implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNodeWithAny[DayOfMonth]]] = + Selector[RawCronExpr, FieldNodeWithAny[DayOfMonth]] + } + implicit val DayOfMonthFromDateExpr: FieldSelector[DateCronExpr, DayOfMonth] = + new DateCronFieldNodeWithAnySelector[DayOfMonth] { + implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNodeWithAny[DayOfMonth]]] = + Selector[RawDateCronExpr, FieldNodeWithAny[DayOfMonth]] + } + + implicit val MonthFromCronExpr: FieldSelector[CronExpr, Month] = + new FullCronFieldNodeSelector[Month] { + implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Month]]] = + Selector[RawCronExpr, FieldNode[Month]] + } + implicit val MonthFromDateExpr: FieldSelector[DateCronExpr, Month] = + new DateCronFieldNodeSelector[Month] { + implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNode[Month]]] = + Selector[RawDateCronExpr, FieldNode[Month]] + } + + implicit val DayOfWeekFromCronExpr: FieldSelector[CronExpr, DayOfWeek] = + new FullCronFieldNodeWithAnySelector[DayOfWeek] { + implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNodeWithAny[DayOfWeek]]] = + Selector[RawCronExpr, FieldNodeWithAny[DayOfWeek]] + } + implicit val DayOfWeekFromDateExpr: FieldSelector[DateCronExpr, DayOfWeek] = + new DateCronFieldNodeWithAnySelector[DayOfWeek] { + implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNodeWithAny[DayOfWeek]]] = + Selector[RawDateCronExpr, FieldNodeWithAny[DayOfWeek]] + } + + // Base classes adding type refinements for the typeclass instances + + private[this] abstract class FieldNodeSelector[A, F <: CronField] extends FieldSelector[A, F] { + type Out[X <: CronField] = FieldNode[X] + } + private[this] abstract class FullCronFieldNodeSelector[F <: CronField] + extends FieldNodeSelector[CronExpr, F] { + type Raw = RawCronExpr + + def selectFrom(expr: CronExpr): FieldNode[F] = hlistSelect.value(expr.raw) + } + private[this] abstract class TimeCronFieldNodeSelector[F <: CronField] + extends FieldNodeSelector[TimeCronExpr, F] { + type Raw = RawTimeCronExpr + + def selectFrom(expr: TimeCronExpr): FieldNode[F] = + hlistSelect.value(expr.raw) + } + private[this] abstract class DateCronFieldNodeSelector[F <: CronField] + extends FieldNodeSelector[DateCronExpr, F] { + type Raw = RawDateCronExpr + + def selectFrom(expr: DateCronExpr): FieldNode[F] = + hlistSelect.value(expr.raw) + } + + private[this] abstract class FieldNodeWithAnySelector[A, F <: CronField] + extends FieldSelector[A, F] { + type Out[X <: CronField] = FieldNodeWithAny[X] + } + private[this] abstract class FullCronFieldNodeWithAnySelector[F <: CronField] + extends FieldNodeWithAnySelector[CronExpr, F] { + type Raw = RawCronExpr + + def selectFrom(expr: CronExpr): FieldNodeWithAny[F] = + hlistSelect.value(expr.raw) + } + private[this] abstract class DateCronFieldNodeWithAnySelector[F <: CronField] + extends FieldNodeWithAnySelector[DateCronExpr, F] { + type Raw = RawDateCronExpr + + def selectFrom(expr: DateCronExpr): FieldNodeWithAny[F] = + hlistSelect.value(expr.raw) + } +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/NodeConversions.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/NodeConversions.scala new file mode 100644 index 00000000..0e719871 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/NodeConversions.scala @@ -0,0 +1,83 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr + +import cron4s.CronField + +import scala.language.implicitConversions + +/** + * Created by alonsodomin on 28/12/2016. + */ +private[cron4s] trait NodeConversions { + implicit def each2Field[F <: CronField](node: EachNode[F]): FieldNode[F] = + new FieldNode(node) + + implicit def const2Field[F <: CronField](node: ConstNode[F]): FieldNode[F] = + new FieldNode(node) + + implicit def between2Field[F <: CronField](node: BetweenNode[F]): FieldNode[F] = + new FieldNode(node) + + implicit def several2Field[F <: CronField](node: SeveralNode[F]): FieldNode[F] = + new FieldNode(node) + + implicit def every2Field[F <: CronField](node: EveryNode[F]): FieldNode[F] = + new FieldNode(node) + + implicit def any2FieldWithAny[F <: CronField](node: AnyNode[F]): FieldNodeWithAny[F] = + new FieldNodeWithAny(node) + + implicit def each2FieldWithAny[F <: CronField](node: EachNode[F]): FieldNodeWithAny[F] = + new FieldNodeWithAny(node) + + implicit def const2FieldWithAny[F <: CronField](node: ConstNode[F]): FieldNodeWithAny[F] = + new FieldNodeWithAny(node) + + implicit def between2FieldWithAny[F <: CronField](node: BetweenNode[F]): FieldNodeWithAny[F] = + new FieldNodeWithAny(node) + + implicit def several2FieldWithAny[F <: CronField](node: SeveralNode[F]): FieldNodeWithAny[F] = + new FieldNodeWithAny(node) + + implicit def every2FieldWithAny[F <: CronField](node: EveryNode[F]): FieldNodeWithAny[F] = + new FieldNodeWithAny(node) + + implicit def field2FieldWithAny[F <: CronField](node: FieldNode[F]): FieldNodeWithAny[F] = + new FieldNodeWithAny[F](node.raw) + + implicit def const2Enumerable[F <: CronField](node: ConstNode[F]): EnumerableNode[F] = + new EnumerableNode(node) + + implicit def between2Enumerable[F <: CronField](node: BetweenNode[F]): EnumerableNode[F] = + new EnumerableNode(node) + + implicit def each2Divisible[F <: CronField](node: EachNode[F]): DivisibleNode[F] = + new DivisibleNode(node) + + implicit def between2Divisible[F <: CronField](node: BetweenNode[F]): DivisibleNode[F] = + new DivisibleNode(node) + + implicit def several2Divisible[F <: CronField](node: SeveralNode[F]): DivisibleNode[F] = + new DivisibleNode(node) + + implicit def enumerable2Field[F <: CronField](node: EnumerableNode[F]): FieldNode[F] = + new FieldNode[F](node.raw) + + implicit def divisible2Field[F <: CronField](node: DivisibleNode[F]): FieldNode[F] = + new FieldNode[F](node.raw) +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/WrapperInstances.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/WrapperInstances.scala new file mode 100644 index 00000000..05caae95 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/WrapperInstances.scala @@ -0,0 +1,138 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr +import cats.{Eq, Show} +import scala.language.implicitConversions +import cron4s.toExprOps +import cron4s.{CronField, CronUnit} +import cron4s.base.Predicate + +private[cron4s] trait FieldNodeInstances { + implicit def fieldNodeEq[F <: CronField]: Eq[FieldNode[F]] = + Eq.fromUniversalEquals + implicit def fieldNodeShow[F <: CronField]: Show[FieldNode[F]] = + Show.fromToString[FieldNode[F]] + implicit def fieldNodeInstance[F <: CronField]: FieldExpr[FieldNode, F] = + new FieldExpr[FieldNode, F] { + def matches(node: FieldNode[F]): Predicate[Int] = + _root_.cron4s.expr.ops.matches(node.raw) + + def range(node: FieldNode[F]): IndexedSeq[Int] = + _root_.cron4s.expr.ops.range(node.raw) + + def implies[EE[_ <: CronField]]( + node: FieldNode[F] + )(ee: EE[F])(implicit EE: FieldExpr[EE, F]): Boolean = + node.raw match { + case each: EachNode[F] => each.implies(ee) + case const: ConstNode[F] => const.implies(ee) + case between: BetweenNode[F] => between.implies(ee) + case several: SeveralNode[F] => several.implies(ee) + case every: EveryNode[F] => every.implies(ee) + case _ => sys.error("Impossible!") + } + + def unit(node: FieldNode[F]): CronUnit[F] = + _root_.cron4s.expr.ops.unit(node.raw) + } +} + +private[cron4s] trait FieldNodeWithAnyInstances { + implicit def fieldNodeWithAnyEq[F <: CronField]: Eq[FieldNodeWithAny[F]] = + Eq.fromUniversalEquals + + implicit def fieldNodeWithAnyShow[F <: CronField]: Show[FieldNodeWithAny[F]] = + Show.fromToString[FieldNodeWithAny[F]] + + implicit def fieldNodeWithAnyInstance[F <: CronField]: FieldExpr[FieldNodeWithAny, F] = + new FieldExpr[FieldNodeWithAny, F] { + def matches(node: FieldNodeWithAny[F]): Predicate[Int] = + _root_.cron4s.expr.ops.matches(node.raw) + + def range(node: FieldNodeWithAny[F]): IndexedSeq[Int] = + _root_.cron4s.expr.ops.range(node.raw) + + def implies[EE[_ <: CronField]]( + node: FieldNodeWithAny[F] + )(ee: EE[F])(implicit EE: FieldExpr[EE, F]): Boolean = + node.raw match { + case any: AnyNode[F] => any.implies(ee) + case tail: RawFieldNode[F] => new FieldNode[F](tail).implies(ee) + } + + def unit(node: FieldNodeWithAny[F]): CronUnit[F] = + _root_.cron4s.expr.ops.unit(node.raw) + } +} + +private[cron4s] trait EnumerableNodeInstances { + implicit def enumerableNodeEq[F <: CronField]: Eq[EnumerableNode[F]] = + Eq.fromUniversalEquals + + implicit def enumerableNodeShow[F <: CronField]: Show[EnumerableNode[F]] = + Show.fromToString[EnumerableNode[F]] + + implicit def enumerableNodeInstance[F <: CronField]: FieldExpr[EnumerableNode, F] = + new FieldExpr[EnumerableNode, F] { + def matches(node: EnumerableNode[F]): Predicate[Int] = + _root_.cron4s.expr.ops.matches(node.raw) + + def implies[EE[_ <: CronField]]( + node: EnumerableNode[F] + )(ee: EE[F])(implicit EE: FieldExpr[EE, F]): Boolean = + node.raw match { + case const: ConstNode[F] => const.implies(ee) + case between: BetweenNode[F] => between.implies(ee) + case _ => sys.error("Impossible!") + } + + def range(node: EnumerableNode[F]): IndexedSeq[Int] = + _root_.cron4s.expr.ops.range(node.raw) + + def unit(node: EnumerableNode[F]): CronUnit[F] = + _root_.cron4s.expr.ops.unit(node.raw) + } +} + +private[cron4s] trait DivisibleNodeInstances { + implicit def divisibleNodeEq[F <: CronField]: Eq[DivisibleNode[F]] = + Eq.fromUniversalEquals + + implicit def divisibleNodeShow[F <: CronField]: Show[DivisibleNode[F]] = + Show.fromToString[DivisibleNode[F]] + + implicit def divisibleNodeInstance[F <: CronField]: FieldExpr[DivisibleNode, F] = + new FieldExpr[DivisibleNode, F] { + def matches(node: DivisibleNode[F]): Predicate[Int] = + _root_.cron4s.expr.ops.matches(node.raw) + + def implies[EE[_ <: CronField]]( + node: DivisibleNode[F] + )(ee: EE[F])(implicit EE: FieldExpr[EE, F]): Boolean = + node.raw match { + case each: EachNode[F] => each.implies(ee) + case between: BetweenNode[F] => between.implies(ee) + case several: SeveralNode[F] => several.implies(ee) + case _ => sys.error("Impossible!") + } + + def range(node: DivisibleNode[F]): IndexedSeq[Int] = + _root_.cron4s.expr.ops.range(node.raw) + + def unit(node: DivisibleNode[F]): CronUnit[F] = _root_.cron4s.expr.ops.unit(node.raw) + } +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala new file mode 100644 index 00000000..a87f41f4 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala @@ -0,0 +1,60 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr + +import cats.Show +import cats.implicits.toShow +import cron4s.CronField + +import cron4s.base.Predicate +import cron4s.CronUnit + +/** + * Created by alonsodomin on 17/12/2016. + */ +private[cron4s] object ops { + type PolyField[F <: CronField] = + EachNode[F] | AnyNode[F] | ConstNode[F] | BetweenNode[F] | SeveralNode[F] | EveryNode[ + F + ] | FieldNode[F] | FieldNodeWithAny[F] | EnumerableNode[F] | DivisibleNode[F] + def matches[F <: CronField](field: PolyField[F]): Predicate[Int] = ??? + def range[F <: CronField](field: PolyField[F]): IndexedSeq[Int] = ??? + def show[F <: CronField](field: PolyField[F]): String = field match { + case n: EachNode[F] => n.show + case n: AnyNode[F] => n.show + case n: ConstNode[F] => n.show + case n: BetweenNode[F] => n.show + case n: SeveralNode[F] => n.show + case n: EveryNode[F] => n.show + case n: FieldNode[F] => ??? + case n: FieldNodeWithAny[F] => ??? + case n: EnumerableNode[F] => ??? + case n: DivisibleNode[F] => ??? + } + def unit[F <: CronField](field: PolyField[F]): CronUnit[F] = field match { + case n: EachNode[F] => n.unit + case n: AnyNode[F] => n.unit + case n: ConstNode[F] => n.unit + case n: BetweenNode[F] => n.unit + case n: SeveralNode[F] => n.unit + case n: EveryNode[F] => n.unit + case n: FieldNode[F] => ??? + case n: FieldNodeWithAny[F] => ??? + case n: EnumerableNode[F] => ??? + case n: DivisibleNode[F] => ??? + } +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala new file mode 100644 index 00000000..e6bf1f47 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s + + +/** + * Created by alonsodomin on 04/01/2016. + */ +package object expr { + private[expr] type RawFieldNode[F <: CronField] = + EachNode[F] | ConstNode[F] | BetweenNode[F] | SeveralNode[F] | + EveryNode[F] + + private[expr] type RawFieldNodeWithAny[F <: CronField] = + AnyNode[F] | RawFieldNode[F] + + private[expr] type RawEnumerableNode[F <: CronField] = + ConstNode[F] | BetweenNode[F] + + private[expr] type RawDivisibleNode[F <: CronField] = + EachNode[F] | BetweenNode[F] | SeveralNode[F] + + type SecondsNode = FieldNode[CronField.Second] + type MinutesNode = FieldNode[CronField.Minute] + type HoursNode = FieldNode[CronField.Hour] + type DaysOfMonthNode = FieldNodeWithAny[CronField.DayOfMonth] + type MonthsNode = FieldNode[CronField.Month] + type DaysOfWeekNode = FieldNodeWithAny[CronField.DayOfWeek] + + private[cron4s] type RawTimeCronExpr = + SecondsNode *: MinutesNode *: HoursNode *: EmptyTuple + private[cron4s] type RawDateCronExpr = + DaysOfMonthNode *: MonthsNode *: DaysOfWeekNode *: EmptyTuple + + private[cron4s] type RawCronExpr = + SecondsNode *: MinutesNode *: HoursNode *: DaysOfMonthNode *: MonthsNode *: DaysOfWeekNode *: EmptyTuple +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/parts.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/parts.scala new file mode 100644 index 00000000..ae92906d --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/parts.scala @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr + +import cats._ +import cats.implicits._ + +final case class DateCronExpr( + daysOfMonth: DaysOfMonthNode, + months: MonthsNode, + daysOfWeek: DaysOfWeekNode +) { + private[cron4s] lazy val raw: RawDateCronExpr = (daysOfMonth, months, daysOfWeek) + + override lazy val toString: String = + List(daysOfMonth, months, daysOfWeek).map(_root_.cron4s.expr.ops.show).mkString(" ") +} + +object DateCronExpr extends DateCronExprInstances + +final case class TimeCronExpr( + seconds: SecondsNode, + minutes: MinutesNode, + hours: HoursNode +) { + private[cron4s] lazy val raw: RawTimeCronExpr = (seconds, minutes, hours) + + override lazy val toString: String = + List(seconds, minutes, hours).map(_root_.cron4s.expr.ops.show).mkString(" ") +} + +object TimeCronExpr extends TimeCronExprInstances diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala new file mode 100644 index 00000000..3b5c8993 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala @@ -0,0 +1,50 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr + +import cats.{Eq, Show} +import cats.syntax.all._ +import cron4s.{CronField, CronUnit} +import cron4s.base.Predicate + +/** + * Created by alonsodomin on 23/01/2017. + */ +final class FieldNode[F <: CronField](private[cron4s] val raw: RawFieldNode[F]) extends AnyVal { + override def toString: String = _root_.cron4s.expr.ops.show(raw) +} + +object FieldNode extends FieldNodeInstances + +final class FieldNodeWithAny[F <: CronField](private[cron4s] val raw: RawFieldNodeWithAny[F]) + extends AnyVal { + override def toString: String = _root_.cron4s.expr.ops.show(raw) +} + +object FieldNodeWithAny extends FieldNodeWithAnyInstances + +final class EnumerableNode[F <: CronField](private[cron4s] val raw: RawEnumerableNode[F]) + extends AnyVal { + override def toString: String = _root_.cron4s.expr.ops.show(raw) +} + +object EnumerableNode extends EnumerableNodeInstances + +final class DivisibleNode[F <: CronField](private[cron4s] val raw: RawDivisibleNode[F]) + extends AnyVal { + override def toString: String = _root_.cron4s.expr.ops.show(raw) +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/validation/NodeValidators.scala b/modules/core/shared/src/main/scala-3/cron4s/validation/NodeValidators.scala new file mode 100644 index 00000000..6487c21a --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/validation/NodeValidators.scala @@ -0,0 +1,183 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.validation + + +import cats.data._ +import cats.implicits._ + +import cron4s.{CronField, CronUnit, InvalidField} +import cron4s.expr._ +import cron4s.base.Enumerated +import cron4s.syntax.field._ + +/** + * Created by alonsodomin on 18/12/2016. + */ +sealed trait NodeValidator[A] { + def validate(node: A): List[InvalidField] +} + +object NodeValidator extends NodeValidatorInstances { + @inline def apply[A](implicit validator: NodeValidator[A]): NodeValidator[A] = + validator + + def alwaysValid[A]: NodeValidator[A] = + new NodeValidator[A] { + def validate(node: A): List[InvalidField] = List.empty + } +} + +private[validation] trait NodeValidatorInstances extends LowPriorityNodeValidatorInstances { + implicit def eachValidator[F <: CronField]: NodeValidator[EachNode[F]] = + NodeValidator.alwaysValid[EachNode[F]] + + implicit def anyValidator[F <: CronField]: NodeValidator[AnyNode[F]] = + NodeValidator.alwaysValid[AnyNode[F]] + + implicit def constValidator[F <: CronField](implicit + ev: Enumerated[CronUnit[F]] + ): NodeValidator[ConstNode[F]] = + new NodeValidator[ConstNode[F]] { + def validate(node: ConstNode[F]): List[InvalidField] = + if (node.value < node.unit.min || node.value > node.unit.max) + List( + InvalidField( + node.unit.field, + s"Value ${node.value} is out of bounds for field: ${node.unit.field}" + ) + ) + else List.empty + } + + implicit def betweenValidator[F <: CronField](implicit + ev: Enumerated[CronUnit[F]] + ): NodeValidator[BetweenNode[F]] = + new NodeValidator[BetweenNode[F]] { + val subValidator = NodeValidator[ConstNode[F]] + + def validate(node: BetweenNode[F]): List[InvalidField] = { + val baseErrors = List( + subValidator.validate(node.begin), + subValidator.validate(node.end) + ).flatten + + if (node.begin.value >= node.end.value) { + val error = InvalidField( + node.unit.field, + s"${node.begin.value} should be less than ${node.end.value}" + ) + error :: baseErrors + } else + baseErrors + } + } + + implicit def severalValidator[F <: CronField](implicit + ev: Enumerated[CronUnit[F]] + ): NodeValidator[SeveralNode[F]] = + new NodeValidator[SeveralNode[F]] { + val elemValidator = NodeValidator[EnumerableNode[F]] + + def implicationErrorMsg(that: EnumerableNode[F], impliedBy: EnumerableNode[F]): String = + s"Value '${that.show}' is implied by '${impliedBy.show}'" + + def checkImplication( + curr: EnumerableNode[F] + ): State[List[EnumerableNode[F]], List[List[InvalidField]]] = { + lazy val currField = curr.unit.field + + def impliedByError(elem: EnumerableNode[F]): List[InvalidField] = + if (curr.impliedBy(elem)) + List(InvalidField(currField, implicationErrorMsg(curr, elem))) + else Nil + + def impliesError(elem: EnumerableNode[F]): List[InvalidField] = + if (curr.implies(elem)) + List(InvalidField(currField, implicationErrorMsg(elem, curr))) + else Nil + + State { seen => + val errors = seen.foldMap(elem => impliesError(elem) ++ impliedByError(elem)) + (curr :: seen) -> List(errors) + } + } + + def validate(node: SeveralNode[F]): List[InvalidField] = { + val validation = node.values.foldMapM { elem => + val elemErrors = elemValidator.validate(elem) + // If subexpressions in the elements are not valid, then + // do not check for element implication + if (elemErrors.isEmpty) checkImplication(elem) + else + State.pure[List[EnumerableNode[F]], List[List[InvalidField]]](List(elemErrors)) + } + validation.map(_.flatten).runEmptyA.value + } + } + + implicit def everyValidator[F <: CronField](implicit + ev: Enumerated[CronUnit[F]] + ): NodeValidator[EveryNode[F]] = + new NodeValidator[EveryNode[F]] { + def validate(node: EveryNode[F]): List[InvalidField] = { + lazy val baseErrors = NodeValidator[DivisibleNode[F]].validate(node.base) + val evenlyDivided = (node.base.range.size % node.freq) == 0 + if (!evenlyDivided) + InvalidField( + node.unit.field, + s"Step '${node.freq}' does not evenly divide the value '${node.base.show}'" + ) :: baseErrors + else baseErrors + } + } +} + + +private[validation] trait LowPriorityNodeValidatorInstances { + implicit def enumerableNodeValidator[F <: CronField](implicit + ev: Enumerated[CronUnit[F]] + ): NodeValidator[EnumerableNode[F]] = + new NodeValidator[EnumerableNode[F]] { + def validate(node: EnumerableNode[F]): List[InvalidField] = + _root_.cron4s.validation.ops.validate(node.raw) + } + + implicit def divisibleNodeValidator[F <: CronField](implicit + ev: Enumerated[CronUnit[F]] + ): NodeValidator[DivisibleNode[F]] = + new NodeValidator[DivisibleNode[F]] { + def validate(node: DivisibleNode[F]): List[InvalidField] = + _root_.cron4s.validation.ops.validate(node.raw) + } + + implicit def fieldNodeValidator[F <: CronField](implicit + ev: Enumerated[CronUnit[F]] + ): NodeValidator[FieldNode[F]] = + new NodeValidator[FieldNode[F]] { + def validate(node: FieldNode[F]): List[InvalidField] = + _root_.cron4s.validation.ops.validate(node.raw) + } + + implicit def fieldNodeWithAnyValidator[F <: CronField](implicit + ev: Enumerated[CronUnit[F]] + ): NodeValidator[FieldNodeWithAny[F]] = + new NodeValidator[FieldNodeWithAny[F]] { + def validate(node: FieldNodeWithAny[F]): List[InvalidField] = + _root_.cron4s.validation.ops.validate(node.raw) + } +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/validation/ops.scala b/modules/core/shared/src/main/scala-3/cron4s/validation/ops.scala new file mode 100644 index 00000000..0aa7243a --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/validation/ops.scala @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.validation + +import cron4s.CronField +import cron4s.expr._ + +import cron4s.InvalidField + +/** + * Created by alonsodomin on 03/02/2017. + */ +private[validation] object ops extends NodeValidatorInstances { + type Validatable[F] = EachNode[F] | AnyNode[F] | ConstNode[F] | BetweenNode[F] | SeveralNode[ + F + ] | EveryNode[F] | FieldNode[F] | FieldNodeWithAny[F] | Nothing + def validate[F <: CronField](node: Validatable[F]): List[InvalidField] = node match { + case field: EachNode[F] => implicitly[NodeValidator[EachNode[F]]].validate(field) + case field: AnyNode[F] => implicitly[NodeValidator[AnyNode[F]]].validate(field) + case field: ConstNode[F] => implicitly[NodeValidator[ConstNode[F]]].validate(field) + case field: BetweenNode[F] => implicitly[NodeValidator[BetweenNode[F]]].validate(field) + case field: SeveralNode[F] => implicitly[NodeValidator[SeveralNode[F]]].validate(field) + case field: EveryNode[F] => implicitly[NodeValidator[EveryNode[F]]].validate(field) + case field: FieldNode[F] => implicitly[NodeValidator[FieldNode[F]]].validate(field) + case field: FieldNodeWithAny[F] => + implicitly[NodeValidator[FieldNodeWithAny[F]]].validate(field) + } +} diff --git a/modules/core/shared/src/main/scala-3/shapelessCompat.scala b/modules/core/shared/src/main/scala-3/shapelessCompat.scala new file mode 100644 index 00000000..da6ea180 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/shapelessCompat.scala @@ -0,0 +1,19 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s +import scala.compiletime.summonInline + diff --git a/modules/core/shared/src/main/scala/cron4s/expr/CronExprInstances.scala b/modules/core/shared/src/main/scala/cron4s/expr/CronExprInstances.scala new file mode 100644 index 00000000..2b29ff93 --- /dev/null +++ b/modules/core/shared/src/main/scala/cron4s/expr/CronExprInstances.scala @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr + +import cats.{Eq, Show} +import cats.implicits._ + +private[cron4s] trait Cron4sInstances { + implicit val CronExprEq: Eq[CronExpr] = + Eq.instance { (lhs, rhs) => + lhs.seconds === rhs.seconds && + lhs.minutes === rhs.minutes && + lhs.hours === rhs.hours && + lhs.daysOfMonth === rhs.daysOfMonth && + lhs.months === rhs.months && + lhs.daysOfWeek === rhs.daysOfWeek + } + + implicit val CronExprShow: Show[CronExpr] = + Show.fromToString[CronExpr] +} diff --git a/modules/core/shared/src/main/scala/cron4s/expr/DateCronExprInstances.scala b/modules/core/shared/src/main/scala/cron4s/expr/DateCronExprInstances.scala new file mode 100644 index 00000000..c809757f --- /dev/null +++ b/modules/core/shared/src/main/scala/cron4s/expr/DateCronExprInstances.scala @@ -0,0 +1,30 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr + +import cats._ +import cats.implicits._ + +private[cron4s] trait DateCronExprInstances { + implicit val dateCronEq: Eq[DateCronExpr] = Eq.instance { (lhs, rhs) => + lhs.daysOfMonth === rhs.daysOfMonth && + lhs.months === rhs.months && + lhs.daysOfWeek === rhs.daysOfWeek + } + implicit val dateCronShow: Show[DateCronExpr] = + Show.fromToString[DateCronExpr] +} diff --git a/modules/core/shared/src/main/scala/cron4s/expr/TimeCronExprInstances.scala b/modules/core/shared/src/main/scala/cron4s/expr/TimeCronExprInstances.scala new file mode 100644 index 00000000..27c376b5 --- /dev/null +++ b/modules/core/shared/src/main/scala/cron4s/expr/TimeCronExprInstances.scala @@ -0,0 +1,30 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.expr +import cats._ +import cats.implicits._ + +private[cron4s] trait TimeCronExprInstances { + implicit val timeCronEq: Eq[TimeCronExpr] = Eq.instance { (lhs, rhs) => + lhs.seconds === rhs.seconds && + lhs.minutes === rhs.minutes && + lhs.hours === rhs.hours + } + + implicit val timeCronShow: Show[TimeCronExpr] = + Show.fromToString[TimeCronExpr] +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a2b022b3..daaef08f 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -33,10 +33,12 @@ object Dependencies { lazy val core = Def.settings( libraryDependencies ++= Seq( - "com.chuusai" %%% "shapeless" % version.shapeless, "org.typelevel" %%% "cats-core" % version.cats.main, "org.scala-lang.modules" %%% "scala-parser-combinators" % version.parserc - ) + ), + libraryDependencies ++= (if (scalaVersion.value.startsWith("2.")) + Seq("com.chuusai" %%% "shapeless" % version.shapeless) + else Seq("org.typelevel" %% "shapeless3-deriving" % "3.0.1")) ) lazy val coreJS = Def.settings { @@ -90,7 +92,8 @@ object Dependencies { "moment-timezone" -> version.momenttz ) lazy val momentjs = Def.settings( - libraryDependencies += "ru.pavkin" %%% "scala-js-momentjs" % version.momentjs, + libraryDependencies += ("ru.pavkin" %%% "scala-js-momentjs" % version.momentjs) + .cross(CrossVersion.for3Use2_13), Compile / npmDependencies ++= momentjsNpmDeps, Test / npmDependencies ++= momentjsNpmDeps ) diff --git a/project/build.properties b/project/build.properties index 46e43a97..40b3b8e7 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.2 +sbt.version=1.9.0 From ba8099371942a7cf51b8f340ee20b595f63f2e20 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 13:31:38 +0900 Subject: [PATCH 02/16] update: scala 3 compiles except warning errors and field selectors --- .../scala-2/cron4s/datetime/package.scala | 10 ++ .../cron4s/validation/NodeValidator.scala | 2 +- .../cron4s/validation/package.scala | 0 .../cron4s/datetime/PredicateReducer.scala | 66 ++++++++ .../scala-3/cron4s/datetime/Stepper.scala | 144 ++++++++++++++++++ .../scala-3/cron4s/datetime/package.scala | 45 +++++- .../main/scala-3/cron4s/expr/CronExpr.scala | 17 ++- ...eldSelector.scala_ => FieldSelector.scala} | 68 ++++----- .../cron4s/expr/WrapperInstances.scala | 13 +- .../src/main/scala-3/cron4s/expr/ops.scala | 43 ++++-- .../src/main/scala-3/cron4s/expr/parts.scala | 6 +- .../main/scala-3/cron4s/expr/wrappers.scala | 1 + .../cron4s/validation/NodeValidators.scala | 6 +- .../main/scala-3/cron4s/validation/ops.scala | 15 +- .../scala-3/cron4s/validation/package.scala | 67 ++++++++ .../src/main/scala-3/shapelessCompat.scala | 19 --- .../scala/cron4s/datetime/DateTimeCron.scala | 6 +- 17 files changed, 439 insertions(+), 89 deletions(-) rename modules/core/shared/src/main/{scala => scala-2}/cron4s/validation/package.scala (100%) create mode 100644 modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala create mode 100644 modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala rename modules/core/shared/src/main/scala-3/cron4s/expr/{FieldSelector.scala_ => FieldSelector.scala} (62%) create mode 100644 modules/core/shared/src/main/scala-3/cron4s/validation/package.scala delete mode 100644 modules/core/shared/src/main/scala-3/shapelessCompat.scala diff --git a/modules/core/shared/src/main/scala-2/cron4s/datetime/package.scala b/modules/core/shared/src/main/scala-2/cron4s/datetime/package.scala index 0cf80162..f92ecf92 100644 --- a/modules/core/shared/src/main/scala-2/cron4s/datetime/package.scala +++ b/modules/core/shared/src/main/scala-2/cron4s/datetime/package.scala @@ -26,6 +26,16 @@ import scala.language.implicitConversions * Created by alonsodomin on 24/01/2017. */ package object datetime { + import cron4s.expr._ + private[datetime] def applyRange( + expr: DateCronExpr + ): List[IndexedSeq[Int]] = expr.raw.map(ops.range).toList + private[datetime] def applyRange( + expr: TimeCronExpr + ): List[IndexedSeq[Int]] = expr.raw.map(ops.range).toList + private[datetime] def applyRange( + expr: CronExpr + ): List[IndexedSeq[Int]] = expr.raw.map(ops.range).toList import CronField._ private[datetime] type AnyCron = diff --git a/modules/core/shared/src/main/scala-2/cron4s/validation/NodeValidator.scala b/modules/core/shared/src/main/scala-2/cron4s/validation/NodeValidator.scala index db02d91a..eac8b75b 100644 --- a/modules/core/shared/src/main/scala-2/cron4s/validation/NodeValidator.scala +++ b/modules/core/shared/src/main/scala-2/cron4s/validation/NodeValidator.scala @@ -135,7 +135,7 @@ private[validation] trait NodeValidatorInstances extends LowPriorityNodeValidato new NodeValidator[EveryNode[F]] { def validate(node: EveryNode[F]): List[InvalidField] = { lazy val baseErrors = NodeValidator[DivisibleNode[F]].validate(node.base) - val evenlyDivided = (node.base.range.size % node.freq) == 0 + val evenlyDivided = (toEnumeratedOps(node.base).range.size % node.freq) == 0 if (!evenlyDivided) InvalidField( node.unit.field, diff --git a/modules/core/shared/src/main/scala/cron4s/validation/package.scala b/modules/core/shared/src/main/scala-2/cron4s/validation/package.scala similarity index 100% rename from modules/core/shared/src/main/scala/cron4s/validation/package.scala rename to modules/core/shared/src/main/scala-2/cron4s/validation/package.scala diff --git a/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala b/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala new file mode 100644 index 00000000..ab645b38 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala @@ -0,0 +1,66 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.datetime + +import cats.MonoidK +import cats.instances.list._ + +import cron4s.CronField +import cron4s.expr._ +import cron4s.base._ +import cron4s.syntax.predicate._ + +/** + * Created by domingueza on 29/07/2016. + */ +private[datetime] final class PredicateReducer[DateTime](DT: IsDateTime[DateTime])(implicit + M: MonoidK[Predicate] +) { + type Predicatable = + SecondsNode | MinutesNode | HoursNode | DaysOfMonthNode | MonthsNode | DaysOfWeekNode + + def asPredicate(t: Predicatable): Predicate[DateTime] = { + def predicateFor[N[_ <: CronField], F <: CronField](field: F, node: N[F])(implicit + expr: FieldExpr[N, F] + ): Predicate[DateTime] = + Predicate { dt => + DT.get(dt, field) + .map(expr.matches(node)) + .getOrElse(M.empty[DateTime](dt)) + } + import CronField._ + t match { + case t: SecondsNode => + predicateFor(Second, t) + case t: MinutesNode => predicateFor(Minute, t) + case t: HoursNode => predicateFor(Hour, t) + case t: DaysOfMonthNode => predicateFor(DayOfMonth, t) + case t: MonthsNode => predicateFor(Month, t) + case t: DaysOfWeekNode => predicateFor(DayOfWeek, t) + } + } + + type FromRawable = CronExpr | DateCronExpr | TimeCronExpr + def fromRaw(t: FromRawable): List[Predicate[DateTime]] = t match { + case t: CronExpr => t.raw.toList.map(asPredicate) + case t: DateCronExpr => t.raw.toList.map(asPredicate) + case t: TimeCronExpr => t.raw.toList.map(asPredicate) + } + + def run(cron: AnyCron): Predicate[DateTime] = + asOf(fromRaw(cron)) +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala b/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala new file mode 100644 index 00000000..5bd44cf2 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala @@ -0,0 +1,144 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.datetime + +import cats.syntax.either._ + +import cron4s._ +import cron4s.base.{Direction, Step} +import cron4s.expr._ + +import scala.annotation.tailrec + +private[datetime] final class Stepper[DateTime](DT: IsDateTime[DateTime]) { + private type ResetPrevFn = DateTime => Option[DateTime] + private type StepST = Option[(ResetPrevFn, DateTime, Step)] + + private val identityReset: ResetPrevFn = Some(_) + + private[this] def stepNode[N[_ <: CronField], F <: CronField](stepState: StepST, node: N[F])( + implicit expr: FieldExpr[N, F] + ): StepST = { + def attemptSet( + dt: DateTime, + step: Step, + newValue: Int, + carryOver: Int + ): Option[(DateTime, Int)] = + DT.set(dt, node.unit.field, newValue) + .map(_ -> carryOver) + .recover { + case InvalidFieldValue(_, _) => + val newCarryOver = step.direction match { + case Direction.Forward => Math.max(carryOver, step.direction.sign) + case Direction.Backwards => + Math.min(carryOver, step.direction.sign) + } + dt -> newCarryOver + } + .toOption + + stepState.flatMap { + case (resetPrevious, from, step) => + def resetThis: DateTime => Option[DateTime] = { + val resetValue = step.direction match { + case Direction.Forward => node.min + case Direction.Backwards => node.max + } + + resetPrevious.andThen(_.flatMap(DT.set(_, node.unit.field, resetValue).toOption)) + } + + DT.get(from, node.unit.field).toOption.flatMap { currentValue => + node.step(currentValue, step) match { + case Some((newValue, carryOver)) => + // Attempt to set a new value in the field and reset previous fields + attemptSet(from, step, newValue, carryOver) + .flatMap { case (dt, co) => resetPrevious(dt).map(_ -> co) } + .map { + case (dt, co) => + (resetThis, dt, step.copy(amount = Math.abs(co))) + } + + case None => + Some((resetThis, from, step.copy(amount = 0))) + } + } + } + } + + private[this] def stepOverMonth(prev: StepST, expr: MonthsNode): StepST = + for { + (_, dt, s @ Step(carryOver, dir)) <- stepNode(prev, expr) + newDateTime <- DT.plus(dt, carryOver * 12 * dir.sign, DateTimeUnit.Months) + } yield (identityReset, newDateTime, s.copy(amount = 0)) + + private[this] def stepOverDayOfWeek(prev: StepST, expr: DaysOfWeekNode): StepST = + for { + (_, dt, s @ Step(carryOver, dir)) <- stepNode(prev, expr) + newDateTime <- DT.plus(dt, carryOver * dir.sign, DateTimeUnit.Weeks) + } yield (identityReset, newDateTime, s.copy(amount = 0)) + + type Steppable = + SecondsNode | MinutesNode | HoursNode | DaysOfMonthNode | MonthsNode | DaysOfWeekNode + def stepPerNode(step: StepST, node: Steppable): StepST = node match { + case node: SecondsNode => stepNode(step, node) + case node: MinutesNode => stepNode(step, node) + case node: HoursNode => stepNode(step, node) + case node: DaysOfMonthNode => stepNode(step, node) + case node: MonthsNode => stepOverMonth(step, node) + case node: DaysOfWeekNode => stepOverDayOfWeek(step, node) + } + + type FoldInternalExprable = CronExpr | DateCronExpr | TimeCronExpr + def foldInternalExpr( + stepSt: StepST, + expr: FoldInternalExprable + ): Option[(ResetPrevFn, DateTime, Step)] = expr match { + case expr: CronExpr => + val dateWithoutDOW = expr.datePart.raw.take(2) + val (_, _, daysOfWeekNode) = expr.datePart.raw + + for { + st @ (resetTime, _, _) <- + expr.timePart.raw.toList.foldLeft(stepSt)(stepPerNode) + (_, dt, step) <- dateWithoutDOW.toList.foldLeft(Some(st): StepST)(stepPerNode) + result <- stepOverDayOfWeek(Some((resetTime, dt, step)), daysOfWeekNode) + } yield result + case expr: DateCronExpr => + expr.raw.toList.foldLeft(stepSt)(stepPerNode) + case expr: TimeCronExpr => + expr.raw.toList.foldLeft(stepSt)(stepPerNode) + } + + def run(cron: AnyCron, from: DateTime, step: Step): Option[DateTime] = { + def initial(dt: DateTime): StepST = + Some((identityReset, dt, step.copy(amount = 1))) + + @tailrec + def go(stepSt: StepST, iteration: Int): StepST = + if (iteration == step.amount) stepSt + else + foldInternalExpr(stepSt, cron) match { + case Some((_, dt, _)) => + go(initial(dt), iteration + 1) + case None => None + } + + go(initial(from), 0).map(_._2) + } +} diff --git a/modules/core/shared/src/main/scala-3/cron4s/datetime/package.scala b/modules/core/shared/src/main/scala-3/cron4s/datetime/package.scala index 765cb3ed..c893e8f8 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/datetime/package.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/datetime/package.scala @@ -18,20 +18,59 @@ package cron4s import cron4s.expr.{DateCronExpr, TimeCronExpr} - import scala.language.implicitConversions /** * Created by alonsodomin on 24/01/2017. */ package object datetime { + private[datetime] def applyRange( + expr: DateCronExpr + ): List[IndexedSeq[Int]] = { + expr.raw match { + case (daysOfMonth, months, daysOfWeek) => + List( + cron4s.expr.ops.range(daysOfMonth), + cron4s.expr.ops.range(months), + cron4s.expr.ops.range(daysOfWeek) + ) + } + } + private[datetime] def applyRange( + expr: TimeCronExpr + ): List[IndexedSeq[Int]] = { + expr.raw match { + case (seconds, minutes, hours) => + List( + cron4s.expr.ops.range(seconds), + cron4s.expr.ops.range(minutes), + cron4s.expr.ops.range(hours) + ) + } + } + private[datetime] def applyRange( + expr: CronExpr + ): List[IndexedSeq[Int]] = { + expr.raw match { + case (seconds, minutes, hours, daysOfMonth, months, daysOfWeek) => + List( + cron4s.expr.ops.range(seconds), + cron4s.expr.ops.range(minutes), + cron4s.expr.ops.range(hours), + cron4s.expr.ops.range(daysOfMonth), + cron4s.expr.ops.range(months), + cron4s.expr.ops.range(daysOfWeek) + ) + } + } + import CronField._ private[datetime] type AnyCron = CronExpr | TimeCronExpr | DateCronExpr - private[datetime] type FieldSeq = Second *: Minute *: Hour *: DayOfMonth *: Month *: DayOfWeek *: EmptyTuple - val FieldSeq: FieldSeq = Second *: Minute *: Hour *: DayOfMonth *: Month *: DayOfWeek *: EmptyTuple + val FieldSeq: FieldSeq = + Second *: Minute *: Hour *: DayOfMonth *: Month *: DayOfWeek *: EmptyTuple } diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala index ec1776e6..6e6a9d2f 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala @@ -15,8 +15,9 @@ */ package cron4s.expr - +import cron4s.CronField import cats.syntax.all._ + /** * Representation of a valid CRON expression as an AST * @@ -30,7 +31,8 @@ final case class CronExpr( months: MonthsNode, daysOfWeek: DaysOfWeekNode ) { - private[cron4s] lazy val raw: RawCronExpr = (seconds,minutes,hours,daysOfMonth,months,daysOfWeek) + private[cron4s] lazy val raw: RawCronExpr = + (seconds, minutes, hours, daysOfMonth, months, daysOfWeek) /** * Time part of the CRON expression @@ -44,9 +46,16 @@ final case class CronExpr( DateCronExpr(daysOfMonth, months, daysOfWeek) override lazy val toString: String = - //raw.map([T:Show]=>> (a:T) => _root_.cron4s.expr.ops.show).toList.mkString(" ") raw match { - case (sec,min,hs,d,m,dw) => List[cron4s.expr.FieldNode[cron4s.CronField]](sec,min,hs,d,m,dw).map(_root_.cron4s.expr.ops.show).mkString(" ") + case (sec, min, hs, d, m, dw) => + List( + _root_.cron4s.expr.ops.show(sec), + _root_.cron4s.expr.ops.show(min), + _root_.cron4s.expr.ops.show(hs), + _root_.cron4s.expr.ops.show(d), + _root_.cron4s.expr.ops.show(m), + _root_.cron4s.expr.ops.show(dw) + ).mkString(" ") } } diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala_ b/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala similarity index 62% rename from modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala_ rename to modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala index 9b323cfd..4acf878c 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala_ +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala @@ -18,8 +18,6 @@ package cron4s.expr import cron4s.CronField -import shapeless.{HList, Lazy} -import shapeless.ops.hlist.Selector import scala.annotation.implicitNotFound @@ -28,10 +26,10 @@ import scala.annotation.implicitNotFound */ @implicitNotFound("Field ${F} is not a member of expression ${A}") sealed trait FieldSelector[A, F <: CronField] { - type Raw <: HList + type Raw <: Tuple type Out[X <: CronField] - protected implicit def hlistSelect: Lazy[Selector[Raw, Out[F]]] + //protected implicit def hlistSelect: Lazy[Selector[Raw, Out[F]]] def selectFrom(expr: A): Out[F] } @@ -43,68 +41,68 @@ object FieldSelector { implicit val SecondsFromCronExpr: FieldSelector[CronExpr, Second] = new FullCronFieldNodeSelector[Second] { - implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Second]]] = - Selector[RawCronExpr, FieldNode[Second]] + // implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Second]]] = + // Selector[RawCronExpr, FieldNode[Second]] } implicit val SecondsFromTimeExpr: FieldSelector[TimeCronExpr, Second] = new TimeCronFieldNodeSelector[Second] { - implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Second]]] = - Selector[RawTimeCronExpr, FieldNode[Second]] + //implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Second]]] = + // Selector[RawTimeCronExpr, FieldNode[Second]] } implicit val MinutesFromCronExpr: FieldSelector[CronExpr, Minute] = new FullCronFieldNodeSelector[Minute] { - implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Minute]]] = - Selector[RawCronExpr, FieldNode[Minute]] + //implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Minute]]] = + // Selector[RawCronExpr, FieldNode[Minute]] } implicit val MinutesFromTimeExpr: FieldSelector[TimeCronExpr, Minute] = new TimeCronFieldNodeSelector[Minute] { - implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Minute]]] = - Selector[RawTimeCronExpr, FieldNode[Minute]] + //implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Minute]]] = + // Selector[RawTimeCronExpr, FieldNode[Minute]] } implicit val HoursFromCronExpr: FieldSelector[CronExpr, Hour] = new FullCronFieldNodeSelector[Hour] { - implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Hour]]] = - Selector[RawCronExpr, FieldNode[Hour]] + //implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Hour]]] = + // Selector[RawCronExpr, FieldNode[Hour]] } implicit val HoursFromTimeExpr: FieldSelector[TimeCronExpr, Hour] = new TimeCronFieldNodeSelector[Hour] { - implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Hour]]] = - Selector[RawTimeCronExpr, FieldNode[Hour]] + //implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Hour]]] = + // Selector[RawTimeCronExpr, FieldNode[Hour]] } implicit val DayOfMonthFromCronExpr: FieldSelector[CronExpr, DayOfMonth] = new FullCronFieldNodeWithAnySelector[DayOfMonth] { - implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNodeWithAny[DayOfMonth]]] = - Selector[RawCronExpr, FieldNodeWithAny[DayOfMonth]] + // implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNodeWithAny[DayOfMonth]]] = + // Selector[RawCronExpr, FieldNodeWithAny[DayOfMonth]] } implicit val DayOfMonthFromDateExpr: FieldSelector[DateCronExpr, DayOfMonth] = new DateCronFieldNodeWithAnySelector[DayOfMonth] { - implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNodeWithAny[DayOfMonth]]] = - Selector[RawDateCronExpr, FieldNodeWithAny[DayOfMonth]] + // implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNodeWithAny[DayOfMonth]]] = + // Selector[RawDateCronExpr, FieldNodeWithAny[DayOfMonth]] } implicit val MonthFromCronExpr: FieldSelector[CronExpr, Month] = new FullCronFieldNodeSelector[Month] { - implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Month]]] = - Selector[RawCronExpr, FieldNode[Month]] + // implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Month]]] = + // Selector[RawCronExpr, FieldNode[Month]] } implicit val MonthFromDateExpr: FieldSelector[DateCronExpr, Month] = new DateCronFieldNodeSelector[Month] { - implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNode[Month]]] = - Selector[RawDateCronExpr, FieldNode[Month]] + // implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNode[Month]]] = + // Selector[RawDateCronExpr, FieldNode[Month]] } implicit val DayOfWeekFromCronExpr: FieldSelector[CronExpr, DayOfWeek] = new FullCronFieldNodeWithAnySelector[DayOfWeek] { - implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNodeWithAny[DayOfWeek]]] = - Selector[RawCronExpr, FieldNodeWithAny[DayOfWeek]] + // implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNodeWithAny[DayOfWeek]]] = + // Selector[RawCronExpr, FieldNodeWithAny[DayOfWeek]] } implicit val DayOfWeekFromDateExpr: FieldSelector[DateCronExpr, DayOfWeek] = new DateCronFieldNodeWithAnySelector[DayOfWeek] { - implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNodeWithAny[DayOfWeek]]] = - Selector[RawDateCronExpr, FieldNodeWithAny[DayOfWeek]] + // implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNodeWithAny[DayOfWeek]]] = + // Selector[RawDateCronExpr, FieldNodeWithAny[DayOfWeek]] } // Base classes adding type refinements for the typeclass instances @@ -116,21 +114,19 @@ object FieldSelector { extends FieldNodeSelector[CronExpr, F] { type Raw = RawCronExpr - def selectFrom(expr: CronExpr): FieldNode[F] = hlistSelect.value(expr.raw) + def selectFrom(expr: CronExpr): FieldNode[F] = ??? } private[this] abstract class TimeCronFieldNodeSelector[F <: CronField] extends FieldNodeSelector[TimeCronExpr, F] { type Raw = RawTimeCronExpr - def selectFrom(expr: TimeCronExpr): FieldNode[F] = - hlistSelect.value(expr.raw) + def selectFrom(expr: TimeCronExpr): FieldNode[F] = ??? } private[this] abstract class DateCronFieldNodeSelector[F <: CronField] extends FieldNodeSelector[DateCronExpr, F] { type Raw = RawDateCronExpr - def selectFrom(expr: DateCronExpr): FieldNode[F] = - hlistSelect.value(expr.raw) + def selectFrom(expr: DateCronExpr): FieldNode[F] = ??? } private[this] abstract class FieldNodeWithAnySelector[A, F <: CronField] @@ -141,14 +137,12 @@ object FieldSelector { extends FieldNodeWithAnySelector[CronExpr, F] { type Raw = RawCronExpr - def selectFrom(expr: CronExpr): FieldNodeWithAny[F] = - hlistSelect.value(expr.raw) + def selectFrom(expr: CronExpr): FieldNodeWithAny[F] = ??? } private[this] abstract class DateCronFieldNodeWithAnySelector[F <: CronField] extends FieldNodeWithAnySelector[DateCronExpr, F] { type Raw = RawDateCronExpr - def selectFrom(expr: DateCronExpr): FieldNodeWithAny[F] = - hlistSelect.value(expr.raw) + def selectFrom(expr: DateCronExpr): FieldNodeWithAny[F] = ??? } } diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/WrapperInstances.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/WrapperInstances.scala index 05caae95..bd97ca42 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/WrapperInstances.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/WrapperInstances.scala @@ -43,7 +43,8 @@ private[cron4s] trait FieldNodeInstances { case between: BetweenNode[F] => between.implies(ee) case several: SeveralNode[F] => several.implies(ee) case every: EveryNode[F] => every.implies(ee) - case _ => sys.error("Impossible!") + case null => + sys.error("expect RawFieldNode[F] but got null at `FieldExpr[FieldNode,F]#implies`") } def unit(node: FieldNode[F]): CronUnit[F] = @@ -97,7 +98,10 @@ private[cron4s] trait EnumerableNodeInstances { node.raw match { case const: ConstNode[F] => const.implies(ee) case between: BetweenNode[F] => between.implies(ee) - case _ => sys.error("Impossible!") + case null => + sys.error( + "expect RawEnumerableNode[F] but got null at `FieldExpr[EnumerableNode,F]#implies`" + ) } def range(node: EnumerableNode[F]): IndexedSeq[Int] = @@ -127,7 +131,10 @@ private[cron4s] trait DivisibleNodeInstances { case each: EachNode[F] => each.implies(ee) case between: BetweenNode[F] => between.implies(ee) case several: SeveralNode[F] => several.implies(ee) - case _ => sys.error("Impossible!") + case null => + sys.error( + "expect DivisibleNode[F] but got null at `FieldExpr[DivisibleNode,F]#implies`" + ) } def range(node: DivisibleNode[F]): IndexedSeq[Int] = diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala index a87f41f4..b069c883 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala @@ -31,8 +31,31 @@ private[cron4s] object ops { EachNode[F] | AnyNode[F] | ConstNode[F] | BetweenNode[F] | SeveralNode[F] | EveryNode[ F ] | FieldNode[F] | FieldNodeWithAny[F] | EnumerableNode[F] | DivisibleNode[F] - def matches[F <: CronField](field: PolyField[F]): Predicate[Int] = ??? - def range[F <: CronField](field: PolyField[F]): IndexedSeq[Int] = ??? + import cron4s.syntax.all.toExprOps + def matches[F <: CronField](field: PolyField[F]): Predicate[Int] = field match { + case node: EachNode[F] => node.matches + case node: AnyNode[F] => node.matches + case node: ConstNode[F] => node.matches + case node: BetweenNode[F] => node.matches + case node: SeveralNode[F] => node.matches + case node: EveryNode[F] => node.matches + case node: FieldNode[F] => matches(node.raw) + case node: FieldNodeWithAny[F] => matches(node.raw) + case node: EnumerableNode[F] => matches(node.raw) + case node: DivisibleNode[F] => matches(node.raw) + } + def range[F <: CronField](field: PolyField[F]): IndexedSeq[Int] = field match { + case node: EachNode[F] => node.range + case node: AnyNode[F] => node.range + case node: ConstNode[F] => node.range + case node: BetweenNode[F] => node.range + case node: SeveralNode[F] => node.range + case node: EveryNode[F] => node.range + case node: FieldNode[F] => range(node.raw) + case node: FieldNodeWithAny[F] => range(node.raw) + case node: EnumerableNode[F] => range(node.raw) + case node: DivisibleNode[F] => range(node.raw) + } def show[F <: CronField](field: PolyField[F]): String = field match { case n: EachNode[F] => n.show case n: AnyNode[F] => n.show @@ -40,10 +63,10 @@ private[cron4s] object ops { case n: BetweenNode[F] => n.show case n: SeveralNode[F] => n.show case n: EveryNode[F] => n.show - case n: FieldNode[F] => ??? - case n: FieldNodeWithAny[F] => ??? - case n: EnumerableNode[F] => ??? - case n: DivisibleNode[F] => ??? + case n: FieldNode[F] => n.show + case n: FieldNodeWithAny[F] => n.show + case n: EnumerableNode[F] => n.show + case n: DivisibleNode[F] => n.show } def unit[F <: CronField](field: PolyField[F]): CronUnit[F] = field match { case n: EachNode[F] => n.unit @@ -52,9 +75,9 @@ private[cron4s] object ops { case n: BetweenNode[F] => n.unit case n: SeveralNode[F] => n.unit case n: EveryNode[F] => n.unit - case n: FieldNode[F] => ??? - case n: FieldNodeWithAny[F] => ??? - case n: EnumerableNode[F] => ??? - case n: DivisibleNode[F] => ??? + case n: FieldNode[F] => unit(n.raw) + case n: FieldNodeWithAny[F] => unit(n.raw) + case n: EnumerableNode[F] => unit(n.raw) + case n: DivisibleNode[F] => unit(n.raw) } } diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/parts.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/parts.scala index ae92906d..7c8e7bda 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/parts.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/parts.scala @@ -27,7 +27,11 @@ final case class DateCronExpr( private[cron4s] lazy val raw: RawDateCronExpr = (daysOfMonth, months, daysOfWeek) override lazy val toString: String = - List(daysOfMonth, months, daysOfWeek).map(_root_.cron4s.expr.ops.show).mkString(" ") + List( + _root_.cron4s.expr.ops.show(daysOfMonth), + _root_.cron4s.expr.ops.show(months), + _root_.cron4s.expr.ops.show(daysOfWeek) + ).mkString(" ") } object DateCronExpr extends DateCronExprInstances diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala index 3b5c8993..b2263466 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala @@ -48,3 +48,4 @@ final class DivisibleNode[F <: CronField](private[cron4s] val raw: RawDivisibleN extends AnyVal { override def toString: String = _root_.cron4s.expr.ops.show(raw) } +object DivisibleNode extends DivisibleNodeInstances diff --git a/modules/core/shared/src/main/scala-3/cron4s/validation/NodeValidators.scala b/modules/core/shared/src/main/scala-3/cron4s/validation/NodeValidators.scala index 6487c21a..ad8c6fa0 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/validation/NodeValidators.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/validation/NodeValidators.scala @@ -23,7 +23,6 @@ import cats.implicits._ import cron4s.{CronField, CronUnit, InvalidField} import cron4s.expr._ import cron4s.base.Enumerated -import cron4s.syntax.field._ /** * Created by alonsodomin on 18/12/2016. @@ -53,6 +52,8 @@ private[validation] trait NodeValidatorInstances extends LowPriorityNodeValidato ev: Enumerated[CronUnit[F]] ): NodeValidator[ConstNode[F]] = new NodeValidator[ConstNode[F]] { + import scala.language.implicitConversions + import cron4s.syntax.all.toEnumeratedOps def validate(node: ConstNode[F]): List[InvalidField] = if (node.value < node.unit.min || node.value > node.unit.max) List( @@ -99,6 +100,8 @@ private[validation] trait NodeValidatorInstances extends LowPriorityNodeValidato def checkImplication( curr: EnumerableNode[F] ): State[List[EnumerableNode[F]], List[List[InvalidField]]] = { + import scala.language.implicitConversions + import cron4s.syntax.all.toExprOps lazy val currField = curr.unit.field def impliedByError(elem: EnumerableNode[F]): List[InvalidField] = @@ -133,6 +136,7 @@ private[validation] trait NodeValidatorInstances extends LowPriorityNodeValidato implicit def everyValidator[F <: CronField](implicit ev: Enumerated[CronUnit[F]] ): NodeValidator[EveryNode[F]] = + import cron4s.syntax.enumerated.toEnumeratedOps new NodeValidator[EveryNode[F]] { def validate(node: EveryNode[F]): List[InvalidField] = { lazy val baseErrors = NodeValidator[DivisibleNode[F]].validate(node.base) diff --git a/modules/core/shared/src/main/scala-3/cron4s/validation/ops.scala b/modules/core/shared/src/main/scala-3/cron4s/validation/ops.scala index 0aa7243a..6d0af7bb 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/validation/ops.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/validation/ops.scala @@ -17,22 +17,23 @@ package cron4s.validation import cron4s.CronField +import cron4s.CronUnit import cron4s.expr._ - +import cron4s.base.Enumerated import cron4s.InvalidField /** * Created by alonsodomin on 03/02/2017. */ private[validation] object ops extends NodeValidatorInstances { - type Validatable[F] = EachNode[F] | AnyNode[F] | ConstNode[F] | BetweenNode[F] | SeveralNode[ + type Validatable[F<: CronField] = EachNode[F] | AnyNode[F] | ConstNode[F] | BetweenNode[F] | SeveralNode[ F ] | EveryNode[F] | FieldNode[F] | FieldNodeWithAny[F] | Nothing - def validate[F <: CronField](node: Validatable[F]): List[InvalidField] = node match { - case field: EachNode[F] => implicitly[NodeValidator[EachNode[F]]].validate(field) - case field: AnyNode[F] => implicitly[NodeValidator[AnyNode[F]]].validate(field) - case field: ConstNode[F] => implicitly[NodeValidator[ConstNode[F]]].validate(field) - case field: BetweenNode[F] => implicitly[NodeValidator[BetweenNode[F]]].validate(field) + def validate[F <: CronField](node: Validatable[F])(using Enumerated[CronUnit[F]]): List[InvalidField] = node match { + case field: EachNode[F] => summon[NodeValidator[EachNode[F]]].validate(field) + case field: AnyNode[F] => summon[NodeValidator[AnyNode[F]]].validate(field) + case field: ConstNode[F] => summon[NodeValidator[ConstNode[F]]].validate(field) + case field: BetweenNode[F] => summon[NodeValidator[BetweenNode[F]]].validate(field) case field: SeveralNode[F] => implicitly[NodeValidator[SeveralNode[F]]].validate(field) case field: EveryNode[F] => implicitly[NodeValidator[EveryNode[F]]].validate(field) case field: FieldNode[F] => implicitly[NodeValidator[FieldNode[F]]].validate(field) diff --git a/modules/core/shared/src/main/scala-3/cron4s/validation/package.scala b/modules/core/shared/src/main/scala-3/cron4s/validation/package.scala new file mode 100644 index 00000000..c43f9156 --- /dev/null +++ b/modules/core/shared/src/main/scala-3/cron4s/validation/package.scala @@ -0,0 +1,67 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s + +import cats.data.NonEmptyList + +/** + * Created by alonsodomin on 30/08/2016. + */ +package object validation { + def validateCron(expr: CronExpr): Either[InvalidCron, CronExpr] = { + val dayFieldError = validateDayFields(expr) + val fieldErrors = expr.raw match { + case (seconds, minutes, hours, daysOfMonth, months, daysOfWeek) => + List( + ops.validate(seconds), + ops.validate(minutes), + ops.validate(hours), + ops.validate(daysOfMonth), + ops.validate(months), + ops.validate(daysOfWeek), + ).flatten + } + + val allErrors = + dayFieldError.fold[List[ValidationError]](fieldErrors)(_ :: fieldErrors) + + NonEmptyList + .fromList(allErrors) + .map(errs => Left(InvalidCron(errs))) + .getOrElse(Right(expr)) + } + + private def validateDayFields(expr: CronExpr) = { + val dayOfMonth = expr.field[CronField.DayOfMonth].toString + val dayOfWeek = expr.field[CronField.DayOfWeek].toString + + if (dayOfMonth == dayOfWeek) + Some( + InvalidFieldCombination( + s"Fields ${CronField.DayOfMonth} and ${CronField.DayOfWeek} can't both have the expression: $dayOfMonth" + ) + ) + else if ((dayOfMonth != "?" && dayOfWeek == "?") || (dayOfMonth == "?" && dayOfWeek != "?")) + None + else + Some( + InvalidFieldCombination( + s"Either ${CronField.DayOfMonth} and ${CronField.DayOfWeek} must have a ? expression" + ) + ) + } +} diff --git a/modules/core/shared/src/main/scala-3/shapelessCompat.scala b/modules/core/shared/src/main/scala-3/shapelessCompat.scala deleted file mode 100644 index da6ea180..00000000 --- a/modules/core/shared/src/main/scala-3/shapelessCompat.scala +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2017 Antonio Alonso Dominguez - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package cron4s -import scala.compiletime.summonInline - diff --git a/modules/core/shared/src/main/scala/cron4s/datetime/DateTimeCron.scala b/modules/core/shared/src/main/scala/cron4s/datetime/DateTimeCron.scala index 6d03c2c2..88e114aa 100644 --- a/modules/core/shared/src/main/scala/cron4s/datetime/DateTimeCron.scala +++ b/modules/core/shared/src/main/scala/cron4s/datetime/DateTimeCron.scala @@ -82,7 +82,7 @@ private[datetime] final class FullCron extends DateTimeCron[CronExpr] { } def ranges(expr: CronExpr): Map[CronField, IndexedSeq[Int]] = - supportedFields.zip(expr.raw.map(ops.range).toList).toMap + supportedFields.zip(applyRange(expr)).toMap @inline val supportedFields: List[CronField] = CronField.All @@ -105,7 +105,7 @@ private[datetime] final class TimeCron extends DateTimeCron[TimeCronExpr] { } def ranges(expr: TimeCronExpr): Map[CronField, IndexedSeq[Int]] = - supportedFields.zip(expr.raw.map(ops.range).toList).toMap + supportedFields.zip(applyRange(expr)).toMap @inline val supportedFields: List[CronField] = @@ -129,7 +129,7 @@ private[datetime] final class DateCron extends DateTimeCron[DateCronExpr] { } def ranges(expr: DateCronExpr): Map[CronField, IndexedSeq[Int]] = - supportedFields.zip(expr.raw.map(ops.range).toList).toMap + supportedFields.zip(applyRange(expr)).toMap @inline val supportedFields: List[CronField] = From 176bb0365abfcdd3e45e7adadd27decba6dab428 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 14:20:12 +0900 Subject: [PATCH 03/16] test: try compiling tests in scala 3 --- build.sbt | 12 ++++- .../cron4s/datetime/PredicateReducer.scala | 3 +- .../scala-3/cron4s/expr/FieldSelector.scala | 50 ++++++++----------- .../cron4s/testkit/CronDateTimeTestKit.scala | 2 +- .../testkit/gen/ArbitraryBetweenNode.scala | 23 ++++++--- .../testkit/gen/ArbitraryConstNode.scala | 17 ++++--- .../testkit/gen/ArbitraryCronUnits.scala | 24 ++++++--- .../testkit/gen/ArbitraryEachNode.scala | 17 ++++--- .../testkit/gen/ArbitraryEveryNode.scala | 18 ++++--- .../testkit/gen/ArbitrarySeveralNode.scala | 23 ++++++--- .../cron4s/testkit/gen/ArbitratyAnyNode.scala | 17 ++++--- .../cron4s/testkit/gen/NodeGenerators.scala | 3 +- 12 files changed, 130 insertions(+), 79 deletions(-) diff --git a/build.sbt b/build.sbt index 9a02f7b7..3e5c3d7d 100644 --- a/build.sbt +++ b/build.sbt @@ -46,7 +46,7 @@ val commonSettings = Def.settings( "-unchecked", "-deprecation", "-explaintypes", - "-Xfatal-warnings", + // "-Xfatal-warnings", "-language:postfixOps", "-language:implicitConversions", "-language:higherKinds", @@ -300,7 +300,7 @@ lazy val docs = project lazy val core = (crossProject(JSPlatform, JVMPlatform) in file("modules/core")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin, MimaPlugin) .settings( - scalaVersion := "3.3.0", + scalaVersion := "3.3.0", crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0"), name := "core", moduleName := "cron4s-core" @@ -329,6 +329,10 @@ lazy val testkit = .jvmSettings(commonJvmSettings) .jvmSettings(consoleSettings) .jvmSettings(mimaSettings("testkit")) + .settings( + scalaVersion := "3.3.0", + crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0") + ) .dependsOn(core) lazy val tests = (crossProject(JSPlatform, JVMPlatform) in file("tests")) @@ -344,6 +348,10 @@ lazy val tests = (crossProject(JSPlatform, JVMPlatform) in file("tests")) .jsSettings(Dependencies.testsJS) .jvmSettings(commonJvmSettings) .jvmSettings(Dependencies.testsJVM) + .settings( + scalaVersion := "3.3.0", + crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0") + ) .dependsOn(testkit % Test) lazy val bench = (project in file("bench")) diff --git a/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala b/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala index ab645b38..983be318 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala @@ -44,8 +44,7 @@ private[datetime] final class PredicateReducer[DateTime](DT: IsDateTime[DateTime } import CronField._ t match { - case t: SecondsNode => - predicateFor(Second, t) + case t: SecondsNode => predicateFor(Second, t) case t: MinutesNode => predicateFor(Minute, t) case t: HoursNode => predicateFor(Hour, t) case t: DaysOfMonthNode => predicateFor(DayOfMonth, t) diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala index 4acf878c..21ed10bd 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala @@ -30,7 +30,7 @@ sealed trait FieldSelector[A, F <: CronField] { type Out[X <: CronField] //protected implicit def hlistSelect: Lazy[Selector[Raw, Out[F]]] - + val hlistSelect: (expr:Raw) => Out[F] def selectFrom(expr: A): Out[F] } @@ -41,68 +41,57 @@ object FieldSelector { implicit val SecondsFromCronExpr: FieldSelector[CronExpr, Second] = new FullCronFieldNodeSelector[Second] { - // implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Second]]] = - // Selector[RawCronExpr, FieldNode[Second]] + val hlistSelect = (expr:RawCronExpr) => expr._1 } implicit val SecondsFromTimeExpr: FieldSelector[TimeCronExpr, Second] = new TimeCronFieldNodeSelector[Second] { - //implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Second]]] = - // Selector[RawTimeCronExpr, FieldNode[Second]] + val hlistSelect = (expr:RawTimeCronExpr) => expr._1 } implicit val MinutesFromCronExpr: FieldSelector[CronExpr, Minute] = new FullCronFieldNodeSelector[Minute] { - //implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Minute]]] = - // Selector[RawCronExpr, FieldNode[Minute]] + val hlistSelect = (expr:RawCronExpr) => expr._2 } implicit val MinutesFromTimeExpr: FieldSelector[TimeCronExpr, Minute] = new TimeCronFieldNodeSelector[Minute] { - //implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Minute]]] = - // Selector[RawTimeCronExpr, FieldNode[Minute]] + val hlistSelect = (expr:RawTimeCronExpr) => expr._2 + } implicit val HoursFromCronExpr: FieldSelector[CronExpr, Hour] = new FullCronFieldNodeSelector[Hour] { - //implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Hour]]] = - // Selector[RawCronExpr, FieldNode[Hour]] + val hlistSelect = (expr:RawCronExpr) => expr._3 } implicit val HoursFromTimeExpr: FieldSelector[TimeCronExpr, Hour] = new TimeCronFieldNodeSelector[Hour] { - //implicit val hlistSelect: Lazy[Selector[RawTimeCronExpr, FieldNode[Hour]]] = - // Selector[RawTimeCronExpr, FieldNode[Hour]] + val hlistSelect = (expr:RawTimeCronExpr) => expr._3 } implicit val DayOfMonthFromCronExpr: FieldSelector[CronExpr, DayOfMonth] = new FullCronFieldNodeWithAnySelector[DayOfMonth] { - // implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNodeWithAny[DayOfMonth]]] = - // Selector[RawCronExpr, FieldNodeWithAny[DayOfMonth]] + val hlistSelect = (expr:RawCronExpr) => expr._4 } implicit val DayOfMonthFromDateExpr: FieldSelector[DateCronExpr, DayOfMonth] = new DateCronFieldNodeWithAnySelector[DayOfMonth] { - // implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNodeWithAny[DayOfMonth]]] = - // Selector[RawDateCronExpr, FieldNodeWithAny[DayOfMonth]] + val hlistSelect = (expr:RawDateCronExpr) => expr._1 } implicit val MonthFromCronExpr: FieldSelector[CronExpr, Month] = new FullCronFieldNodeSelector[Month] { - // implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNode[Month]]] = - // Selector[RawCronExpr, FieldNode[Month]] + val hlistSelect = (expr:RawCronExpr) => expr._5 } implicit val MonthFromDateExpr: FieldSelector[DateCronExpr, Month] = new DateCronFieldNodeSelector[Month] { - // implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNode[Month]]] = - // Selector[RawDateCronExpr, FieldNode[Month]] + val hlistSelect = (expr:RawDateCronExpr) => expr._2 } implicit val DayOfWeekFromCronExpr: FieldSelector[CronExpr, DayOfWeek] = new FullCronFieldNodeWithAnySelector[DayOfWeek] { - // implicit val hlistSelect: Lazy[Selector[RawCronExpr, FieldNodeWithAny[DayOfWeek]]] = - // Selector[RawCronExpr, FieldNodeWithAny[DayOfWeek]] + val hlistSelect = (expr:RawCronExpr) => expr._6 } implicit val DayOfWeekFromDateExpr: FieldSelector[DateCronExpr, DayOfWeek] = new DateCronFieldNodeWithAnySelector[DayOfWeek] { - // implicit val hlistSelect: Lazy[Selector[RawDateCronExpr, FieldNodeWithAny[DayOfWeek]]] = - // Selector[RawDateCronExpr, FieldNodeWithAny[DayOfWeek]] + val hlistSelect = (expr:RawDateCronExpr) => expr._3 } // Base classes adding type refinements for the typeclass instances @@ -114,19 +103,20 @@ object FieldSelector { extends FieldNodeSelector[CronExpr, F] { type Raw = RawCronExpr - def selectFrom(expr: CronExpr): FieldNode[F] = ??? + def selectFrom(expr: CronExpr): FieldNode[F] = hlistSelect(expr.raw) } private[this] abstract class TimeCronFieldNodeSelector[F <: CronField] extends FieldNodeSelector[TimeCronExpr, F] { type Raw = RawTimeCronExpr - def selectFrom(expr: TimeCronExpr): FieldNode[F] = ??? + def selectFrom(expr: TimeCronExpr): FieldNode[F] = hlistSelect(expr.raw) } private[this] abstract class DateCronFieldNodeSelector[F <: CronField] extends FieldNodeSelector[DateCronExpr, F] { type Raw = RawDateCronExpr - def selectFrom(expr: DateCronExpr): FieldNode[F] = ??? + def selectFrom(expr: DateCronExpr): FieldNode[F] = hlistSelect(expr.raw) + } private[this] abstract class FieldNodeWithAnySelector[A, F <: CronField] @@ -137,12 +127,12 @@ object FieldSelector { extends FieldNodeWithAnySelector[CronExpr, F] { type Raw = RawCronExpr - def selectFrom(expr: CronExpr): FieldNodeWithAny[F] = ??? + def selectFrom(expr: CronExpr): FieldNodeWithAny[F] = hlistSelect(expr.raw) } private[this] abstract class DateCronFieldNodeWithAnySelector[F <: CronField] extends FieldNodeWithAnySelector[DateCronExpr, F] { type Raw = RawDateCronExpr - def selectFrom(expr: DateCronExpr): FieldNodeWithAny[F] = ??? + def selectFrom(expr: DateCronExpr): FieldNodeWithAny[F] = hlistSelect(expr.raw) } } diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/CronDateTimeTestKit.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/CronDateTimeTestKit.scala index feb781c9..929fc86d 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/CronDateTimeTestKit.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/CronDateTimeTestKit.scala @@ -21,7 +21,7 @@ import cats.implicits._ import cron4s.Cron import cron4s.datetime.IsDateTime - +import cron4s.syntax.all.toDateTimeCronOps import org.scalatest.flatspec.AnyFlatSpec /** diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryBetweenNode.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryBetweenNode.scala index 40497f5e..f52a8dc8 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryBetweenNode.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryBetweenNode.scala @@ -16,6 +16,7 @@ package cron4s.testkit.gen +import cron4s.expr.BetweenNode import cron4s.CronField._ import org.scalacheck.Arbitrary @@ -24,10 +25,20 @@ import org.scalacheck.Arbitrary * Created by alonsodomin on 28/08/2016. */ trait ArbitraryBetweenNode extends NodeGenerators { - implicit lazy val arbitraryBetweenSecond = Arbitrary(betweenGen[Second]) - implicit lazy val arbitraryBetweenMinute = Arbitrary(betweenGen[Minute]) - implicit lazy val arbitraryBetweenHour = Arbitrary(betweenGen[Hour]) - implicit lazy val arbitraryBetweenDayOfMonth = Arbitrary(betweenGen[DayOfMonth]) - implicit lazy val arbitraryBetweenMonth = Arbitrary(betweenGen[Month]) - implicit lazy val arbitraryBetweenDayOfWeek = Arbitrary(betweenGen[DayOfWeek]) + implicit lazy val arbitraryBetweenSecond: Arbitrary[BetweenNode[Second]] = Arbitrary( + betweenGen[Second] + ) + implicit lazy val arbitraryBetweenMinute: Arbitrary[BetweenNode[Minute]] = Arbitrary( + betweenGen[Minute] + ) + implicit lazy val arbitraryBetweenHour: Arbitrary[BetweenNode[Hour]] = Arbitrary(betweenGen[Hour]) + implicit lazy val arbitraryBetweenDayOfMonth: Arbitrary[BetweenNode[DayOfMonth]] = Arbitrary( + betweenGen[DayOfMonth] + ) + implicit lazy val arbitraryBetweenMonth: Arbitrary[BetweenNode[Month]] = Arbitrary( + betweenGen[Month] + ) + implicit lazy val arbitraryBetweenDayOfWeek: Arbitrary[BetweenNode[DayOfWeek]] = Arbitrary( + betweenGen[DayOfWeek] + ) } diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryConstNode.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryConstNode.scala index ada3640e..93a60702 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryConstNode.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryConstNode.scala @@ -17,6 +17,7 @@ package cron4s.testkit.gen import cron4s.CronField._ +import cron4s.expr.ConstNode import org.scalacheck.Arbitrary @@ -24,10 +25,14 @@ import org.scalacheck.Arbitrary * Created by alonsodomin on 28/08/2016. */ trait ArbitraryConstNode extends NodeGenerators { - implicit lazy val arbitraryConstSecond = Arbitrary(constGen[Second]) - implicit lazy val arbitraryConstMinute = Arbitrary(constGen[Minute]) - implicit lazy val arbitraryConstHour = Arbitrary(constGen[Hour]) - implicit lazy val arbitraryConstDayOfMonth = Arbitrary(constGen[DayOfMonth]) - implicit lazy val arbitraryConstMonth = Arbitrary(constGen[Month]) - implicit lazy val arbitraryConstDayOfWeek = Arbitrary(constGen[DayOfWeek]) + implicit lazy val arbitraryConstSecond: Arbitrary[ConstNode[Second]] = Arbitrary(constGen[Second]) + implicit lazy val arbitraryConstMinute: Arbitrary[ConstNode[Minute]] = Arbitrary(constGen[Minute]) + implicit lazy val arbitraryConstHour: Arbitrary[ConstNode[Hour]] = Arbitrary(constGen[Hour]) + implicit lazy val arbitraryConstDayOfMonth: Arbitrary[ConstNode[DayOfMonth]] = Arbitrary( + constGen[DayOfMonth] + ) + implicit lazy val arbitraryConstMonth: Arbitrary[ConstNode[Month]] = Arbitrary(constGen[Month]) + implicit lazy val arbitraryConstDayOfWeek: Arbitrary[ConstNode[DayOfWeek]] = Arbitrary( + constGen[DayOfWeek] + ) } diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryCronUnits.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryCronUnits.scala index f2f2187d..af2e6e5c 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryCronUnits.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryCronUnits.scala @@ -25,10 +25,22 @@ import org.scalacheck._ * Created by alonsodomin on 28/08/2016. */ trait ArbitraryCronUnits { - implicit lazy val arbitrarySecondsUnit = Arbitrary(Gen.const(CronUnit[Second])) - implicit lazy val arbitraryMinutesUnit = Arbitrary(Gen.const(CronUnit[Minute])) - implicit lazy val arbitraryHoursUnit = Arbitrary(Gen.const(CronUnit[Hour])) - implicit lazy val arbitraryDaysOfMonthUnit = Arbitrary(Gen.const(CronUnit[DayOfMonth])) - implicit lazy val arbitraryMonthsUnit = Arbitrary(Gen.const(CronUnit[Month])) - implicit lazy val arbitraryDaysOfWeekUnit = Arbitrary(Gen.const(CronUnit[DayOfWeek])) + implicit lazy val arbitrarySecondsUnit: Arbitrary[CronUnit[Second]] = Arbitrary( + Gen.const(CronUnit[Second]) + ) + implicit lazy val arbitraryMinutesUnit: Arbitrary[CronUnit[Minute]] = Arbitrary( + Gen.const(CronUnit[Minute]) + ) + implicit lazy val arbitraryHoursUnit: Arbitrary[CronUnit[Hour]] = Arbitrary( + Gen.const(CronUnit[Hour]) + ) + implicit lazy val arbitraryDaysOfMonthUnit: Arbitrary[CronUnit[DayOfMonth]] = Arbitrary( + Gen.const(CronUnit[DayOfMonth]) + ) + implicit lazy val arbitraryMonthsUnit: Arbitrary[CronUnit[Month]] = Arbitrary( + Gen.const(CronUnit[Month]) + ) + implicit lazy val arbitraryDaysOfWeekUnit: Arbitrary[CronUnit[DayOfWeek]] = Arbitrary( + Gen.const(CronUnit[DayOfWeek]) + ) } diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryEachNode.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryEachNode.scala index dc10f5e8..2cd22cfa 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryEachNode.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryEachNode.scala @@ -17,6 +17,7 @@ package cron4s.testkit.gen import cron4s.CronField._ +import cron4s.expr.EachNode import org.scalacheck.Arbitrary @@ -24,10 +25,14 @@ import org.scalacheck.Arbitrary * Created by alonsodomin on 28/08/2016. */ trait ArbitraryEachNode extends NodeGenerators { - implicit lazy val arbitraryEachSecond = Arbitrary(eachGen[Second]) - implicit lazy val arbitraryEachMinute = Arbitrary(eachGen[Minute]) - implicit lazy val arbitraryEachHour = Arbitrary(eachGen[Hour]) - implicit lazy val arbitraryEachDayOfMonth = Arbitrary(eachGen[DayOfMonth]) - implicit lazy val arbitraryEachMonth = Arbitrary(eachGen[Month]) - implicit lazy val arbitraryEachDayOfWeek = Arbitrary(eachGen[DayOfWeek]) + implicit lazy val arbitraryEachSecond: Arbitrary[EachNode[Second]] = Arbitrary(eachGen[Second]) + implicit lazy val arbitraryEachMinute: Arbitrary[EachNode[Minute]] = Arbitrary(eachGen[Minute]) + implicit lazy val arbitraryEachHour: Arbitrary[EachNode[Hour]] = Arbitrary(eachGen[Hour]) + implicit lazy val arbitraryEachDayOfMonth: Arbitrary[EachNode[DayOfMonth]] = Arbitrary( + eachGen[DayOfMonth] + ) + implicit lazy val arbitraryEachMonth: Arbitrary[EachNode[Month]] = Arbitrary(eachGen[Month]) + implicit lazy val arbitraryEachDayOfWeek: Arbitrary[EachNode[DayOfWeek]] = Arbitrary( + eachGen[DayOfWeek] + ) } diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryEveryNode.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryEveryNode.scala index 099f0c41..64cfe8e6 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryEveryNode.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitraryEveryNode.scala @@ -17,17 +17,21 @@ package cron4s.testkit.gen import cron4s.CronField._ - +import cron4s.expr.EveryNode import org.scalacheck.Arbitrary /** * Created by alonsodomin on 28/08/2016. */ trait ArbitraryEveryNode extends NodeGenerators { - implicit lazy val arbitraryEverySecond = Arbitrary(everyGen[Second]) - implicit lazy val arbitraryEveryMinute = Arbitrary(everyGen[Minute]) - implicit lazy val arbitraryEveryHour = Arbitrary(everyGen[Hour]) - implicit lazy val arbitraryEveryDayOfMonth = Arbitrary(everyGen[DayOfMonth]) - implicit lazy val arbitraryEveryMonth = Arbitrary(everyGen[Month]) - implicit lazy val arbitraryEveryDayOfWeek = Arbitrary(everyGen[DayOfWeek]) + implicit lazy val arbitraryEverySecond: Arbitrary[EveryNode[Second]] = Arbitrary(everyGen[Second]) + implicit lazy val arbitraryEveryMinute: Arbitrary[EveryNode[Minute]] = Arbitrary(everyGen[Minute]) + implicit lazy val arbitraryEveryHour: Arbitrary[EveryNode[Hour]] = Arbitrary(everyGen[Hour]) + implicit lazy val arbitraryEveryDayOfMonth: Arbitrary[EveryNode[DayOfMonth]] = Arbitrary( + everyGen[DayOfMonth] + ) + implicit lazy val arbitraryEveryMonth: Arbitrary[EveryNode[Month]] = Arbitrary(everyGen[Month]) + implicit lazy val arbitraryEveryDayOfWeek: Arbitrary[EveryNode[DayOfWeek]] = Arbitrary( + everyGen[DayOfWeek] + ) } diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitrarySeveralNode.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitrarySeveralNode.scala index c9c68857..e58a5727 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitrarySeveralNode.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitrarySeveralNode.scala @@ -17,6 +17,7 @@ package cron4s.testkit.gen import cron4s.CronField._ +import cron4s.expr.SeveralNode import org.scalacheck.Arbitrary @@ -24,10 +25,20 @@ import org.scalacheck.Arbitrary * Created by alonsodomin on 28/08/2016. */ trait ArbitrarySeveralNode extends NodeGenerators { - implicit lazy val arbitrarySeveralSecond = Arbitrary(severalGen[Second]) - implicit lazy val arbitrarySeveralMinute = Arbitrary(severalGen[Minute]) - implicit lazy val arbitrarySeveralHour = Arbitrary(severalGen[Hour]) - implicit lazy val arbitrarySeveralDayOfMonth = Arbitrary(severalGen[DayOfMonth]) - implicit lazy val arbitrarySeveralMonth = Arbitrary(severalGen[Month]) - implicit lazy val arbitrarySeveralDayOfWeek = Arbitrary(severalGen[DayOfWeek]) + implicit lazy val arbitrarySeveralSecond: Arbitrary[SeveralNode[Second]] = Arbitrary( + severalGen[Second] + ) + implicit lazy val arbitrarySeveralMinute: Arbitrary[SeveralNode[Minute]] = Arbitrary( + severalGen[Minute] + ) + implicit lazy val arbitrarySeveralHour: Arbitrary[SeveralNode[Hour]] = Arbitrary(severalGen[Hour]) + implicit lazy val arbitrarySeveralDayOfMonth: Arbitrary[SeveralNode[DayOfMonth]] = Arbitrary( + severalGen[DayOfMonth] + ) + implicit lazy val arbitrarySeveralMonth: Arbitrary[SeveralNode[Month]] = Arbitrary( + severalGen[Month] + ) + implicit lazy val arbitrarySeveralDayOfWeek: Arbitrary[SeveralNode[DayOfWeek]] = Arbitrary( + severalGen[DayOfWeek] + ) } diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitratyAnyNode.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitratyAnyNode.scala index f3e086f7..43198032 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitratyAnyNode.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/ArbitratyAnyNode.scala @@ -15,6 +15,7 @@ */ package cron4s.testkit.gen +import cron4s.expr.AnyNode import cron4s.CronField._ @@ -24,10 +25,14 @@ import org.scalacheck.Arbitrary * Created by alonsodomin on 10/02/2017. */ trait ArbitratyAnyNode extends NodeGenerators { - implicit lazy val arbitraryAnySecond = Arbitrary(anyGen[Second]) - implicit lazy val arbitraryAnyMinute = Arbitrary(anyGen[Minute]) - implicit lazy val arbitraryAnyHour = Arbitrary(anyGen[Hour]) - implicit lazy val arbitraryAnyDayOfMonth = Arbitrary(anyGen[DayOfMonth]) - implicit lazy val arbitraryAnyMonth = Arbitrary(anyGen[Month]) - implicit lazy val arbitraryAnyDayOfWeek = Arbitrary(anyGen[DayOfWeek]) + implicit lazy val arbitraryAnySecond: Arbitrary[AnyNode[Second]] = Arbitrary(anyGen[Second]) + implicit lazy val arbitraryAnyMinute: Arbitrary[AnyNode[Minute]] = Arbitrary(anyGen[Minute]) + implicit lazy val arbitraryAnyHour: Arbitrary[AnyNode[Hour]] = Arbitrary(anyGen[Hour]) + implicit lazy val arbitraryAnyDayOfMonth: Arbitrary[AnyNode[DayOfMonth]] = Arbitrary( + anyGen[DayOfMonth] + ) + implicit lazy val arbitraryAnyMonth: Arbitrary[AnyNode[Month]] = Arbitrary(anyGen[Month]) + implicit lazy val arbitraryAnyDayOfWeek: Arbitrary[AnyNode[DayOfWeek]] = Arbitrary( + anyGen[DayOfWeek] + ) } diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/NodeGenerators.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/NodeGenerators.scala index 7a05718b..5c7aadc6 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/NodeGenerators.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/NodeGenerators.scala @@ -19,8 +19,9 @@ package cron4s.testkit.gen import cron4s.{CronField, CronUnit} import cron4s.expr._ import cron4s.base._ - import org.scalacheck._ +import cron4s.syntax.all.toExprOps +import cron4s.syntax.all.toEnumeratedOps /** * Created by alonsodomin on 28/08/2016. From 5a2bddbfd98874237c1587f019b112558b44b6b3 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 14:36:38 +0900 Subject: [PATCH 04/16] update: address more compiler errrors --- .../cron4s/testkit/gen/CronGenerators.scala | 6 +- .../cron4s/testkit/gen/CronGenerators.scala | 63 +++++++++++++++++++ .../scala/cron4s/base/PredicateSpec.scala | 4 +- 3 files changed, 68 insertions(+), 5 deletions(-) rename modules/testkit/shared/src/main/{scala => scala-2}/cron4s/testkit/gen/CronGenerators.scala (92%) create mode 100644 modules/testkit/shared/src/main/scala-3/cron4s/testkit/gen/CronGenerators.scala diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/CronGenerators.scala b/modules/testkit/shared/src/main/scala-2/cron4s/testkit/gen/CronGenerators.scala similarity index 92% rename from modules/testkit/shared/src/main/scala/cron4s/testkit/gen/CronGenerators.scala rename to modules/testkit/shared/src/main/scala-2/cron4s/testkit/gen/CronGenerators.scala index 76bddf4c..25ea6dba 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/gen/CronGenerators.scala +++ b/modules/testkit/shared/src/main/scala-2/cron4s/testkit/gen/CronGenerators.scala @@ -35,7 +35,7 @@ trait CronGenerators extends NodeGenerators { case _ => anyGen[CronField.DayOfWeek].map(any2FieldWithAny) } - private[this] val fullCronGen = for { + private[this] val fullCronGen: Gen[CronExpr] = for { seconds <- nodeGen[CronField.Second] minutes <- nodeGen[CronField.Minute] hours <- nodeGen[CronField.Hour] @@ -44,13 +44,13 @@ trait CronGenerators extends NodeGenerators { daysOfWeek <- chooseDaysOfWeek(daysOfMonth) } yield CronExpr(seconds, minutes, hours, daysOfMonth, months, daysOfWeek) - private[this] val timeCronGen = for { + private[this] val timeCronGen: Gen[TimeCronExpr] = for { seconds <- nodeGen[CronField.Second] minutes <- nodeGen[CronField.Minute] hours <- nodeGen[CronField.Hour] } yield TimeCronExpr(seconds, minutes, hours) - private[this] val dateCronGen = for { + private[this] val dateCronGen: Gen[DateCronExpr] = for { daysOfMonth <- nodeWithAnyGen[CronField.DayOfMonth] months <- nodeGen[CronField.Month] daysOfWeek <- chooseDaysOfWeek(daysOfMonth) diff --git a/modules/testkit/shared/src/main/scala-3/cron4s/testkit/gen/CronGenerators.scala b/modules/testkit/shared/src/main/scala-3/cron4s/testkit/gen/CronGenerators.scala new file mode 100644 index 00000000..48076888 --- /dev/null +++ b/modules/testkit/shared/src/main/scala-3/cron4s/testkit/gen/CronGenerators.scala @@ -0,0 +1,63 @@ +/* + * Copyright 2017 Antonio Alonso Dominguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cron4s.testkit.gen + +import cron4s.CronField +import cron4s.expr._ + +import org.scalacheck._ + +/** + * Created by alonsodomin on 29/01/2017. + */ +trait CronGenerators extends NodeGenerators { + private[this] def chooseDaysOfWeek( + daysOfMonth: DaysOfMonthNode + ): Gen[DaysOfWeekNode] = + daysOfMonth.raw match { + case a: AnyNode[_] => nodeGen[CronField.DayOfWeek].map(field2FieldWithAny) // any + case _ => anyGen[CronField.DayOfWeek].map(any2FieldWithAny) + } + + private[this] val fullCronGen: Gen[CronExpr] = for { + seconds <- nodeGen[CronField.Second] + minutes <- nodeGen[CronField.Minute] + hours <- nodeGen[CronField.Hour] + daysOfMonth <- nodeWithAnyGen[CronField.DayOfMonth] + months <- nodeGen[CronField.Month] + daysOfWeek <- chooseDaysOfWeek(daysOfMonth) + } yield CronExpr(seconds, minutes, hours, daysOfMonth, months, daysOfWeek) + + private[this] val timeCronGen: Gen[TimeCronExpr] = for { + seconds <- nodeGen[CronField.Second] + minutes <- nodeGen[CronField.Minute] + hours <- nodeGen[CronField.Hour] + } yield TimeCronExpr(seconds, minutes, hours) + + private[this] val dateCronGen: Gen[DateCronExpr] = for { + daysOfMonth <- nodeWithAnyGen[CronField.DayOfMonth] + months <- nodeGen[CronField.Month] + daysOfWeek <- chooseDaysOfWeek(daysOfMonth) + } yield DateCronExpr(daysOfMonth, months, daysOfWeek) + + implicit lazy val arbitraryFullCron: Arbitrary[CronExpr] = + Arbitrary(fullCronGen) + implicit lazy val arbitraryTimeCron: Arbitrary[TimeCronExpr] = + Arbitrary(timeCronGen) + implicit lazy val arbitraryDateCron: Arbitrary[DateCronExpr] = + Arbitrary(dateCronGen) +} diff --git a/tests/shared/src/test/scala/cron4s/base/PredicateSpec.scala b/tests/shared/src/test/scala/cron4s/base/PredicateSpec.scala index 8c79251c..93b16886 100644 --- a/tests/shared/src/test/scala/cron4s/base/PredicateSpec.scala +++ b/tests/shared/src/test/scala/cron4s/base/PredicateSpec.scala @@ -31,11 +31,11 @@ import org.scalacheck._ class PredicateSpec extends Cron4sLawSuite { import Arbitrary._ - implicit lazy val arbitraryPredicate = Arbitrary[Predicate[Int]] { + implicit lazy val arbitraryPredicate: Arbitrary[Predicate[Int]] = Arbitrary[Predicate[Int]] { for { x <- arbitrary[Int] } yield equalTo(x) } - implicit val predicateEq = Eq.by[Predicate[Int], Boolean](_.apply(0)) + implicit val predicateEq: Eq[Predicate[Int]] = Eq.by[Predicate[Int], Boolean](_.apply(0)) checkAll("ContravariantPredicate", ContravariantTests[Predicate].contravariant[Int, Int, Int]) checkAll( From c94dca9f3fd3d61ea60f879654f262a220c6eb3a Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 15:11:32 +0900 Subject: [PATCH 05/16] update tests to compile --- .../core/shared/src/main/scala-3/cron4s/expr/package.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala index e6bf1f47..b7e98111 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala @@ -30,6 +30,11 @@ package object expr { private[expr] type RawEnumerableNode[F <: CronField] = ConstNode[F] | BetweenNode[F] + extension [F<:CronField](t: RawEnumerableNode[F]) { + def select[T]: Option[T] = t match + case t: T => Some(t) + case _ => None + } private[expr] type RawDivisibleNode[F <: CronField] = EachNode[F] | BetweenNode[F] | SeveralNode[F] From a547e164239962bd30dc9a18daaafef9777f161a Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 15:37:40 +0900 Subject: [PATCH 06/16] update: explicit implicit val types --- .../main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala | 4 ++-- .../main/scala/cron4s/testkit/laws/DateTimeNodeLaws.scala | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala index ad075d7d..94babad2 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala @@ -76,7 +76,7 @@ object DateTimeCronLaws { TC0: DateTimeCron[E] ): DateTimeCronLaws[E, DateTime] = new DateTimeCronLaws[E, DateTime] { - implicit val DT = dt0 - implicit val TC = TC0 + implicit def DT: IsDateTime[DateTime] = dt0 + implicit def TC: DateTimeCron[E] = TC0 } } diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeNodeLaws.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeNodeLaws.scala index f778683a..32a0bcb5 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeNodeLaws.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeNodeLaws.scala @@ -51,8 +51,8 @@ object DateTimeNodeLaws { TC0: DateTimeNode[E, F] ): DateTimeNodeLaws[E, F, DateTime] = new DateTimeNodeLaws[E, F, DateTime] { - implicit val DT = dt0 - implicit val expr = expr0 - implicit val TC = TC0 + implicit def DT: IsDateTime[DateTime] = dt0 + implicit def expr: FieldExpr[E, F] = expr0 + implicit def TC: DateTimeNode[E, F] = TC0 } } From e0d32401fc576b3426709bbd0b93f8e6f07c9786 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 16:06:31 +0900 Subject: [PATCH 07/16] fix: avoid unsafe type match --- .../cron4s/datetime/PredicateReducer.scala | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala b/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala index 983be318..3669dd10 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/datetime/PredicateReducer.scala @@ -18,10 +18,9 @@ package cron4s.datetime import cats.MonoidK import cats.instances.list._ - import cron4s.CronField -import cron4s.expr._ import cron4s.base._ +import cron4s.expr._ import cron4s.syntax.predicate._ /** @@ -30,34 +29,44 @@ import cron4s.syntax.predicate._ private[datetime] final class PredicateReducer[DateTime](DT: IsDateTime[DateTime])(implicit M: MonoidK[Predicate] ) { + private def predicateFor[N[_ <: CronField], F <: CronField](field: F, node: N[F])(implicit + expr: FieldExpr[N, F] + ): Predicate[DateTime] = + Predicate { dt => + DT.get(dt, field) + .map(expr.matches(node)) + .getOrElse(M.empty[DateTime](dt)) + } type Predicatable = SecondsNode | MinutesNode | HoursNode | DaysOfMonthNode | MonthsNode | DaysOfWeekNode - def asPredicate(t: Predicatable): Predicate[DateTime] = { - def predicateFor[N[_ <: CronField], F <: CronField](field: F, node: N[F])(implicit - expr: FieldExpr[N, F] - ): Predicate[DateTime] = - Predicate { dt => - DT.get(dt, field) - .map(expr.matches(node)) - .getOrElse(M.empty[DateTime](dt)) - } - import CronField._ - t match { - case t: SecondsNode => predicateFor(Second, t) - case t: MinutesNode => predicateFor(Minute, t) - case t: HoursNode => predicateFor(Hour, t) - case t: DaysOfMonthNode => predicateFor(DayOfMonth, t) - case t: MonthsNode => predicateFor(Month, t) - case t: DaysOfWeekNode => predicateFor(DayOfWeek, t) - } - } - type FromRawable = CronExpr | DateCronExpr | TimeCronExpr + import CronField._ def fromRaw(t: FromRawable): List[Predicate[DateTime]] = t match { - case t: CronExpr => t.raw.toList.map(asPredicate) - case t: DateCronExpr => t.raw.toList.map(asPredicate) - case t: TimeCronExpr => t.raw.toList.map(asPredicate) + case t: CronExpr => t.raw match + case (seconds, minutes, hours, daysOfMonth, months, daysOfWeek) => + List( + predicateFor(Second, seconds), + predicateFor(Minute, minutes), + predicateFor(Hour, hours), + predicateFor(DayOfMonth, daysOfMonth), + predicateFor(Month, months), + predicateFor(DayOfWeek, daysOfWeek), + ) + case t: DateCronExpr => t.raw match + case (daysOfMonth, months, daysOfWeek) => + List( + predicateFor(DayOfMonth, daysOfMonth), + predicateFor(Month, months), + predicateFor(DayOfWeek, daysOfWeek), + ) + case t: TimeCronExpr => t.raw match + case (seconds, minutes, hours) => + List( + predicateFor(Second, seconds), + predicateFor(Minute, minutes), + predicateFor(Hour, hours), + ) } def run(cron: AnyCron): Predicate[DateTime] = From 0e290125c95dcfb86435ccb2f8cb32b1ad4db615 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 16:36:30 +0900 Subject: [PATCH 08/16] update: drop unused dependencies and build configs --- build.sbt | 1 - project/Dependencies.scala | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index 3e5c3d7d..d24bfc9b 100644 --- a/build.sbt +++ b/build.sbt @@ -343,7 +343,6 @@ lazy val tests = (crossProject(JSPlatform, JVMPlatform) in file("tests")) ) .settings(commonSettings) .settings(noPublishSettings) - .settings(Dependencies.tests) .jsSettings(commonJsSettings) .jsSettings(Dependencies.testsJS) .jvmSettings(commonJvmSettings) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index daaef08f..2f57b58d 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -37,8 +37,8 @@ object Dependencies { "org.scala-lang.modules" %%% "scala-parser-combinators" % version.parserc ), libraryDependencies ++= (if (scalaVersion.value.startsWith("2.")) - Seq("com.chuusai" %%% "shapeless" % version.shapeless) - else Seq("org.typelevel" %% "shapeless3-deriving" % "3.0.1")) + Seq("com.chuusai" %%% "shapeless" % version.shapeless) + else Seq.empty) ) lazy val coreJS = Def.settings { @@ -58,10 +58,6 @@ object Dependencies { ) } - lazy val tests = Def.settings { - libraryDependencies ++= Seq() - } - lazy val testsJS = Def.settings { libraryDependencies += "io.github.cquiroz" %%% "scala-java-time" % version.scalaJavaTime } From 03eb1412f52fa8f0f411eafe00ae985aa79d59d4 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 16:36:54 +0900 Subject: [PATCH 09/16] fix: avoid unsafe type checking --- .../scala-3/cron4s/datetime/Stepper.scala | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala b/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala index 5bd44cf2..0ff7669b 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala @@ -93,16 +93,6 @@ private[datetime] final class Stepper[DateTime](DT: IsDateTime[DateTime]) { newDateTime <- DT.plus(dt, carryOver * dir.sign, DateTimeUnit.Weeks) } yield (identityReset, newDateTime, s.copy(amount = 0)) - type Steppable = - SecondsNode | MinutesNode | HoursNode | DaysOfMonthNode | MonthsNode | DaysOfWeekNode - def stepPerNode(step: StepST, node: Steppable): StepST = node match { - case node: SecondsNode => stepNode(step, node) - case node: MinutesNode => stepNode(step, node) - case node: HoursNode => stepNode(step, node) - case node: DaysOfMonthNode => stepNode(step, node) - case node: MonthsNode => stepOverMonth(step, node) - case node: DaysOfWeekNode => stepOverDayOfWeek(step, node) - } type FoldInternalExprable = CronExpr | DateCronExpr | TimeCronExpr def foldInternalExpr( @@ -111,18 +101,34 @@ private[datetime] final class Stepper[DateTime](DT: IsDateTime[DateTime]) { ): Option[(ResetPrevFn, DateTime, Step)] = expr match { case expr: CronExpr => val dateWithoutDOW = expr.datePart.raw.take(2) + val (dom,mt) = expr.datePart.raw.take(2) val (_, _, daysOfWeekNode) = expr.datePart.raw for { - st @ (resetTime, _, _) <- - expr.timePart.raw.toList.foldLeft(stepSt)(stepPerNode) - (_, dt, step) <- dateWithoutDOW.toList.foldLeft(Some(st): StepST)(stepPerNode) + st @ (resetTime, _, _) <- foldInternalExpr(stepSt,expr.timePart) + (_, dt, step) <- + List( + (step:StepST) => stepNode(step,dom), + (step:StepST) => stepOverMonth(step,mt), + ).foldLeft(Some(st): StepST){case (step,f) => f(step)} result <- stepOverDayOfWeek(Some((resetTime, dt, step)), daysOfWeekNode) } yield result case expr: DateCronExpr => - expr.raw.toList.foldLeft(stepSt)(stepPerNode) + expr.raw match + case (daysOfMonth,month,daysOfWeek) => + List( + (step:StepST) => stepNode(step, daysOfMonth), + (step:StepST) => stepOverMonth(step,month), + (step:StepST) => stepOverDayOfWeek(step,daysOfWeek), + ).foldLeft(stepSt){case (step,f) => f(step)} case expr: TimeCronExpr => - expr.raw.toList.foldLeft(stepSt)(stepPerNode) + expr.raw match + case (seconds, minutes, hours) => + List( + (step:StepST) => stepNode(step, seconds), + (step:StepST) => stepNode(step, minutes), + (step:StepST) => stepNode(step, hours), + ).foldLeft(stepSt){case (step,f) => f(step)} } def run(cron: AnyCron, from: DateTime, step: Step): Option[DateTime] = { From 7ec7172a803b0ddb0ab51b05b2d2abca5b55c8ef Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 16:42:02 +0900 Subject: [PATCH 10/16] fix: format and ci check --- .github/workflows/ci.yml | 2 +- build.sbt | 3 --- .../main/scala-2/cron4s/expr/CronExpr.scala | 1 - .../src/main/scala-2/cron4s/expr/ops.scala | 1 + .../main/scala-2/cron4s/expr/wrappers.scala | 1 - project/GithubWorkflow.scala | 8 ++++-- project/plugins.sbt | 26 +++++++++---------- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 68026aff..598146dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.12.17] + scala: [2.12.18] java: [adopt-hotspot@8] runs-on: ${{ matrix.os }} steps: diff --git a/build.sbt b/build.sbt index d24bfc9b..d22ec508 100644 --- a/build.sbt +++ b/build.sbt @@ -300,7 +300,6 @@ lazy val docs = project lazy val core = (crossProject(JSPlatform, JVMPlatform) in file("modules/core")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin, MimaPlugin) .settings( - scalaVersion := "3.3.0", crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0"), name := "core", moduleName := "cron4s-core" @@ -330,7 +329,6 @@ lazy val testkit = .jvmSettings(consoleSettings) .jvmSettings(mimaSettings("testkit")) .settings( - scalaVersion := "3.3.0", crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0") ) .dependsOn(core) @@ -348,7 +346,6 @@ lazy val tests = (crossProject(JSPlatform, JVMPlatform) in file("tests")) .jvmSettings(commonJvmSettings) .jvmSettings(Dependencies.testsJVM) .settings( - scalaVersion := "3.3.0", crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0") ) .dependsOn(testkit % Test) diff --git a/modules/core/shared/src/main/scala-2/cron4s/expr/CronExpr.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/CronExpr.scala index 576d2db6..724c2fcb 100644 --- a/modules/core/shared/src/main/scala-2/cron4s/expr/CronExpr.scala +++ b/modules/core/shared/src/main/scala-2/cron4s/expr/CronExpr.scala @@ -16,7 +16,6 @@ package cron4s.expr - import shapeless._ /** diff --git a/modules/core/shared/src/main/scala-2/cron4s/expr/ops.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/ops.scala index 77fe2800..aaf05c37 100644 --- a/modules/core/shared/src/main/scala-2/cron4s/expr/ops.scala +++ b/modules/core/shared/src/main/scala-2/cron4s/expr/ops.scala @@ -23,6 +23,7 @@ import cron4s.CronField import shapeless._ import cron4s.base.Predicate import cron4s.CronUnit + /** * Created by alonsodomin on 17/12/2016. */ diff --git a/modules/core/shared/src/main/scala-2/cron4s/expr/wrappers.scala b/modules/core/shared/src/main/scala-2/cron4s/expr/wrappers.scala index 0213fcf0..ffcc0b75 100644 --- a/modules/core/shared/src/main/scala-2/cron4s/expr/wrappers.scala +++ b/modules/core/shared/src/main/scala-2/cron4s/expr/wrappers.scala @@ -32,7 +32,6 @@ final class FieldNode[F <: CronField](private[cron4s] val raw: RawFieldNode[F]) object FieldNode extends FieldNodeInstances - final class FieldNodeWithAny[F <: CronField](private[cron4s] val raw: RawFieldNodeWithAny[F]) extends AnyVal { override def toString: String = raw.fold(_root_.cron4s.expr.ops.show) diff --git a/project/GithubWorkflow.scala b/project/GithubWorkflow.scala index 248a52c4..221f684d 100644 --- a/project/GithubWorkflow.scala +++ b/project/GithubWorkflow.scala @@ -1,14 +1,18 @@ import sbtghactions.GenerativePlugin.autoImport._ object GithubWorkflow { - val DefaultJVM = JavaSpec(JavaSpec.Distribution.Adopt,"8") + val DefaultJVM = JavaSpec(JavaSpec.Distribution.Adopt, "8") val JvmCond = s"matrix.platform == 'jvm'" val JsCond = s"matrix.platform == 'js'" def settings = Seq( - githubWorkflowJavaVersions := Seq(DefaultJVM, JavaSpec(JavaSpec.Distribution.Adopt,"11"),JavaSpec(JavaSpec.Distribution.Temurin,"17")), + githubWorkflowJavaVersions := Seq( + DefaultJVM, + JavaSpec(JavaSpec.Distribution.Adopt, "11"), + JavaSpec(JavaSpec.Distribution.Temurin, "17") + ), githubWorkflowTargetBranches := Seq("master"), githubWorkflowTargetTags ++= Seq("v*"), githubWorkflowPublishTargetBranches := Seq(RefPredicate.StartsWith(Ref.Tag("v"))), diff --git a/project/plugins.sbt b/project/plugins.sbt index 03435dfa..d2d1922a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,15 +1,15 @@ resolvers += "Typesafe Repository" at "https://repo.typesafe.com/typesafe/releases/" -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.1") -addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.7") -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.4") -addSbtPlugin("com.47deg" % "sbt-microsites" % "1.4.3") -addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.2") -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.9.0") -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0") -addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") -addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.21.1") -addSbtPlugin("com.codecommit" % "sbt-github-actions" % "0.14.2") -libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.1") +addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.7") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.4") +addSbtPlugin("com.47deg" % "sbt-microsites" % "1.4.3") +addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.2") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.9.0") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0") +addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") +addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.21.1") +addSbtPlugin("com.codecommit" % "sbt-github-actions" % "0.14.2") +libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always From e15a7aa755fb6f3b272e4756ba6aa64af872a3e3 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 16:55:44 +0900 Subject: [PATCH 11/16] fix: address compiler warnings --- build.sbt | 2 +- modules/core/shared/src/main/scala-3/cron4s/expr/package.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index d22ec508..e27231c1 100644 --- a/build.sbt +++ b/build.sbt @@ -46,7 +46,7 @@ val commonSettings = Def.settings( "-unchecked", "-deprecation", "-explaintypes", - // "-Xfatal-warnings", + "-Xfatal-warnings", "-language:postfixOps", "-language:implicitConversions", "-language:higherKinds", diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala index b7e98111..5b09d26f 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/package.scala @@ -31,7 +31,7 @@ package object expr { private[expr] type RawEnumerableNode[F <: CronField] = ConstNode[F] | BetweenNode[F] extension [F<:CronField](t: RawEnumerableNode[F]) { - def select[T]: Option[T] = t match + private[cron4s] inline def select[T]: Option[T] = t match case t: T => Some(t) case _ => None } From 218a31d3843d80bc7464694ea4f8174da1eb977e Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 17:12:41 +0900 Subject: [PATCH 12/16] feat: cross build for scala 3 --- .github/workflows/ci.yml | 2 +- build.sbt | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 598146dd..a4be78bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.13.10, 2.12.17] + scala: [2.13.10, 2.12.17, 3.3.0] java: - adopt-hotspot@8 - adopt-hotspot@11 diff --git a/build.sbt b/build.sbt index e27231c1..337c527c 100644 --- a/build.sbt +++ b/build.sbt @@ -20,7 +20,7 @@ inThisBuild( organizationName := "Antonio Alonso Dominguez", description := "CRON expression parser for Scala", startYear := Some(2017), - crossScalaVersions := Seq("2.13.10", "2.12.17"), + crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0"), homepage := Some(url("https://github.com/alonsodomin/cron4s")), licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.txt")), scmInfo := Some( @@ -300,9 +300,8 @@ lazy val docs = project lazy val core = (crossProject(JSPlatform, JVMPlatform) in file("modules/core")) .enablePlugins(AutomateHeaderPlugin, ScalafmtPlugin, MimaPlugin) .settings( - crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0"), - name := "core", - moduleName := "cron4s-core" + name := "core", + moduleName := "cron4s-core" ) .settings(commonSettings) .settings(publishSettings) @@ -328,9 +327,6 @@ lazy val testkit = .jvmSettings(commonJvmSettings) .jvmSettings(consoleSettings) .jvmSettings(mimaSettings("testkit")) - .settings( - crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0") - ) .dependsOn(core) lazy val tests = (crossProject(JSPlatform, JVMPlatform) in file("tests")) @@ -345,9 +341,6 @@ lazy val tests = (crossProject(JSPlatform, JVMPlatform) in file("tests")) .jsSettings(Dependencies.testsJS) .jvmSettings(commonJvmSettings) .jvmSettings(Dependencies.testsJVM) - .settings( - crossScalaVersions := Seq("2.13.10", "2.12.17", "3.3.0") - ) .dependsOn(testkit % Test) lazy val bench = (project in file("bench")) From 48c90436e798c1aeb66acbdf6d7cb4017bff7089 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 17:21:06 +0900 Subject: [PATCH 13/16] fix: drop invalid scalac options for 3 --- build.sbt | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/build.sbt b/build.sbt index 337c527c..4eacdb41 100644 --- a/build.sbt +++ b/build.sbt @@ -89,16 +89,17 @@ lazy val commonJvmSettings = Seq( lazy val commonJsSettings = Seq( Global / scalaJSStage := FastOptStage, - scalacOptions += { - val tagOrHash = { - if (isSnapshot.value) - sys.process.Process("git rev-parse HEAD").lineStream_!.head - else version.value - } - val a = (LocalRootProject / baseDirectory).value.toURI.toString - val g = "https://raw.githubusercontent.com/alonsodomin/cron4s/" + tagOrHash - s"-P:scalajs:mapSourceURI:$a->$g/" - }, + scalacOptions ++= (if (scalaVersion.value.startsWith("2.")) Seq { + val tagOrHash = { + if (isSnapshot.value) + sys.process.Process("git rev-parse HEAD").lineStream_!.head + else version.value + } + val a = (LocalRootProject / baseDirectory).value.toURI.toString + val g = "https://raw.githubusercontent.com/alonsodomin/cron4s/" + tagOrHash + s"-P:scalajs:mapSourceURI:$a->$g/" + } + else Seq.empty), scalaJSLinkerConfig := scalaJSLinkerConfig.value.withModuleKind(ModuleKind.CommonJSModule), jsEnv := new org.scalajs.jsenv.nodejs.NodeJSEnv() ) From 6f7a99518b1e29a3cfcdc4a9c8bdf937a79313cb Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 17:40:19 +0900 Subject: [PATCH 14/16] tidy: enable lint and address linter warnings --- build.sbt | 17 +++++++++-------- .../scala/cron4s/lib/js/JsDateInstance.scala | 1 - .../main/scala-3/cron4s/datetime/Stepper.scala | 1 - .../src/main/scala-3/cron4s/expr/CronExpr.scala | 1 - .../src/main/scala-3/cron4s/expr/ops.scala | 4 +--- .../src/main/scala-3/cron4s/expr/wrappers.scala | 4 +--- .../shared/src/main/scala/cron4s/Cron.scala | 2 +- .../scala/cron4s/datetime/DateTimeNode.scala | 2 -- .../src/main/scala/cron4s/parsing/package.scala | 1 - .../cron4s/lib/momentjs/MomentJSInstance.scala | 1 - .../cron4s/testkit/laws/DateTimeCronLaws.scala | 1 - .../cron4s/testkit/laws/DateTimeNodeLaws.scala | 1 - 12 files changed, 12 insertions(+), 24 deletions(-) diff --git a/build.sbt b/build.sbt index 4eacdb41..1933607c 100644 --- a/build.sbt +++ b/build.sbt @@ -56,18 +56,19 @@ val commonSettings = Def.settings( Seq( "-Xlint:-unused,_" ), - } else { Seq.empty }), + } else + Seq( + "-Wunused:imports", + "-Wunused:locals", + "-Wunused:implicits", + "-Wunused:privates" + )), scalacOptions ++= { CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, n)) if n == 12 => Seq("-Ypartial-unification") - case _ => Nil - } - }, - scalacOptions ++= { - CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, n)) if n > 12 => + case Some((2,n)) if n > 12 => Seq("-Xlint:-byname-implicit", "-Ymacro-annotations") - case _ => Nil + case _ => Nil } }, Compile / console / scalacOptions := scalacOptions.value.filterNot( diff --git a/modules/core/js/src/main/scala/cron4s/lib/js/JsDateInstance.scala b/modules/core/js/src/main/scala/cron4s/lib/js/JsDateInstance.scala index 859d5ec5..859a0ceb 100644 --- a/modules/core/js/src/main/scala/cron4s/lib/js/JsDateInstance.scala +++ b/modules/core/js/src/main/scala/cron4s/lib/js/JsDateInstance.scala @@ -16,7 +16,6 @@ package cron4s.lib.js -import cats.syntax.either._ import cron4s.CronField import cron4s.datetime.{DateTimeError, DateTimeUnit, InvalidFieldValue, IsDateTime} diff --git a/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala b/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala index 0ff7669b..258b8e80 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/datetime/Stepper.scala @@ -100,7 +100,6 @@ private[datetime] final class Stepper[DateTime](DT: IsDateTime[DateTime]) { expr: FoldInternalExprable ): Option[(ResetPrevFn, DateTime, Step)] = expr match { case expr: CronExpr => - val dateWithoutDOW = expr.datePart.raw.take(2) val (dom,mt) = expr.datePart.raw.take(2) val (_, _, daysOfWeekNode) = expr.datePart.raw diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala index 6e6a9d2f..ffe3a749 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/CronExpr.scala @@ -15,7 +15,6 @@ */ package cron4s.expr -import cron4s.CronField import cats.syntax.all._ /** diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala index b069c883..d01e487a 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/ops.scala @@ -16,10 +16,8 @@ package cron4s.expr -import cats.Show -import cats.implicits.toShow import cron4s.CronField - +import cats.syntax.all.toShow import cron4s.base.Predicate import cron4s.CronUnit diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala index b2263466..62c19e33 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/wrappers.scala @@ -16,10 +16,8 @@ package cron4s.expr -import cats.{Eq, Show} import cats.syntax.all._ -import cron4s.{CronField, CronUnit} -import cron4s.base.Predicate +import cron4s.CronField /** * Created by alonsodomin on 23/01/2017. diff --git a/modules/core/shared/src/main/scala/cron4s/Cron.scala b/modules/core/shared/src/main/scala/cron4s/Cron.scala index 9035829c..b1d70e5a 100644 --- a/modules/core/shared/src/main/scala/cron4s/Cron.scala +++ b/modules/core/shared/src/main/scala/cron4s/Cron.scala @@ -17,7 +17,7 @@ package cron4s import scala.scalajs.js.annotation.JSExportTopLevel -import scala.util.{Failure, Success, Try} +import scala.util.Try /** * The entry point for parsing cron expressions diff --git a/modules/core/shared/src/main/scala/cron4s/datetime/DateTimeNode.scala b/modules/core/shared/src/main/scala/cron4s/datetime/DateTimeNode.scala index 82556392..ec3b89e7 100644 --- a/modules/core/shared/src/main/scala/cron4s/datetime/DateTimeNode.scala +++ b/modules/core/shared/src/main/scala/cron4s/datetime/DateTimeNode.scala @@ -31,7 +31,6 @@ trait DateTimeNode[E[_ <: CronField], F <: CronField] { */ def matchesIn[DateTime](expr: E[F], DT: IsDateTime[DateTime]): Predicate[DateTime] = Predicate { dt => - import cats.syntax.either._ val current = DT.get(dt, expr.unit.field) current.map(expr.matches).getOrElse(false) } @@ -71,7 +70,6 @@ trait DateTimeNode[E[_ <: CronField], F <: CronField] { expr: E[F], DT: IsDateTime[DateTime] )(dateTime: DateTime, step: Int): Option[DateTime] = { - import cats.syntax.either._ for { current <- DT.get(dateTime, expr.unit.field).toOption newValue <- expr.step(current, step).map(_._1) diff --git a/modules/core/shared/src/main/scala/cron4s/parsing/package.scala b/modules/core/shared/src/main/scala/cron4s/parsing/package.scala index 9efc340f..e310240c 100644 --- a/modules/core/shared/src/main/scala/cron4s/parsing/package.scala +++ b/modules/core/shared/src/main/scala/cron4s/parsing/package.scala @@ -16,7 +16,6 @@ package cron4s -import cats.syntax.either._ package object parsing { private[cron4s] def parse(e: String): Either[Error, CronExpr] = diff --git a/modules/momentjs/src/main/scala/cron4s/lib/momentjs/MomentJSInstance.scala b/modules/momentjs/src/main/scala/cron4s/lib/momentjs/MomentJSInstance.scala index c636c00e..858c6f54 100644 --- a/modules/momentjs/src/main/scala/cron4s/lib/momentjs/MomentJSInstance.scala +++ b/modules/momentjs/src/main/scala/cron4s/lib/momentjs/MomentJSInstance.scala @@ -16,7 +16,6 @@ package cron4s.lib.momentjs -import cats.syntax.either._ import cron4s.CronField import cron4s.datetime.{DateTimeError, DateTimeUnit, InvalidFieldValue, IsDateTime} diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala index 94babad2..aca61071 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala @@ -17,7 +17,6 @@ package cron4s.testkit.laws import cats.laws._ -import cats.syntax.either._ import cron4s.CronField import cron4s.datetime.{DateTimeCron, IsDateTime} diff --git a/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeNodeLaws.scala b/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeNodeLaws.scala index 32a0bcb5..1f7ae8c2 100644 --- a/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeNodeLaws.scala +++ b/modules/testkit/shared/src/main/scala/cron4s/testkit/laws/DateTimeNodeLaws.scala @@ -17,7 +17,6 @@ package cron4s.testkit.laws import cats.laws._ -import cats.syntax.either._ import cron4s.CronField import cron4s.datetime.{IsDateTime, DateTimeNode} From b44835149b44df1d13f4afbda73482cd81ae5ef8 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 17:46:21 +0900 Subject: [PATCH 15/16] chore: drop comment --- .../core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala b/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala index 21ed10bd..4c565c33 100644 --- a/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala +++ b/modules/core/shared/src/main/scala-3/cron4s/expr/FieldSelector.scala @@ -29,7 +29,6 @@ sealed trait FieldSelector[A, F <: CronField] { type Raw <: Tuple type Out[X <: CronField] - //protected implicit def hlistSelect: Lazy[Selector[Raw, Out[F]]] val hlistSelect: (expr:Raw) => Out[F] def selectFrom(expr: A): Out[F] } From af5b736190c62dd628295db449e6adcfa4bab1bd Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 2 Jul 2023 17:56:51 +0900 Subject: [PATCH 16/16] fix: address compiler warnings --- build.sbt | 4 ++-- .../core/js/src/main/scala/cron4s/lib/js/JsDateInstance.scala | 1 - .../core/shared/src/main/scala/cron4s/parsing/package.scala | 1 - .../src/main/scala/cron4s/lib/momentjs/MomentJSInstance.scala | 1 - tests/shared/src/test/scala/cron4s/CronSpec.scala | 3 +-- tests/shared/src/test/scala/cron4s/parsing/ParserSpec.scala | 2 -- 6 files changed, 3 insertions(+), 9 deletions(-) diff --git a/build.sbt b/build.sbt index 1933607c..e5b9ad6f 100644 --- a/build.sbt +++ b/build.sbt @@ -66,9 +66,9 @@ val commonSettings = Def.settings( scalacOptions ++= { CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, n)) if n == 12 => Seq("-Ypartial-unification") - case Some((2,n)) if n > 12 => + case Some((2, n)) if n > 12 => Seq("-Xlint:-byname-implicit", "-Ymacro-annotations") - case _ => Nil + case _ => Nil } }, Compile / console / scalacOptions := scalacOptions.value.filterNot( diff --git a/modules/core/js/src/main/scala/cron4s/lib/js/JsDateInstance.scala b/modules/core/js/src/main/scala/cron4s/lib/js/JsDateInstance.scala index 859a0ceb..8b8e89b9 100644 --- a/modules/core/js/src/main/scala/cron4s/lib/js/JsDateInstance.scala +++ b/modules/core/js/src/main/scala/cron4s/lib/js/JsDateInstance.scala @@ -16,7 +16,6 @@ package cron4s.lib.js - import cron4s.CronField import cron4s.datetime.{DateTimeError, DateTimeUnit, InvalidFieldValue, IsDateTime} diff --git a/modules/core/shared/src/main/scala/cron4s/parsing/package.scala b/modules/core/shared/src/main/scala/cron4s/parsing/package.scala index e310240c..c37fed45 100644 --- a/modules/core/shared/src/main/scala/cron4s/parsing/package.scala +++ b/modules/core/shared/src/main/scala/cron4s/parsing/package.scala @@ -16,7 +16,6 @@ package cron4s - package object parsing { private[cron4s] def parse(e: String): Either[Error, CronExpr] = for { diff --git a/modules/momentjs/src/main/scala/cron4s/lib/momentjs/MomentJSInstance.scala b/modules/momentjs/src/main/scala/cron4s/lib/momentjs/MomentJSInstance.scala index 858c6f54..2a4a9425 100644 --- a/modules/momentjs/src/main/scala/cron4s/lib/momentjs/MomentJSInstance.scala +++ b/modules/momentjs/src/main/scala/cron4s/lib/momentjs/MomentJSInstance.scala @@ -16,7 +16,6 @@ package cron4s.lib.momentjs - import cron4s.CronField import cron4s.datetime.{DateTimeError, DateTimeUnit, InvalidFieldValue, IsDateTime} diff --git a/tests/shared/src/test/scala/cron4s/CronSpec.scala b/tests/shared/src/test/scala/cron4s/CronSpec.scala index 7cfe03d7..659d6c48 100644 --- a/tests/shared/src/test/scala/cron4s/CronSpec.scala +++ b/tests/shared/src/test/scala/cron4s/CronSpec.scala @@ -19,7 +19,6 @@ package cron4s import cats.data.NonEmptyList import cron4s.expr._ -import cron4s.syntax.all._ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec @@ -94,7 +93,7 @@ class CronSpec extends AnyFlatSpec with Matchers { import CronSpec._ "Cron" should "not parse an invalid expression" in { - val expectedError = + val _ = InvalidFieldCombination("Fields DayOfMonth and DayOfWeek can't both have the expression: *") forAll(InvalidExprs) { (desc: String, expr: String, err: Error) => diff --git a/tests/shared/src/test/scala/cron4s/parsing/ParserSpec.scala b/tests/shared/src/test/scala/cron4s/parsing/ParserSpec.scala index 51918952..1052e9e4 100644 --- a/tests/shared/src/test/scala/cron4s/parsing/ParserSpec.scala +++ b/tests/shared/src/test/scala/cron4s/parsing/ParserSpec.scala @@ -17,8 +17,6 @@ package cron4s package parsing -import cats.syntax.either._ - import cron4s.expr._ import cron4s.testkit.Cron4sPropSpec import cron4s.testkit.gen.{ArbitraryEachNode, NodeGenerators}