A Kotlin Multi-platform Validation Library. Simple, flexible, and not tied to any particular framework.
val result = validateEmail("[email protected]")
if (result.isValid) {
// ...
}This library has two fundamental pieces: Validator and ValidationResult. The validator-core module is the base
module and contains everything necessary for creating and working with Validators, including the Validator
and ValidationResult types.
The Validator type is a simple interface that takes in a type T, performs validation on that type, and returns
a ValidationResult of type R:
interface Validator<T, R> {
fun validate(input: T): ValidationResult<R>
}So a Validator can be used like so:
val result = validator.validate("exampleInput")The Validator interface also provides the following operator function which just delegates to the validate function:
operator fun invoke(input: T): ValidationResult<R> = validate(input)This way, a Validator can also be used like so:
val result = validator("exampleInput")The Validator interface takes two generic type parameters <T, R>. This allows Validators to return a different
value than they take in as their input and can be especially useful for Validators that take in nullable types for
their input and wish to return a non-null type for their output. This works because the returned value is only available
for a ValidationResult.Success result.
For example, consider the following Validator that takes in a nullable type of String? and returns a non-null type
of String:
class NonNullStringValidator : Validator<String?, String> {
override fun validate(input: String?) = if (input == null) Invalid() else Valid(input)
}The ValidationResult type is a sealed class with two sub-classes: Valid and Invalid. Obviously, the Valid type
should be returned from a Validator when the validation succeeded, and the Invalid when the validation failed.
Checking for the ValidationResult type is as simple as using Kotlin's is keyword:
val result = validator(input)
if (result is ValidationResult.Valid) {
}
if (result is ValidationResult.Invalid) {
}Or if only the condition is required, then the isValid and isInvalid convenience functions can be used:
val result = validator(input)
if (result.isValid) {
}
if (result.isInvalid) {
}The Valid class has a single value property which contains the successfully validated input to a Validator.
val result = validateNonNullString("nonNullStringInput")
when (result) {
is ValidationResult.Valid -> performSuccessAction(result.value)
}To create a Valid object instance, simply call the constructor function with the return value:
Valid(validatedInput)The Invalid class has an errors property which is a Kotlin Collection of ValidationError. Accessing these errors
is just as simple as accessing a Valid.value property:
val result = validateNonNullString(null)
when (result) {
is ValidationResult.Invalid -> performFailedAction(result.errors)
}To create an Invalid object instance, simply call one of the constructor functions with the errors encountered:
Invalid(errorOne, errorTwo)
// Or
Invalid(listOf(errorOne, errorTwo))The ValidationError type is a simple interface with an optional details property:
interface ValidationError {
val details: String?
}Note: ValidationError implementations can be
Kotlin Exceptions but they are not thrown from
a Validator and instead are wrapped in a ValidationResult.Invalid class and returned.
A ValidationError implementation can be whatever is required, but a common pattern is to use sealed classes:
sealed class ExampleError(override val details: String? = null) : ValidationError {
object Null : ExampleError()
object TooShort : ExampleError()
object TooLong : ExampleError()
data class InvalidCharacters(val characters: List<Char>) : ExampleError()
}There's a convenience operator function on the ValidationResult class to determine if it is an Invalid instance and
contains the provided ValidationError:
val result = validate(input)
if (result.contains(ExampleError.Null)) {
}The validator-core module contains everything necessary to create and work with validation logic. But sometimes more
specific functionality is required. That's why this library contains separate modules for extra functionality and
specific use cases. With this approach, the User can choose which functionality they utilize.
Currently, the following modules are available:
validator-core- The common code and base for the whole library.validator-dynamic-string- A Kotlin DSL approach for creating a Validator forStringinput.validator-email- Provides validation for determining whether aStringis in a valid Email format.validator-field- An extension on thevalidator-coremodule, providing aFieldValidationErrorwhich associates a Kotlin property on a class.validator-mac- Provides validation for determining whether aStringis in a valid MAC Address format.validator-phone- Provides validation for determining whether aStringis in a valid Phone Number format.validator-web- ProvidesValidatorsfor web-basedStringformats, such as, URLs, URIs, and IP Addresses.
The library is provided through Repsy.io. Checkout
the releases page to get the latest version.
repositories {
maven { url = uri("https://repo.repsy.io/mvn/chrynan/public") }
}Core:
implementation("com.chrynan.validator:validator-core:$VERSION")Dynamic String:
implementation("com.chrynan.validator:validator-dynamic-string:$VERSION")Email:
implementation "com.chrynan.validator:validator-email:$VERSION"Field:
implementation("com.chrynan.validator:validator-field:$VERSION")Mac Address:
implementation("com.chrynan.validator:validator-mac:$VERSION")Phone Number:
implementation("com.chrynan.validator:validator-phone:$VERSION")Web:
implementation("com.chrynan.validator:validator-web:$VERSION")More detailed documentation is available in the docs folder. The entry point to the documentation can be found here.
Contributions are welcome and encouraged! Please consider the following when contributing code:
- All validation code must be written in the
commonMainsource set. If this is not possible, then each target platform must have an actual implementation. - All public facing code must have detailed KDoc comments.
- All code must have corresponding tests written in the
commonTestsource set. - Follow existing coding conventions for this repository.
- If submitting a new
Validator, make sure it is a common use case. - Code reviews must maintain a productive, courteous, and respectful atmosphere.
- Self review code before submitting a PR.
- All CI builds must pass for a PR to be merged.
- All submitted code will be subject to this repository's license.
It's true, there are many Kotlin validation libraries. However, I found myself unsatisfied with the current implementations for the following reasons:
- Tied to a framework.
- Most of the available validation libraries were built around UI Components from frameworks like Android.
- Not multi-platform.
- The available validation libraries typically are for either a framework, like Android, or specifically for the JVM.
- There's no reason why the code can't be written for Kotlin Multi-platform, so that it can be used anywhere.
- No flexibility.
- The current libraries are all or nothing. Including functionality that might not be required.
- Forced to conform to a particular DSL approach without the ability to extend the functionality.
This library aims to resolve the issues with the other libraries by:
- Not being tied to a framework.
- Supporting Kotlin Multi-platform.
- Providing flexibility to choose functionality.
- Being extensible.
- Providing commonly used
Validators.
Copyright 2021 chRyNaN
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.