Skip to content

Instantly share code, notes, and snippets.

@wolfendale
Last active April 18, 2016 13:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wolfendale/e72d7b1c9cb0e4a9f8cb to your computer and use it in GitHub Desktop.
Save wolfendale/e72d7b1c9cb0e4a9f8cb to your computer and use it in GitHub Desktop.
Validation Shapeless
package wolfendale
trait Enum {
type Value <: Singleton
}
object Enum {
trait Aux[A <: Singleton] extends Enum { type Value = A }
}
package wolfendale
import play.api.data.mapping._
import shapeless.labelled.FieldType
import shapeless.{Path => _, _}
trait RuleInstances0 {
implicit def productR[I, A <: Product, H <: HList]
(implicit
gen: LabelledGeneric.Aux[A, H],
r: Lazy[Rule[I, H]]
): Rule[I, A] =
r.value fmap (gen.from)
implicit def coproductR[I, A, C <: Coproduct]
(implicit
gen: Generic.Aux[A, C],
r: Lazy[Rule[I, C]]
): Rule[I, A] =
r.value fmap (gen.from)
}
trait RuleInstances extends RuleInstances0 {
implicit def hnilR[I]: Rule[I, HNil] = Rule {
_ => Success(HNil)
}
implicit def cnilR[I]: Rule[I, CNil] = Rule {
_ => Failure(Seq(Path -> Seq(ValidationError("error.invalid"))))
}
implicit def hconsR[I, H, T <: HList]
(implicit
hR: Lazy[Rule[I, H]],
tR: Lazy[Rule[I, T]]
): Rule[I, H :: T] =
Rule[I, H :: T] { data =>
(hR.value.validate(data), tR.value.validate(data)) match {
case (Success(_), Failure(e)) => Failure(e)
case (Failure(e), Success(_)) => Failure(e)
case (Failure(h), Failure(t)) => Failure(h ++ t)
case (Success(h), Success(t)) => Success(h :: t)
}
}
implicit def cconsR[I, H <: Enum, T <: Coproduct]
(implicit
p: Path => RuleLike[I, String],
wV: Witness.Aux[H#Value],
hR: Lazy[Rule[I, H]],
tR: Lazy[Rule[I, T]]
): Rule[I, H :+: T] =
Rule[I, H :+: T] { data =>
(From[I] { __ =>
import play.api.data.mapping.GenericRules._
(__ \ "type").read(equalTo(wV.value.toString))
}.validate(data) match {
case Success(_) => hR.value fmap Inl.apply
case _ => tR.value fmap Inr.apply
}).validate(data)
}
implicit def fieldR[I, K, O]
(implicit
wk: Witness.Aux[K],
p: Lazy[Path => RuleLike[I, O]]
): Rule[I, FieldType[K, O]] =
From[I] {
import labelled._
val key = wk.value.toString.substring(1)
__ => (__ \ key).read[O](p.value) fmap (field[K](_))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment