I'm creating a DSL where users have a given when then, where they pass a string with the action they want.
All those actions are controlled using some regular expressions. Now using shapeless or macros I would like to control what users compones using the DSL, and in case the string does not fit the string as we expect make the code don't compile.
Si for instance
Given("a good action") -> fine
When("wrong string action") -> compilation error
Can anybody please point me to a good blog or documentation of how to achieve this?
Regards
After the solution of Gabriele now I´m facing a design issue. This is my vaidator class
object DSLValidator {
import scala.language.experimental.macros
def Given(message: String): Any = macro checkActionImpl
def When(message: String): Any = macro checkActionImpl
def Then(message: String): Any = macro checkActionImpl
def And(message: String): Any = macro checkActionImpl
def checkActionImpl(c: blackbox.Context)(message: c.Tree): c.Tree = {
import c.universe._
def isValidAction(s: String): Boolean = checkMessage(s)
message match {
case _tree@Literal(Constant(s: String)) if isValidAction(s) => _tree
case _ => c.abort(c.enclosingPosition, "Invalid action for DSL. Check the allowed actions in RegexActions")
}
}
val PARAMS = "(.*)=(.*)"
val PAYLOAD_VALUE_REGEX = s"^Payload $PARAMS".r
def checkMessage(action: String): Boolean = {
action match {
case PAYLOAD_VALUE_REGEX(c, c1) => true
case "Make a request to server" => true
case _ => false
}
}
}
And if I invoke the Given/When/Then from another class with wrong arguments the compiler complain as we expect.
@Test
def TestDSL(): Unit = {
DSLValidator.Given("Make a request to wrong")--> not compiling
DSLValidator.When("Payload code=works") --> compiling
println("End")
}
But now thinking the whole idea of was not only to check the values on compilation time but also execute the logic of the Given/When/then
And I realize than when I use macros in a function there´s nothing else that we can do so.
def Given(message: String): Any = {
macro checkActionImpl
//My bussiness logic
}
is not compiling, so my question is, where can I put now the business logic and where the invocation of the macro function to continue working.
Also using a static class as bridge does not work and wont compile since the string message does not fit.
object Macros {
def Given(message: String) = {
DSLValidator.Given(message)
//Logic here
println("Given")
}
}
Macros.Given("Make a request to wrong")
Any idea?
Regards
I think shapeless cannot help you in this case, so a macro would be the way to go.
Here's a skeleton for a macro you can start from:
import scala.language.experimental.macros
def Given(message: String): /* the return type of Given */ = macro givenImpl
def givenImpl(c: Context)(message: c.Tree): c.Tree = {
import c.universe._
def isValid(s: String): Boolean = ??? // your validation logic here
message match {
case case t @ Literal(Constant(s: String)) if isValid(s) => t
case _ => c.abort(c.enclosingPosition, "Invalid message")
}
}
In order to fully understand this I can recommend you to read: