Skip to content

Commit

Permalink
Add a compile time string interpolator (#126)
Browse files Browse the repository at this point in the history
* Add a compile time string interpolator

* Add a test for the string interpolator
  • Loading branch information
A. Alonso Dominguez committed Dec 14, 2018
1 parent c9871e1 commit 455cc21
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 4 deletions.
30 changes: 30 additions & 0 deletions core/shared/src/main/scala/cron4s/Interpolator.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

import cats.implicits._

import contextual._

import cron4s.expr.CronExpr

object CronInterpolator extends Verifier[CronExpr] {
def check(input: String) = Cron(input).leftMap {
case parseErr: ParseFailed => parseErr.position -> parseErr.getMessage
case other: Error => 0 -> other.getMessage
}
}
10 changes: 9 additions & 1 deletion core/shared/src/main/scala/cron4s/package.scala
Expand Up @@ -17,4 +17,12 @@
import cron4s.syntax.AllSyntax
import cron4s.expr.NodeConversions

package object cron4s extends AllSyntax with NodeConversions
import contextual._

package object cron4s extends AllSyntax with NodeConversions {

implicit class Cron4sStringContext(sc: StringContext) {
val cron = Prefix(CronInterpolator, sc)
}

}
22 changes: 22 additions & 0 deletions docs/src/main/tut/docs/index.md
Expand Up @@ -78,6 +78,28 @@ unless you are aware of the consequences (well, it also comes handy during a REP
Cron.unsafeParse("10-65 * * * * *")
```

#### Compile time expressions

Starting at `cron4s` 0.5.0, there is support for compile-time verified cron expressions, which gives an additional
guarantee that our expressions are well formed without the need for a runtime check. Compile-time cron expressions
are basically a string interpolated and prefixed with the `cron` word:

```tut
val compileTimeChecked = cron"10-35 2,4,6 * ? * *"
```

As you can see, the return time is not wrapped in an `Either`, the expressions has been verified at compile time and
therefore there is no need to handle an error case. If we input an invalid cron expression, our code won't compile:

```tut:fail
val compileFails = cron"10-65 * * * * *"
```

Now compare that error to the previous one returned by `Cron.unsafeParse`, which was a runtime check. As you can see,
the compile time error is more helpful but it has its limitations, being the most important one the fact that the
expression text needs to be resolvable at compile time, if for some reason you are reading your expression as an
input into your program, you should still fallback to the previous methods.

#### Validation errors

The CRON expression will be validated right after parsing. Any error found during this stage will be returned
Expand Down
1 change: 1 addition & 0 deletions docs/src/main/tut/docs/whats_new.md
Expand Up @@ -11,6 +11,7 @@ New features:

* [#125](https://github.com/alonsodomin/cron4s/pull/125): Add extension module for [decline](http://ben.kirw.in/decline/).
Find documentation about it in the [extensions](https://alonsodomin.github.io/cron4s/extensions.html) docs.
* [#126](https://github.com/alonsodomin/cron4s/pull/126): Add a compile-time string interpolator.

Version upgrades:

Expand Down
9 changes: 6 additions & 3 deletions project/Dependencies.scala
Expand Up @@ -23,6 +23,8 @@ object Dependencies {

val momentjs = "0.8.1"
val scalaJavaTime = "2.0.0-RC1"

val contextual = "1.1.0"
}

val macroParadise = compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
Expand All @@ -31,9 +33,10 @@ object Dependencies {

lazy val core = Def.settings {
libraryDependencies ++= compilerPlugins ++ Seq(
"com.chuusai" %%% "shapeless" % version.shapeless,
"org.typelevel" %%% "cats-core" % version.cats,
"com.lihaoyi" %%% "fastparse" % version.fastparse
"com.chuusai" %%% "shapeless" % version.shapeless,
"org.typelevel" %%% "cats-core" % version.cats,
"com.lihaoyi" %%% "fastparse" % version.fastparse,
"com.propensive" %%% "contextual" % version.contextual
)
}

Expand Down
6 changes: 6 additions & 0 deletions tests/shared/src/test/scala/cron4s/CronSpec.scala
Expand Up @@ -90,4 +90,10 @@ class CronSpec extends FlatSpec with Matchers {
Cron.unsafeParse(exprStr) shouldBe ValidExpr
}

it should "compile a valid expression" in {
val expr = cron"17-30,5 * 12 * * ?"

expr shouldBe ValidExpr
}

}

0 comments on commit 455cc21

Please sign in to comment.