-
Notifications
You must be signed in to change notification settings - Fork 1
Violation
A Violation represents a single validation failure. It is the fundamental unit of failed validation in KVerify — every
failed rule produces exactly one.
The interface is minimal by design:
interface Violation {
val reason: String
}For simple, one-off violations that don't need to carry structured data, use the violation() factory function:
enforce {
if (user.name.isBlank()) violation("Name must not be blank") else null
}This is the quickest way to produce a violation, and it's a good fit for custom rules where the reason string is all you need.
When a failure carries information beyond a message — for example, the actual value, the constraint that was violated,
or a code your API can act on — implement Violation directly:
data class PasswordTooShortViolation(
val actualLength: Int,
val minimumLength: Int,
) : Violation {
override val reason =
"Password must be at least $minimumLength characters, but got $actualLength"
}This lets you handle failures by type rather than by parsing strings:
for (violation in result.violations) {
val message = when (violation) {
is PasswordTooShortViolation ->
"Try a longer password (minimum: ${violation.minimumLength})"
is EmailFormatViolation ->
"Please check your email format"
else -> violation.reason
}
println(message)
}Typed violations are one of KVerify's core strengths — use them wherever the caller needs to react to a specific failure differently than others.
If you have kverify-rule-set on your classpath, PathAwareViolation is available as an extension of Violation that
also carries the path to the value that failed:
interface PathAwareViolation : Violation {
val validationPath: ValidationPath
}All built-in rule violations implement PathAwareViolation. If you are writing custom violations for use alongside
built-in rules, implementing it keeps your violations consistent with the rest of the library:
data class PasswordTooShortViolation(
val actualLength: Int,
val minimumLength: Int,
override val validationPath: ValidationPath,
) : PathAwareViolation {
override val reason =
"Password must be at least $minimumLength characters, but got $actualLength"
}The validationPath is typically obtained from the active scope inside a rule:
fun Verification<String>.minPasswordLength(min: Int): Verification<String> =
apply {
val actualLength = value.length
scope.failIf({ actualLength < min }) {
PasswordTooShortViolation(
actualLength = actualLength,
minimumLength = min,
validationPath = scope.validationContext.validationPath(),
)
}
}If you only depend on kverify-core or path information isn't relevant to your use case, implementing Violation
directly is perfectly fine.
- ValidationContext & ValidationPath — understand how path information travels through validation
- Verification — the primary surface for chaining rules