I want to provide a json schema (at compile time) of a case class, based on the differents types (Scala refined included)
object JsonSchema {
def jsonSchema[T]: String = macro impl[T]
def impl[T: c.WeakTypeTag](c: scala.reflect.macros.whitebox.Context): c.Expr[String] = {
import c.universe._
val r = weakTypeOf[T].decls.collect {
case m: MethodSymbol if m.isCaseAccessor =>
val typeArgs = m.info match {
case NullaryMethodType(v) => v.typeArgs
}
val supportedStringFormat = List("IPv4", "IPv6", "Uri")
typeArgs match {
case _type :: _predicate :: Nil if _type =:= typeOf[String] && supportedStringFormat.contains(_predicate.typeSymbol.name.toString()) => Json.obj(m.name.decodedName.toString -> Json.obj("type" -> "string", "format" -> _predicate.typeSymbol.name.toString().toLowerCase()))
case _type :: _predicate :: Nil if _type =:= typeOf[String] && _predicate =:= typeOf[NonEmpty] => Json.obj(m.name.decodedName.toString -> Json.obj("type" -> "string", "minLength" -> 1))
case _type :: _predicate :: Nil if _type =:= typeOf[String] && _predicate =:= typeOf[Size[_0]] => {
val size = _predicate.typeArgs match {
case h :: _ if h <:< typeOf[Nat._0] => 0
}
Json.obj(m.name.decodedName.toString-> Json.obj("type" -> "string", "minLength" -> size))
}
case _type :: _ if _type =:= typeOf[String] => Json.obj(m.name.decodedName.toString -> Json.obj("type" -> "string"))
case _type :: _predicate :: Nil if _type =:= typeOf[Int] && _predicate =:= typeOf[Positive] => Json.obj(m.name.decodedName.toString ->Json.obj("type" -> "int", "minValue" -> 1))
case _type :: _predicate :: Nil if _type =:= typeOf[List[String]] && _predicate =:= typeOf[NonEmpty] => Json.obj(m.name.decodedName.toString-> Json.obj("type" -> "array", "minLength" -> 1))
case List() => Json.obj(m.name.decodedName.toString ->Json.obj("type"-> m.info.typeSymbol.name.decodedName.toString.toLowerCase()))
case other => Json.obj("other"-> other.map(_.toString()).mkString)
}
}
val json = r.reduce(_ ++ _)
c.Expr[String](q"""${json.toString()}""")
}
}
I want to be able to pattern match for all shapeless natural :
typeOf[Size[_]]
instead of typeOf[Size[_0]]
But i have a compile error :
No TypeTag available for eu.timepit.refined.collection.Size[_]
[error] case _type :: _predicate :: Nil if _type =:= typeOf[String] && _predicate =:= typeOf[Size[_]] => {
and i want to get an int for size
val size = _predicate.typeArgs match {
case h :: _ if h <:< typeOf[Nat._0] => 0
}
Usage :
case class StringWithMinSize22(k: String Refined MinSize[_22])
"String with min size 22" must {
"return a schema with min size" in {
JsonSchema.jsonSchema[StringWithMinSize22] mustBe """{"k":{"type":"string","minLength":22}}"""
}
}
Try
But it would make sense to think if you can encode your logic in a type class rather than raw macros.