Skip to content

Instantly share code, notes, and snippets.

@dgouyette
Created May 5, 2017 09:08
Show Gist options
  • Save dgouyette/2777b950dbf209acc5ef6881a821af41 to your computer and use it in GitHub Desktop.
Save dgouyette/2777b950dbf209acc5ef6881a821af41 to your computer and use it in GitHub Desktop.
import jto.validation._
sealed trait Color
object Color {
case object Red extends Color
case object Orange extends Color
case object Green extends Color
val colorR : Rule[String, Color] = Rule.fromMapping {
case "Red" => Valid(Red)
case "Orange" => Valid(Orange)
case "Green" => Valid(Green)
case other => Invalid(Seq(ValidationError("unhandled.value", other)))
}
}
Color.colorR.validate("aaa")//Invalid(List((/,List(ValidationError(List(unhandled.value),WrappedArray(aaa))))))
Color.colorR.validate("Red")//Valid(Red)
@jeantil
Copy link

jeantil commented May 5, 2017

sealed trait Color{
val name=toString
}

val colorR  : Rule[String, Color] = Rule.fromMapping {
    case Red.name => Valid(Red)
    case Orange.name => Valid(Orange)
    case Green.name => Valid(Green)
    case other => Invalid(Seq(ValidationError("unhandled.value", other)))
  }

et une petite macro scala meta our generer ca automatiquement ...

@jeantil
Copy link

jeantil commented May 5, 2017

could be based on something like

package scalaworld.macros

import scala.collection.immutable.Seq
import scala.meta._

class enum extends scala.annotation.StaticAnnotation {
  inline def apply(defn: Any): Any = meta {
    defn match {
      case Term.Block(
      Seq(t @ ClassOrTrait(mods, name), companion: Defn.Object)) if Hierarchy.isSealed(mods) =>
        val companionStatements = companion.templ.stats.getOrElse(Nil)
        val subTypes = companionStatements.collect {
          case t: Defn.Object if Hierarchy.inherits(name)(t) => t
        }
        val newStatements =
          Hierarchy.mkApply(name, subTypes) +: companionStatements
        val newCompanion =
          companion.copy(templ = companion.templ.copy(stats = Some(newStatements)))
        Term.Block(Seq(t, newCompanion))
    }
  }
}

object Hierarchy {
  def inherits(superType: Type.Name)(cls: Defn.Object): Boolean =
    cls.templ.parents.headOption.exists {
      case q"$parent()" => parent.syntax == superType.syntax
      case _            => false
    }
  def isSealed(mods: Seq[Mod]): Boolean = mods.exists(_.syntax == "sealed")
  def mkApply(name: Type.Name, subTypes: Seq[Defn.Object]):Stat={
    val cases: Seq[Case] = subTypes.map {
      case cls =>
        val name= cls.name.value
        val term = q"""Some(${Term.Name(name)})"""
        p"case ${Lit(cls.name.value)} => $term"
    }
    q"""
        def apply(s:String):Option[$name]=s match {
          ..case $cases
        }
    """
  }


}

@dgouyette
Copy link
Author

Thank you, I will look at this carefully

@dgouyette
Copy link
Author


import scala.meta._
import collection.immutable.Seq

object Hierarchy {
  def inherits(superType: Type.Name)(cls: Defn.Object): Boolean =
    cls.templ.parents.headOption.exists {
      case q"$parent()" => parent.syntax == superType.syntax
      case _ => false
    }
  def isSealed(mods: Seq[Mod]): Boolean = mods.exists(_.syntax == "sealed")

}

class Reader extends scala.annotation.StaticAnnotation {


  inline def apply(defn: Any): Any = meta {
    defn match {
      case Term.Block(Seq(t@ClassOrTrait(mods, name), companion: Defn.Object)) if Hierarchy.isSealed(mods) =>

        val oldTemplStats = companion.templ.stats.getOrElse(Nil)

        val caseObjectNames : Seq[scala.meta.Case] = oldTemplStats.collect {
          case t: Defn.Object =>  scala.meta.Case(
            Lit.String(t.name.value),
            None,
            q"""cats.data.Validated.Valid(${t.name}) """
          )
        }

        val plus =q"""implicit val rule : jto.validation.Rule[String,$name] = jto.validation.Rule.fromMapping {
             ..${caseObjectNames.map(n => q"{case $n  }" )}
          }"""

        val updatedCompanion = companion.copy(templ = companion.templ.copy(stats = Some(oldTemplStats:+plus)))

        val resul = Term.Block(Seq(t, updatedCompanion))
        println("------------")
        println(resul.syntax)
        println("------------")
        resul
      case _ => abort("Reader must annotate sealed trait")
    }
  }
}

object ClassOrTrait {
  def unapply(any: Defn): Option[(Seq[Mod], Type.Name)] = any match {
    case t: Defn.Class => Some((t.mods, t.name))
    case t: Defn.Trait => Some((t.mods, t.name))
    case _ => None
  }
}

@dgouyette
Copy link
Author

dgouyette commented May 9, 2017

Avec le code ci dessous, j'obtiens le code suivant :

  sealed trait Color
  object Color {
    case object Red extends Color()
    case object Orange extends Color()
    case object Green extends Color()
    implicit val rule: jto.validation.Rule[String, Color] = jto.validation.Rule.fromMapping {
      {
        case "Red" =>
          cats.data.Validated.Valid(Red)
      }
      {
        case "Orange" =>
          cats.data.Validated.Valid(Orange)
      }
      {
        case "Green" =>
          cats.data.Validated.Valid(Green)
      }
    }
  }
}

Si tu as une idée pour applatir les cases, je suis preneur

@dgouyette
Copy link
Author

It's work with this version :

package validation

import scala.meta._
import collection.immutable.Seq

object Hierarchy {
  def inherits(superType: Type.Name)(cls: Defn.Object): Boolean =
    cls.templ.parents.headOption.exists {
      case q"$parent()" => parent.syntax == superType.syntax
      case _ => false
    }
  def isSealed(mods: Seq[Mod]): Boolean = mods.exists(_.syntax == "sealed")

}

class Reader extends scala.annotation.StaticAnnotation {


  inline def apply(defn: Any): Any = meta {
    defn match {
      case Term.Block(Seq(t@ClassOrTrait(mods, name), companion: Defn.Object)) if Hierarchy.isSealed(mods) =>

        val oldTemplStats = companion.templ.stats.getOrElse(Nil)

        val cases = oldTemplStats.collect{
          case t: Defn.Object =>
            val name = t.name.value
            val term = q"""cats.data.Validated.Valid(${t.name}) """
            p"""case ${Lit.String(name)} => $term  """
        }

        val plus =
          q"""implicit val rule : jto.validation.Rule[String,$name] = jto.validation.Rule.fromMapping {
            ..case $cases
          }"""

        val updatedCompanion = companion.copy(templ = companion.templ.copy(stats = Some(oldTemplStats:+plus)))

        val resul = Term.Block(Seq(t, updatedCompanion))
        println("------------")
        println(resul.syntax)
        println("------------")
        resul
      case _ => abort("Reader must annotate sealed trait")
    }
  }
}

object ClassOrTrait {
  def unapply(any: Defn): Option[(Seq[Mod], Type.Name)] = any match {
    case t: Defn.Class => Some((t.mods, t.name))
    case t: Defn.Trait => Some((t.mods, t.name))
    case _ => None
  }
}

@jeantil
Copy link

jeantil commented May 9, 2017

ça vaudrait le coup de regarder dans contrib si il y a pas des trucs a réutiliser plutôt que de les redéfinir je pense en particulier a Class Or Trait ou a Hierarchy

genre https://github.com/scalameta/scalameta/blob/master/scalameta/contrib/shared/src/main/scala/scala/meta/contrib/TreeOps.scala#L58 pour hierarchy .. ?

@dgouyette
Copy link
Author

dgouyette commented May 10, 2017

Bonne idée. Maintenant que cela fonctionne, je vais essayer de simplifier mon code.
Merci pour l'info

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment