Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- scala> :pas
- // Entering paste mode (ctrl-D to finish)
- import shapeless._
- import shapeless.ops.hlist.LeftFolder
- trait Rule {
- type Value
- }
- object Rule {
- type Aux[T] = Rule { type Value = T }
- }
- implicit class RuleOps[R <: Rule]( rule: R )(
- implicit
- definition: Definition[R],
- show: Show[R]
- ) {
- def validate( value: R#Value ): Result[R#Value] = {
- definition( value, rule ) match {
- case true ⇒ Success( value )
- case false ⇒ Failure( value, Seq( show( value, rule ) ) )
- }
- }
- }
- /**
- * Type class that defines the actual validation logic
- */
- trait Definition[-R <: Rule] {
- def apply( value: R#Value, rule: R ): Boolean
- }
- /**
- * Type class that renders an error message for a failed rule validation
- */
- trait Show[-R <: Rule] {
- def apply( value: R#Value, rule: R ): String
- }
- /**
- * A Result is the outcome of a rule(s) validation
- */
- sealed trait Result[T] { def value: T }
- case class Success[T]( value: T ) extends Result[T]
- case class Failure[T]( value: T, messages: Seq[String] ) extends Result[T]
- /**
- * Validate a value against a list of rules
- */
- case class Validation[T, H <: HList]( rules: H )(
- implicit
- fold: LeftFolder.Aux[H, T, Validation.combine.type, Result[T]]
- ) {
- def validate( value: T ): Result[T] = {
- rules.foldLeft( value )( Validation.combine )
- }
- }
- object Validation {
- object combine extends Poly {
- /**
- * First fold element retrieves a value input and generates either a
- * Success or a Failure
- */
- implicit def head[R <: Rule]( implicit definition: Definition[R], show: Show[R] ) = {
- use( ( value: R#Value, rule: R ) ⇒ rule.validate( value ) )
- }
- /**
- * If the previous fold returned a Success, the next rule is validated
- */
- implicit def success[R <: Rule]( implicit definition: Definition[R], show: Show[R] ) = {
- use( ( rule: R, success: Success[R#Value] ) ⇒ head.apply( success.value, rule ) )
- }
- /**
- * If the previous fold returned a Failure, all succeeding folds will
- * return Failures as well
- *
- * When this case fails, the additional error messages are appended to
- * the input Failure. In case of a successful validation, the input
- * Failure is passed along.
- */
- implicit def failure[R <: Rule]( implicit definition: Definition[R], show: Show[R] ) = {
- use( ( rule: R, failure: Failure[R#Value] ) ⇒ {
- rule.validate( failure.value ) match {
- case Failure( _, messages ) ⇒
- ( lens[Failure[R#Value]] >> 'messages ).modify( failure )( _ ++ messages )
- case Success( _ ) ⇒ failure
- }
- } )
- }
- }
- }
- trait Email extends Rule {
- override type Value = String
- }
- object Email extends Email {
- implicit val dfn = new Definition[Email] {
- override def apply( value: String, rule: Email ) = false
- }
- implicit val show = new Show[Email] {
- override def apply( value: String, rule: Email ) = "error.email"
- }
- }
- // Exiting paste mode, now interpreting.
- import shapeless._
- import shapeless.ops.hlist.LeftFolder
- defined trait Rule
- defined object Rule
- defined class RuleOps
- defined trait Definition
- defined trait Show
- defined trait Result
- defined class Success
- defined class Failure
- defined class Validation
- defined object Validation
- defined trait Email
- defined object Email
- scala> Email.validate( "asdf" )
- res11: Result[Email.Value] = Failure(asdf,List(error.email))
- scala> Validation( Email :: HNil ).validate( "asdf" )
- res12: Result[String] = Failure(asdf,List(error.email))
- scala> Validation( Email :: Email :: HNil ).validate( "asdf" )
- <console>:86: error: could not find implicit value for parameter fold: shapeless.ops.hlist.LeftFolder.Aux[shapeless.::[Email.type,shapeless.::[Email.type,shapeless.HNil]],T,Validation.combine.type,Result[T]]
- Validation( Email :: Email :: HNil ).validate( "asdf" )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement