Gradle
dependencies {
implementation("ru.potatophobe:validsl:$validslVersion")
}
Maven
<dependencies>
<dependency>
<groupId>ru.potatophobe</groupId>
<artifactId>validsl</artifactId>
<version>${validslVersion}</version>
</dependency>
</dependencies>
fun processPerson(person: Person?) {
validate(person) {
value { notNull() }
properties {
validate(Person::name) {
value { hasLengthIn(3..20) and matchPattern("<someNamePattern>") }
}
validate(Person::phoneNumbers) {
value { notEmptyCollection() }
elements {
value { matchPattern("<somePhonePattern>") }
}
}
validate(Person::familyMap) {
value { notEmptyMap() }
keys {
value { isOneOf("Mother", "Father", "Sister", "Brother") }
}
values {
value { matchPattern("<someNamePattern>") }
}
}
}
} fail {
throw BusinessException(value, faults)
}
// main logic
}
data class Person(
val name: String,
val phoneNumbers: List<String>,
val familyMap: Map<String, String>
) {
init {
validate(this) {
value { notNull() }
properties {
validate(Person::name) {
value { hasLengthIn(3..20) and matchPattern("<someNamePattern>") }
}
validate(Person::phoneNumbers) {
value { notEmptyCollection() }
elements {
value { matchPattern("<somePhonePattern>") }
}
}
validate(Person::familyMap) {
value { notEmptyMap() }
keys {
value { isOneOf("Mother", "Father", "Sister", "Brother") }
}
values {
value { matchPattern("<someNamePattern>") }
}
}
}
} fail {
throw BusinessException(value, faults)
}
}
}
val value: String // assignment
validate(value) {
value {
match { it == "something" } description "Must be equal to something"
}
} success {
// do something if validation result is success
} fail {
// do something if validation result is fail
}
If value not equal to "something"
fail
block will execute.
Inside fail
block you have access to value
property that contains actual validated value,
and faults
property that is list of validation faults with 3 properties:
path
- path to property, which failed validation,
description
- constraint description you defined above,
value
- actual value of property, which failed validation
You can define aliases for frequent constraints according to convention:
Aliases starting with `is` specifies that `value` must not be `null`, except `isNull()`
other aliases specifies that `null` `value` will pass validation
All aliases must return `ru.potatophobe.validsl.MatchAlias` object to support multiple aliases on one line using `and`
Example: `{ notNull() and hasLength(10) }`
fun <T> ValueScope<T>.isEqualTo(value: T & Any) = matchAlias {
match { it == value } description "Must be equal to $value"
}
and now validation code looks better
val value: String // assignment
validate(value) {
value {
isEqualTo("something")
}
} success {
// do something if validation result is success
} fail {
// do something if validation result is fail
}
Most frequent constraints already have aliases
/**
* Specifies that validated value must be null
* */
@Validsl
fun ValueScope<*>.isNull() = matchAlias {
match { it == null } description "Must be null"
}
/**
* Specifies that validated value must not be null
* */
@Validsl
fun ValueScope<*>.isNotNull() = matchAlias {
match { it != null } description "Must not be null"
}
/**
* Specifies that validated value must not be null and be equal to provided value
* */
@Validsl
fun <T> ValueScope<T>.isEqualTo(value: T & Any) = matchAlias {
match { it == value } description "Must be equal to $value"
}
/**
* Specifies that validated value must not be null and be equal to one of provided values
* */
@Validsl
fun <T> ValueScope<T>.isOneOf(vararg values: T & Any) = matchAlias {
match { values.contains(it) } description "Must be one of $values"
}
full list in ru.potatophobe.validsl.MatchAliases.kt
data class Something(
val property: String
)
val value: Something // assignment
validate(value) {
properties { // object properties validation definition
validate(Something::property) {
value { isEqualTo("something") }
}
}
} success {
// do something if validation result is success
} fail {
// do something if validation result is fail
}
data class Something(
val properties: List<String>
)
val value: Something // assignment
validate(value) {
properties {
validate(Something::properties) {
elements { // object elements validation definition
value { isEqualTo("something") }
}
}
}
} success {
// do something if validation result is success
} fail {
// do something if validation result is fail
}
data class Something(
val propertiesMap: Map<String, String>
)
val value: Something // assignment
validate(value) {
properties {
validate(Something::propertiesMap) {
entries {
value { mapsKey("key" to "value") }
}
keys { // object keys validation definition
value { isEqualTo("something") }
}
values { // object values validation definition
value { isEqualTo("something else") }
}
}
}
} success {
// do something if validation result is success
} fail {
// do something if validation result is fail
}