Advertisement
Guest User

Untitled

a guest
Sep 3rd, 2015
185
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scala 4.13 KB | None | 0 0
  1. scala> :pas
  2. // Entering paste mode (ctrl-D to finish)
  3.  
  4. import shapeless._
  5. import shapeless.ops.hlist.LeftFolder
  6.  
  7. trait Rule {
  8.     type Value
  9. }
  10.  
  11. object Rule {
  12.     type Aux[T] = Rule { type Value = T }
  13. }
  14.  
  15. implicit class RuleOps[R <: Rule]( rule: R )(
  16.     implicit
  17.     definition: Definition[R],
  18.     show:       Show[R]
  19. ) {
  20.     def validate( value: R#Value ): Result[R#Value] = {
  21.         definition( value, rule ) match {
  22.             case true  ⇒ Success( value )
  23.             case false ⇒ Failure( value, Seq( show( value, rule ) ) )
  24.         }
  25.     }
  26. }
  27.  
  28. /**
  29.  * Type class that defines the actual validation logic
  30.  */
  31. trait Definition[-R <: Rule] {
  32.     def apply( value: R#Value, rule: R ): Boolean
  33. }
  34.  
  35. /**
  36.  * Type class that renders an error message for a failed rule validation
  37.  */
  38. trait Show[-R <: Rule] {
  39.     def apply( value: R#Value, rule: R ): String
  40. }
  41.  
  42. /**
  43.  * A Result is the outcome of a rule(s) validation
  44.  */
  45. sealed trait Result[T] { def value: T }
  46. case class Success[T]( value: T ) extends Result[T]
  47. case class Failure[T]( value: T, messages: Seq[String] ) extends Result[T]
  48.  
  49. /**
  50.  * Validate a value against a list of rules
  51.  */
  52. case class Validation[T, H <: HList]( rules: H )(
  53.     implicit
  54.     fold: LeftFolder.Aux[H, T, Validation.combine.type, Result[T]]
  55. ) {
  56.     def validate( value: T ): Result[T] = {
  57.         rules.foldLeft( value )( Validation.combine )
  58.     }
  59. }
  60.  
  61. object Validation {
  62.     object combine extends Poly {
  63.         /**
  64.          * First fold element retrieves a value input and generates either a
  65.          * Success or a Failure
  66.          */
  67.         implicit def head[R <: Rule]( implicit definition: Definition[R], show: Show[R] ) = {
  68.             use( ( value: R#Value, rule: R ) ⇒ rule.validate( value ) )
  69.         }
  70.  
  71.         /**
  72.          * If the previous fold returned a Success, the next rule is validated
  73.          */
  74.         implicit def success[R <: Rule]( implicit definition: Definition[R], show: Show[R] ) = {
  75.             use( ( rule: R, success: Success[R#Value] ) ⇒ head.apply( success.value, rule ) )
  76.         }
  77.  
  78.         /**
  79.          * If the previous fold returned a Failure, all succeeding folds will
  80.          * return Failures as well
  81.          *
  82.          * When this case fails, the additional error messages are appended to
  83.          * the input Failure. In case of a successful validation, the input
  84.          * Failure is passed along.
  85.          */
  86.         implicit def failure[R <: Rule]( implicit definition: Definition[R], show: Show[R] ) = {
  87.             use( ( rule: R, failure: Failure[R#Value] ){
  88.                 rule.validate( failure.value ) match {
  89.                     case Failure( _, messages )
  90.                         ( lens[Failure[R#Value]] >> 'messages ).modify( failure )( _ ++ messages )
  91.                     case Success( _ ) ⇒ failure
  92.                 }
  93.             } )
  94.         }
  95.     }
  96. }
  97.  
  98. trait Email extends Rule {
  99.     override type Value = String
  100. }
  101.  
  102. object Email extends Email {
  103.     implicit val dfn = new Definition[Email] {
  104.         override def apply( value: String, rule: Email ) = false
  105.     }
  106.  
  107.     implicit val show = new Show[Email] {
  108.         override def apply( value: String, rule: Email ) = "error.email"
  109.     }
  110. }
  111.  
  112. // Exiting paste mode, now interpreting.
  113.  
  114. import shapeless._
  115. import shapeless.ops.hlist.LeftFolder
  116. defined trait Rule
  117. defined object Rule
  118. defined class RuleOps
  119. defined trait Definition
  120. defined trait Show
  121. defined trait Result
  122. defined class Success
  123. defined class Failure
  124. defined class Validation
  125. defined object Validation
  126. defined trait Email
  127. defined object Email
  128.  
  129. scala> Email.validate( "asdf" )
  130. res11: Result[Email.Value] = Failure(asdf,List(error.email))
  131.  
  132. scala> Validation( Email :: HNil ).validate( "asdf" )
  133. res12: Result[String] = Failure(asdf,List(error.email))
  134.  
  135. scala> Validation( Email :: Email :: HNil ).validate( "asdf" )
  136. <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]]
  137.        Validation( Email :: Email :: HNil ).validate( "asdf" )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement