Fields is a Scala validation library that you should use because it is:
- Final Tagless. Choose any Effect, Validated, or Error types.
- Informative. Error paths help understanding where the error occurred.
- Expressive. Rich, extendable validation syntax.
- Lightweight. The core module has no-dependencies.
- Dauntless. Have no fear of complex validations with
Rule
type. - Short-circuit. Avoid running undesired validation side-effects.
// Here we pass validation dependencies using implicits, but you could be doing this the way you prefer
def policy(implicit tokenService: TokenService, userService: UserService): Policy[RegisterRequest] = Policy
.builder[RegisterRequest]
.subRule(_.age)(_ >= 18, _ <= 110)
.subRule(_.email)(_.map(_.value).matchesRegex(EmailRegex))
.subRule(_.password)(_.nonEmpty, _.minSize(4), _.maxSize(100))
.subRule(_.password, _.passwordRepeat)(_ equalTo _)
.subRule(_.username)(validateUsername)
.subRule(_.token)(_.ensureF(tokenService.validateToken, _.failMessage("invalid-token")))
.build
// Extract complex validations to reusable methods.
def validateUsername(username: Field[String])(implicit userService: UserService): MRule =
username.minSize(1) &&
username.maxSize(10) &&
MRule.flatten {
userService.findByUsername(username.value).map {
case Some(_) => username.failMessage("username-already-exists")
case None => MRule.valid
}
}