app/models/CustomisedRole.scala (88 lines of code) (raw):
package models
import org.scanamo.DynamoFormat
import org.scanamo.TypeCoercionError
import org.scanamo.generic.auto.genericDerivedFormat
case class CustomisedRole(roleId: RoleId, variables: Map[String, ParamValue]) {
def variablesToString =
variables.map { case (k, v) => s"$k: $v" }.mkString("{ ", ", ", " }")
/** Render the variables for display in a form input box */
def variablesToFormInputText = {
if (variables.isEmpty) ""
else variables.map { case (k, v) => s"$k: ${v.quoted}" }.mkString(", ")
}
}
sealed trait ParamValue { def quoted: String }
case class SingleParamValue(param: String) extends ParamValue {
override def toString: String = param
val quoted =
if (param.forall(CustomisedRole.allowedUnquotedChars)) param
else s"'$param'"
}
case class ListParamValue(params: List[SingleParamValue]) extends ParamValue {
override def toString: String = s"[${params.mkString(", ")}]"
val quoted = s"[${params.map(_.quoted).mkString(", ")}]"
}
case class DictParamValue(params: Map[String, SingleParamValue])
extends ParamValue {
override def toString: String =
params.map { case (k, v) => s"$k: $v" }.mkString("{", ", ", "}")
val quoted: String =
params.map { case (k, v) => s"$k: ${v.quoted}" }.mkString("{", ", ", "}")
}
object ListParamValue {
def of(params: String*) = ListParamValue(params.map(SingleParamValue).toList)
}
object ParamValue {
implicit val format: DynamoFormat[ParamValue] =
DynamoFormat.xmap[ParamValue, String](
fastparse
.parse(_, CustomisedRole.paramValue(_))
.fold(
(_, _, _) =>
Left(
TypeCoercionError(
new RuntimeException("Unable to read ParamValue")
)
),
(pv, _) => Right(pv)
),
_.quoted
)
}
object CustomisedRole {
import fastparse._, ScalaWhitespace._
def key[T: P]: P[String] = P(CharsWhile(_ != ':').!)
def allowedUnquotedChars: Char => Boolean = c =>
c.isLetterOrDigit || c == '-' || c == '_' || c == '/' || c == '.'
def unquotedSingleValue[T: P]: P[SingleParamValue] =
P(CharPred(allowedUnquotedChars).rep(1).!).map(SingleParamValue)
def quotedSingleValue[T: P]: P[SingleParamValue] =
P("'" ~ CharsWhile(_ != '\'', 0).! ~ "'").map(SingleParamValue)
def singleValue[T: P]: P[SingleParamValue] = P(
unquotedSingleValue | quotedSingleValue
)
def multiValues[T: P]: P[ListParamValue] = P(
"[" ~/ singleValue.rep(sep = ",") ~ "]"
).map(params => ListParamValue(params.toList))
def dictValues[T: P]: P[DictParamValue] =
P("{" ~/ dictPair.rep(sep = ",") ~ "}").map { pairs =>
DictParamValue(pairs.toMap)
}
def dictPair[T: P]: P[(String, SingleParamValue)] = P(
key ~ ":" ~/ singleValue
)
def paramValue[T: P]: P[ParamValue] = P(
dictValues | multiValues | singleValue
)
def pair[T: P]: P[(String, ParamValue)] = P(key ~ ":" ~ paramValue)
def parameters[T: P]: P[Seq[(String, ParamValue)]] = P(
Start ~ pair.rep(sep = ",") ~ End
)
def formInputTextToVariables(
input: String
): Either[String, Map[String, ParamValue]] = {
fastparse.parse(input, parameters(_)) match {
case Parsed.Success(value, _) => Right(value.toMap)
case f: Parsed.Failure => Left(f.toString)
}
}
}