From 3eb6958a23730d12cc45f8951f9c0c9a36148ca6 Mon Sep 17 00:00:00 2001 From: Antonio Alonso Dominguez Date: Sat, 4 Feb 2017 15:21:20 +0000 Subject: [PATCH] Decouple Shows and FieldExpr typeclasses. --- .../src/main/scala/cron4s/CronField.scala | 10 ++++- .../main/scala/cron4s/expr/FieldExpr.scala | 4 +- .../src/main/scala/cron4s/expr/nodes.scala | 42 +++++++++++++------ .../src/main/scala/cron4s/expr/wrappers.scala | 22 ++++++---- .../discipline/DateTimeCronTests.scala | 3 +- .../testkit/laws/DateTimeCronLaws.scala | 6 ++- 6 files changed, 60 insertions(+), 27 deletions(-) diff --git a/core/shared/src/main/scala/cron4s/CronField.scala b/core/shared/src/main/scala/cron4s/CronField.scala index 5b897b75..b45281aa 100644 --- a/core/shared/src/main/scala/cron4s/CronField.scala +++ b/core/shared/src/main/scala/cron4s/CronField.scala @@ -16,13 +16,15 @@ package cron4s +import scalaz.Equal + /** * Each of the different fields supported in CRON expressions * * @author Antonio Alonso Dominguez */ sealed trait CronField extends Serializable -object CronField { +object CronField extends CronFieldInstances { sealed trait Second extends CronField case object Second extends Second @@ -45,3 +47,9 @@ object CronField { final val All: List[CronField] = List(Second, Minute, Hour, DayOfMonth, Month, DayOfWeek) } + +private[cron4s] trait CronFieldInstances { + + implicit val cronFieldEq: Equal[CronField] = Equal.equalA[CronField] + +} diff --git a/core/shared/src/main/scala/cron4s/expr/FieldExpr.scala b/core/shared/src/main/scala/cron4s/expr/FieldExpr.scala index d148fcf3..ef9d1b96 100644 --- a/core/shared/src/main/scala/cron4s/expr/FieldExpr.scala +++ b/core/shared/src/main/scala/cron4s/expr/FieldExpr.scala @@ -19,12 +19,10 @@ package cron4s.expr import cron4s.{CronField, CronUnit} import cron4s.base.{Enumerated, Predicate} -import scalaz.Show - /** * Created by alonsodomin on 25/08/2016. */ -trait FieldExpr[E[_ <: CronField], F <: CronField] extends Enumerated[E[F]] with Show[E[F]] { +trait FieldExpr[E[_ <: CronField], F <: CronField] extends Enumerated[E[F]] { def matches(e: E[F]): Predicate[Int] diff --git a/core/shared/src/main/scala/cron4s/expr/nodes.scala b/core/shared/src/main/scala/cron4s/expr/nodes.scala index b5ee4c33..b602fa1c 100644 --- a/core/shared/src/main/scala/cron4s/expr/nodes.scala +++ b/core/shared/src/main/scala/cron4s/expr/nodes.scala @@ -21,7 +21,7 @@ import cron4s.base._ import cron4s.syntax.field._ import cron4s.syntax.predicate._ -import scalaz.NonEmptyList +import scalaz.{NonEmptyList, Show} import scalaz.std.anyVal._ import scalaz.std.list._ import scalaz.syntax.show._ @@ -47,10 +47,15 @@ final case class EachNode[+F <: CronField](implicit val unit: CronUnit[F]) lazy val range: IndexedSeq[Int] = unit.range + override val toString = "*" + } object EachNode { + implicit def eachNodeShow[F <: CronField]: Show[EachNode[F]] = + Show.showFromToString[EachNode[F]] + implicit def eachNodeInstance[F <: CronField]: FieldExpr[EachNode, F] = new FieldExpr[EachNode, F] { def unit(node: EachNode[F]): CronUnit[F] = node.unit @@ -62,8 +67,6 @@ object EachNode { x >= min(node) && x <= max(node) } - override def shows(node: EachNode[F]): String = "*" - def range(node: EachNode[F]): IndexedSeq[Int] = node.range } @@ -76,10 +79,16 @@ final case class ConstNode[F <: CronField] lazy val range: IndexedSeq[Int] = Vector(value) + override lazy val toString: String = + textValue.getOrElse(value.toString) + } object ConstNode { + implicit def constNodeShow[F <: CronField]: Show[ConstNode[F]] = + Show.showFromToString[ConstNode[F]] + implicit def constNodeInstance[F <: CronField]: FieldExpr[ConstNode, F] = new FieldExpr[ConstNode, F] { def unit(node: ConstNode[F]): CronUnit[F] = node.unit @@ -92,8 +101,6 @@ object ConstNode { } def range(node: ConstNode[F]): IndexedSeq[Int] = node.range - - override def shows(node: ConstNode[F]): String = node.textValue.getOrElse(node.value.toString) } } @@ -109,10 +116,16 @@ final case class BetweenNode[F <: CronField] min to max } + override lazy val toString: String = + s"${begin.shows}-${end.shows}" + } object BetweenNode { + implicit def betweenNodeShow[F <: CronField]: Show[BetweenNode[F]] = + Show.showFromToString[BetweenNode[F]] + implicit def betweenNodeInstance[F <: CronField] (implicit elemExpr: FieldExpr[ConstNode, F]): FieldExpr[BetweenNode, F] = new FieldExpr[BetweenNode, F] { @@ -130,8 +143,6 @@ object BetweenNode { def range(node: BetweenNode[F]): IndexedSeq[Int] = node.range - override def shows(node: BetweenNode[F]): String = - s"${node.begin.shows}-${node.end.shows}" } } @@ -144,6 +155,9 @@ final case class SeveralNode[F <: CronField] lazy val range: IndexedSeq[Int] = values.list.toVector.view.flatMap(_.range).distinct.sorted.toIndexedSeq + override lazy val toString: String = + values.map(_.shows).list.toList.mkString(",") + } object SeveralNode { @@ -152,6 +166,9 @@ object SeveralNode { (implicit unit: CronUnit[F]): SeveralNode[F] = SeveralNode(NonEmptyList(head, tail: _*)) + implicit def severalNodeShow[F <: CronField]: Show[SeveralNode[F]] = + Show.showFromToString[SeveralNode[F]] + implicit def severalNodeInstance[F <: CronField] (implicit elemExpr: FieldExpr[EnumerableNode, F]): FieldExpr[SeveralNode, F] = new FieldExpr[SeveralNode, F] { @@ -165,8 +182,6 @@ object SeveralNode { def range(node: SeveralNode[F]): IndexedSeq[Int] = node.range - override def shows(node: SeveralNode[F]): String = - node.values.map(_.shows).list.toList.mkString(",") } } @@ -184,10 +199,16 @@ final case class EveryNode[F <: CronField] elements.toVector } + override lazy val toString: String = + s"${base.shows}/$freq" + } object EveryNode { + implicit def everyNodeShow[F <: CronField]: Show[EveryNode[F]] = + Show.showFromToString[EveryNode[F]] + implicit def everyNodeInstance[F <: CronField] (implicit baseExpr: FieldExpr[DivisibleNode, F]): FieldExpr[EveryNode, F] = new FieldExpr[EveryNode, F] { @@ -202,9 +223,6 @@ object EveryNode { def range(node: EveryNode[F]): IndexedSeq[Int] = node.range - override def shows(node: EveryNode[F]): String = - s"${node.base.shows}/${node.freq}" - } } diff --git a/core/shared/src/main/scala/cron4s/expr/wrappers.scala b/core/shared/src/main/scala/cron4s/expr/wrappers.scala index 1cf6dc31..66f5d7af 100644 --- a/core/shared/src/main/scala/cron4s/expr/wrappers.scala +++ b/core/shared/src/main/scala/cron4s/expr/wrappers.scala @@ -21,6 +21,8 @@ import cron4s.base.Predicate import shapeless._ +import scalaz.Show + /** * Created by alonsodomin on 23/01/2017. */ @@ -31,6 +33,10 @@ final class FieldNode[F <: CronField](private[cron4s] val raw: RawFieldNode[F]) } object FieldNode { + + implicit def fieldNodeShow[F <: CronField]: Show[FieldNode[F]] = + Show.shows(_.raw.fold(ops.show)) + implicit def fieldNodeInstance[F <: CronField]: FieldExpr[FieldNode, F] = new FieldExpr[FieldNode, F] { def matches(node: FieldNode[F]): Predicate[Int] = node.raw.fold(ops.matches) @@ -50,9 +56,6 @@ object FieldNode { def unit(node: FieldNode[F]): CronUnit[F] = node.raw.fold(ops.unit) - - override def shows(node: FieldNode[F]): String = - node.raw.fold(ops.show) } } @@ -64,6 +67,9 @@ final class EnumerableNode[F <: CronField](val raw: RawEnumerableNode[F]) extend object EnumerableNode { + implicit def enumerableNodeShow[F <: CronField]: Show[EnumerableNode[F]] = + Show.shows(_.raw.fold(ops.show)) + implicit def enumerableNodeInstance[F <: CronField]: FieldExpr[EnumerableNode, F] = new FieldExpr[EnumerableNode, F] { def matches(node: EnumerableNode[F]): Predicate[Int] = @@ -83,9 +89,6 @@ object EnumerableNode { def unit(node: EnumerableNode[F]): CronUnit[F] = node.raw.fold(ops.unit) - - override def shows(node: EnumerableNode[F]): String = - node.raw.fold(ops.show) } } @@ -97,6 +100,10 @@ final class DivisibleNode[F <: CronField](val raw: RawDivisibleNode[F]) extends } object DivisibleNode { + + implicit def divisibleNodeShow[F <: CronField]: Show[DivisibleNode[F]] = + Show.shows(_.raw.fold(ops.show)) + implicit def divisibleNodeInstance[F <: CronField]: FieldExpr[DivisibleNode, F] = new FieldExpr[DivisibleNode, F] { def matches(node: DivisibleNode[F]): Predicate[Int] = @@ -115,8 +122,5 @@ object DivisibleNode { def unit(node: DivisibleNode[F]): CronUnit[F] = node.raw.fold(ops.unit) - - override def shows(node: DivisibleNode[F]): String = - node.raw.fold(ops.show) } } diff --git a/testkit/src/main/scala/cron4s/testkit/discipline/DateTimeCronTests.scala b/testkit/src/main/scala/cron4s/testkit/discipline/DateTimeCronTests.scala index 654b4132..54010397 100644 --- a/testkit/src/main/scala/cron4s/testkit/discipline/DateTimeCronTests.scala +++ b/testkit/src/main/scala/cron4s/testkit/discipline/DateTimeCronTests.scala @@ -43,7 +43,8 @@ trait DateTimeCronTests[E, DateTime] extends Laws { "matchAll" -> forAll(laws.matchAll _), "matchAny" -> forAll(laws.matchAny _), "forwards" -> forAll(laws.forwards _), - "backwards" -> forAll(laws.backwards _) + "backwards" -> forAll(laws.backwards _), + "supportedFields" -> forAll(laws.supportedFieldsEquality _) ) } diff --git a/testkit/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala b/testkit/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala index 9d0fe660..9b29409c 100644 --- a/testkit/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala +++ b/testkit/src/main/scala/cron4s/testkit/laws/DateTimeCronLaws.scala @@ -16,7 +16,8 @@ package cron4s.testkit.laws -import cron4s.datetime.{IsDateTime, DateTimeCron} +import cron4s.CronField +import cron4s.datetime.{DateTimeCron, IsDateTime} import cron4s.testkit._ import cron4s.syntax.cron._ @@ -71,6 +72,9 @@ trait DateTimeCronLaws[E, DateTime] { def backwards(e: E, from: DateTime): IsEqual[Option[DateTime]] = e.prev(from) <-> e.step(from, -1) + def supportedFieldsEquality(e: E): IsEqual[List[CronField]] = + supportedFields[E] <-> e.supportedFields + } object DateTimeCronLaws {