Skip to content

Instantly share code, notes, and snippets.

@Ghurtchu
Last active April 4, 2023 21:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ghurtchu/904049da40a3fb892c73490e4d83f64c to your computer and use it in GitHub Desktop.
Save Ghurtchu/904049da40a3fb892c73490e4d83f64c to your computer and use it in GitHub Desktop.
Accumulative Validator
object ValidatorImpl {
sealed trait Validated[+E, +S]
object Validated {
final case class Passed[S](value: S) extends Validated[Nothing, S]
final case class Failed[E](errors: List[E]) extends Validated[E, Nothing]
}
trait Validator[+E, S] { previous =>
import Validator.PredicateMeta
protected def input: S
protected def failedPredicates[E1 >: E]: List[PredicateMeta[E1, S]] = List.empty
def satisfying[E1 >: E](f: S => Boolean, failureReason: E1): Validator[E1, S] = new Validator[E1, S] {
override def input: S = previous.input
override def failedPredicates[E2 >: E1]: List[PredicateMeta[E2, S]] =
if (f(input)) previous.failedPredicates
else PredicateMeta[E2, S](f, failureReason) :: previous.failedPredicates
}
def validated: Validated[E, S] = failedPredicates match {
case Nil => Validated.Passed(input)
case _ => Validated.Failed(failedPredicates.map[E](_.failureReason).reverse)
}
}
object Validator {
def apply[E, S](s: S): Validator[E, S] = new Validator[E, S] {
override def input: S = s
}
protected[Validator] final case class PredicateMeta[+E, S](f: S => Boolean, failureReason: E)
}
def main(args: Array[String]): Unit = {
final case class Person(name: String, age: Int)
sealed trait PersonValidationError { def reason: String }
object PersonValidationError {
case object EmptyName extends PersonValidationError { override def reason: String = "Person has an empty name" }
case object NonAdult extends PersonValidationError { override def reason: String = "Person is non adult" }
}
val person = Person("", 14)
val validatedPerson =
Validator(person)
.satisfying(_.name.nonEmpty, PersonValidationError.EmptyName)
.satisfying(_.age >= 18, PersonValidationError.NonAdult)
.validated
validatedPerson match {
case Validated.Passed(value) => println(s"passed $value")
case Validated.Failed(errors) => println(s"failed: $errors") // List(EmptyName, NonAdult)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment