Getting started
This article will show you how to install Akkurate and write your first validation code.
Installation
Akkurate is shipped with two dependencies; one is the library, the other one is a compiler plugin based on KSP. Follow the installation instructions below, according to your project structure.
Install in a single-platform project
Add KSP to your plugin list; make sure to use the appropriate version, depending on the Kotlin version you're using.
plugins { kotlin("jvm") version "2.0.20" id("com.google.devtools.ksp") version "2.0.20-1.0.25" }Add the dependencies and register the compiler plugin through KSP.
dependencies { implementation("dev.nesk.akkurate:akkurate-core:0.10.0") ksp("dev.nesk.akkurate:akkurate-ksp-plugin:0.10.0") }
Install in a multiplatform project
Add KSP to your plugin list; make sure to use the appropriate version, depending on the Kotlin version you're using.
plugins { kotlin("multiplatform") version "2.0.20" id("com.google.devtools.ksp") version "2.0.20-1.0.25" }Register the compiler plugin for the common target:
dependencies { add("kspCommonMainMetadata", "dev.nesk.akkurate:akkurate-ksp-plugin:0.10.0") } tasks { withType<KotlinCompilationTask<*>>().configureEach { if (name != "kspCommonMainKotlinMetadata") { dependsOn("kspCommonMainKotlinMetadata") } } }Configure the
commonMain
source set:kotlin.sourceSets.commonMain { dependencies { // Add Akkurate dependencies implementation("dev.nesk.akkurate:akkurate-core:0.10.0") } // Include the code generated by the KSP plugin kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin") }
Basic usage
Akkurate requires two elements to validate an object:
the
@Validate
annotation, to mark each class you need to validate;and the
Validator
interface, to define validation rules for your classes.
Here we want to validate a Book
class, with its title and release date. Let's create it and add the @Validate
annotation to it:
This annotation marks the class for the compiler processor, so it can generate validatable accessors for it. Now, build the project with ./gradlew build
(or in IntelliJ IDEA) to trigger code generation. We will explain this whole concept a bit further.
To apply constraints, you need to instantiate a Validator
with a lambda, let's start with an empty one:
The lambda receiver is a Validatable<Book>
, a generic class wrapping each value you access during validation. Its job is to track the path of each value, provide a DSL to ease validation, and act as a register for the constraints you apply to the value.
Now, let's constrain the title and the release date; the former must contain characters, and the latter can't be more than one year later after the current date:
You might have noticed that title
and releaseDate
aren't properties of the Validatable<T>
class, but it works anyway. This is why we had to annotate our data class with @Validate
and trigger the generation of validatable accessors; those act as a bridge between the Validatable<T>
class and the underlying value.
It is time to validate a book with our new validator:
Once validation is done, it returns a ValidationResult
, which is a sealed interface composed of two classes:
ValidationResult.Success
: the validation has succeeded, it contains the validated value.ValidationResult.Failure
: the validation has failed, it contains a violation list. Each violation contains the path of the property, and the message describing why it failed.
Here, the validation failed, so we got the following lines in the output stream:
Here is the whole code of this example, feel free to play with it in your editor: