### Complete Book Validation Example Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/getting-started.md A full working example showing class definition, validator creation, constraint application, and validation execution. Includes necessary imports and demonstrates error reporting. ```kotlin package dev.nesk.akkurate.demo import dev.nesk.akkurate.ValidationResult import dev.nesk.akkurate.Validator import dev.nesk.akkurate.annotations.Validate import dev.nesk.akkurate.constraints.builders.* import dev.nesk.akkurate.demo.validation.accessors.* import java.time.LocalDateTime @Validate data class Book(val title: String, val releaseDate: LocalDateTime) val validateBook = Validator { title.isNotEmpty() val oneYearLater = LocalDateTime.now().plusYears(1) releaseDate.isBeforeOrEqualTo(oneYearLater) } fun main() { val invalidBook = Book( title = "", releaseDate = LocalDateTime.now().plusYears(3) ) when (val result = validateBook(invalidBook)) { is ValidationResult.Success -> { println("The book is valid: ${result.value}") } is ValidationResult.Failure -> { println("The book is invalid, here are the errors:") for ((message, path) in result.violations) { println(" - $path: $message") } } } } ``` -------------------------------- ### Install %product% in Multiplatform Project (Kotlin) Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/getting-started.md This snippet demonstrates setting up a multiplatform Kotlin project with %product%. It covers adding the KSP plugin, registering the plugin for the common target, and configuring the commonMain source set with core dependencies and generated source directories. ```kotlin plugins { kotlin("multiplatform") version "2.1.0" id("com.google.devtools.ksp") version "2.1.0-1.0.29" } dependencies { add("kspCommonMainMetadata", "dev.nesk.akkurate:akkurate-ksp-plugin:%version%") } tasks { withType>().configureEach { if (name != "kspCommonMainKotlinMetadata") { dependsOn("kspCommonMainKotlinMetadata") } } } kotlin.sourceSets.commonMain { dependencies { // Add %product% dependencies implementation("dev.nesk.akkurate:akkurate-core:%version%") } // Include the code generated by the KSP plugin kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin") } ``` -------------------------------- ### Validation Error Output Example Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/getting-started.md Shows the expected output when validation fails. Displays violations with their paths and constraint messages. Output format helps identify problematic fields quickly. ```text The book is invalid, here are the errors: - [title]: Must not be empty - [releaseDate]: Must be before or equal to "2024-08-11T18:56:04.0" ``` -------------------------------- ### Install %product% in Single-Platform Project (Kotlin) Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/getting-started.md This snippet shows how to configure a single-platform Kotlin project to use %product%. It includes adding the KSP plugin and the necessary %product% core and KSP plugin dependencies. ```kotlin plugins { kotlin("jvm") version "2.1.0" id("com.google.devtools.ksp") version "2.1.0-1.0.29" } dependencies { implementation("dev.nesk.akkurate:akkurate-core:%version%") ksp("dev.nesk.akkurate:akkurate-ksp-plugin:%version%") } ``` -------------------------------- ### Execute Validation and Handle Results Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/getting-started.md Validates an instance of the data class and handles the result. The result is either Success (with valid data) or Failure (with violation messages and paths). ```kotlin val invalidBook = Book( title = "", releaseDate = LocalDateTime.now().plusYears(3) ) when (val result = validateBook(invalidBook)) { is ValidationResult.Success -> { println("The book is valid: ${result.value}") } is ValidationResult.Failure -> { println("The book is invalid, here are the errors:") for ((message, path) in result.violations) { println(" - $path: $message") } } } ``` -------------------------------- ### Create Validator for Data Class Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/getting-started.md Instantiates a Validator for a data class using a lambda. The lambda receiver is a Validatable wrapper that tracks value paths and provides a DSL for applying constraints. ```kotlin val validateBook = Validator { // this: Validatable } ``` -------------------------------- ### Define Validatable Data Class with Annotation Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/getting-started.md Marks a Kotlin data class for validation using the @Validate annotation. This enables code generation of validatable accessors required for the validation process. ```kotlin @Validate data class Book(val title: String, val releaseDate: LocalDateTime) ``` -------------------------------- ### Apply Validation Constraints to Properties Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/getting-started.md Defines validation rules within the Validator lambda. Uses generated accessors to apply constraints directly to data class properties. Requires prior @Validate annotation and code generation. ```kotlin val validateBook = Validator { title.isNotEmpty() val oneYearLater = LocalDateTime.now().plusYears(1) releaseDate.isBeforeOrEqualTo(oneYearLater) } ``` -------------------------------- ### Kotlin: DSL Scope Control Example Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/migration-guide.md Illustrates the use of scope control in Akkurate's DSL, specifically highlighting the difference between implicit and explicit receivers for method calls within nested blocks. This change was introduced in version 0.7.0. ```kotlin @Validate data class Book(val title: String, val labels: List) val validateBook = Validator { title.isNotBlank() labels { each { isNotBlank() hasSizeLowerThan(10) // ❌ Compiler error: 'hasSizeLowerThan' can't be called // in this context by implicit receiver. Use the explicit // one if necessary. // 💬 It happens because 'hasSizeLowerThan' is implicitly // applied to the 'labels' property. } } } ``` ```kotlin @Validate data class Book(val title: String, val labels: List) val validateBook = Validator { title.isNotBlank() labels { hasSizeLowerThan(10) // ✅ Success: 'hasSizeLowerThan' is explicitly applied to the // 'labels' property. each { isNotBlank() } } } ``` -------------------------------- ### cURL Request to List Books Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Example cURL command to send a GET request to the /books endpoint to retrieve a list of all books stored in the database. ```shell curl 127.0.0.1:8080/books -v ``` -------------------------------- ### Kotlin: Configuration Builder DSL Update Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/migration-guide.md Shows the updated way to create a Configuration instance using a builder DSL, which replaced the data class constructor in version 0.4.0. This change adheres to JVM API guidelines for backward compatibility. ```kotlin Configuration { defaultViolationMessage = "The field contains an invalid value" rootPath("custom", "root", "path") } ``` ```kotlin Configuration( defaultViolationMessage = "The field contains an invalid value", rootPath = listOf("custom", "root", "path"), ) ``` -------------------------------- ### cURL Request to Create a Book Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Example cURL command to send a POST request to the /books endpoint to create a new book with a specified ISBN and title. ```shell curl 127.0.0.1:8080/books -v \ --header 'Content-Type: application/json' \ --data '{"isbn": "123", "title": ""}' ``` -------------------------------- ### Kotlin Book Validation Example Source: https://github.com/nesk/akkurate/blob/main/README.md Demonstrates defining data classes (Book, Author) with validation annotations and then writing validation rules using Akkurate's DSL. It includes validation for string emptiness, date ranges, list sizes, and nested object validation. The example also shows how to execute the validation and handle success or failure results. ```kotlin import java.time.LocalDateTime import akkurate.ConstraintViolation.Companion.Failure import akkurate.ConstraintViolation.Companion.Success import akkurate.Validate import akkurate.Validator // Define your classes @Validate data class Book( val title: String, val releaseDate: LocalDateTime, val authors: List, ) @Validate data class Author(val firstName: String, val lastName: String) // Write your validation rules val validateBook = Validator { // First the property, then the constraint, finally the message. title.isNotEmpty() otherwise { "Missing title" } releaseDate.isInPast() otherwise { "Release date must be in past" } authors.hasSizeBetween(1..10) otherwise { "Wrong author count" } authors.each { // Apply constraints to each author (firstName and lastName) { // Apply the same constraint to both properties isNotEmpty() otherwise { "Missing name" } } } } // Validate your data // Assuming 'someBook' is an instance of Book // val someBook = Book("The Great Novel", LocalDateTime.now().minusDays(1), listOf(Author("John", "Doe"))) /* when (val result = validateBook(someBook)) { is Success -> println("Success: ${result.value}") is Failure -> { val list = result.violations .joinToString("\n") { "${it.path}: ${it.message}" } println("Failures:\n$list") } } */ ``` -------------------------------- ### Configure Ktor Database Connection and DAO Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Initializes the database connection and instantiates the 'BookDao' within the Ktor application's configuration. Uses a top-level 'lateinit var' for the DAO, suitable for simpler setups. ```kotlin import io.ktor.server.application.Application import org.jetbrains.exposed.sql.Database lateinit var bookDao: BookDao fun Application.configureDatabases() { val database = Database.connect(/* connection details */) bookDao = BookDao(database) } ``` -------------------------------- ### Apply same constraints to multiple properties Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/harness-the-dsl.md Demonstrates how to use validatable compounds to apply the same constraints to multiple properties at once. The example validates firstName, middleName, and lastName with the same constraints. Dependencies include the Akkurate validation library. The input is a User instance and the output is a validation result. ```kotlin Validator { firstName.isNotEmpty() firstName.hasLengthLowerThanOrEqualTo(50) middleName.isNotEmpty() middleName.hasLengthLowerThanOrEqualTo(50) lastName.isNotEmpty() lastName.hasLengthLowerThanOrEqualTo(50) } ``` ```kotlin Validator { (firstName and middleName and lastName) { isNotEmpty() hasLengthLowerThanOrEqualTo(50) } } ``` -------------------------------- ### Add %product% Ktor Server Plugin Dependency Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-server-integration.md This snippet shows how to add the %product% Ktor Server plugin dependency to your Gradle build script. Ensure you have the Content Negotiation and Request Validation plugins installed in your Ktor application. ```kotlin implementation("dev.nesk.akkurate:akkurate-ktor-server:%version%") ``` -------------------------------- ### Access properties in Kotlin DSL Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/harness-the-dsl.md Demonstrates basic property access within a Validator block. The example shows how to access the 'title' property of a Book class. Dependencies include the Akkurate validation library. The input is a Book instance and the output is a validation result. ```kotlin @Validate data class Book(val title: String) Validator { title.isNotEmpty() } ``` -------------------------------- ### Configure API Routes (Kotlin) Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Defines the routing for the HTTP API, including a POST endpoint for creating books and a GET endpoint for listing books. It handles request deserialization, database interaction, and response generation. ```kotlin routing { post("/books") { val book = call.receive() bookDao.create(book) call.respond(HttpStatusCode.Created) } get("/books") { val books = bookDao.list() call.respond(HttpStatusCode.OK, books) } } ``` -------------------------------- ### Gradle: Akkurate Dependency Update for Validate Annotation Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/migration-guide.md Shows how to update Gradle dependencies after the Validate annotation was moved to the aakkurate-core artifact in version 0.9.0. This change allows for the removal of the aakkurate-ksp-plugin implementation dependency. ```gradle dependencies { implementation("dev.nesk.akkurate:akkurate-core:0.8.0") implementation("dev.nesk.akkurate:akkurate-ksp-plugin:0.8.0") ksp("dev.nesk.akkurate:akkurate-ksp-plugin:0.8.0") } ``` ```gradle dependencies { implementation("dev.nesk.akkurate:akkurate-core:0.9.0") ksp("dev.nesk.akkurate:akkurate-ksp-plugin:0.9.0") } ``` -------------------------------- ### Kotlin Validation Example for Book and Author Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/overview.md Demonstrates defining data classes with validation annotations, writing validation rules using a declarative DSL, and executing the validation against a data object. It shows how to define constraints for properties and collections, including nested validation for list elements. ```kotlin import java.time.LocalDateTime // Define your classes @Validate data class Book( val title: String, val releaseDate: LocalDateTime, val authors: List, ) @Validate data class Author(val firstName: String, val lastName: String) // Write your validation rules val validateBook = Validator { // First the property, then the constraint, finally the message. title.isNotEmpty() otherwise { "Missing title" } releaseDate.isInPast() otherwise { "Release date must be in past" } authors.hasSizeBetween(1..10) otherwise { "Wrong author count" } authors.each { // Apply constraints to each author (firstName and lastName) { // Apply the same constraint to both properties isNotEmpty() otherwise { "Missing name" } } } } // Validate your data when (val result = validateBook(someBook)) { is Success -> println("Success: ${result.value}") is Failure -> { val list = result.violations .joinToString("\n") { "${it.path}: ${it.message}" } println("Failures:\n$list") } } ``` -------------------------------- ### Validate Event with Temporal Constraints (Kotlin) Source: https://context7.com/nesk/akkurate/llms.txt This example demonstrates validating an Event object using Akkurate and kotlinx-datetime. It includes constraints for event name, start/end dates being in the future, and custom logic ensuring the end date is after the start date and the registration deadline is before the event start. Dependencies include Akkurate, kotlinx-datetime, and standard Kotlin data classes. ```kotlin import dev.nesk.akkurate.Validator import dev.nesk.akkurate.constraints.builders.* import kotlinx.datetime.* data class Event( val name: String, val startDate: LocalDateTime, val endDate: LocalDateTime, val registrationDeadline: Instant ) val validateEvent = Validator { name { isNotBlank() otherwise { "Event name required" } hasLengthBetween(5..200) } startDate { isInFuture() otherwise { "Event must start in the future" } } endDate { isInFuture() otherwise { "Event must end in the future" } // Custom constraint: end date must be after start date constrain { endDate -> endDate > unwrap().startDate } otherwise { "End date must be after start date" } } registrationDeadline { isInFuture() otherwise { "Registration deadline must be in future" } // Deadline must be before event start constrain { deadline -> val event = unwrap() deadline < event.startDate.toInstant(TimeZone.currentSystemDefault()) } otherwise { "Registration must close before event starts" } } } // Example with specific date validation val validateBirthDate = Validator { isInPast() otherwise { "Birth date must be in the past" } isAfter(LocalDate(1900, 1, 1)) otherwise { "Birth date too far in past" } isBefore(LocalDate.parse("2020-01-01")) otherwise { "Must be at least 5 years old" } } val birthDate = LocalDate(1990, 5, 15) validateBirthDate(birthDate) // Success ``` -------------------------------- ### Kotlin Validation Rule Description Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/overview.md Illustrates how a validation rule in Akkurate can be read like a natural language sentence, making the code more understandable. This example shows a simple 'isNotEmpty' constraint with a custom error message. ```kotlin title.isNotEmpty() otherwise { "Missing title" } ``` -------------------------------- ### Kotlin: ConstraintViolationSet.equals Symmetry Update Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/migration-guide.md Demonstrates the change in ConstraintViolationSet.equals method behavior before and after version 0.9.0. The equals method is now symmetric as required by the Set interface specification. No external dependencies are required for this code snippet. ```kotlin fun compare( standardSet: Set, constraintViolationSet: ConstraintViolationSet ) { standardSet.equals(constraintViolationSet) // ✅ true constraintViolationSet.equals(standardSet) // ❌ false } ``` ```kotlin fun compare( standardSet: Set, constraintViolationSet: ConstraintViolationSet ) { standardSet.equals(constraintViolationSet) // ✅ true constraintViolationSet.equals(standardSet) // ✅ true } ``` -------------------------------- ### Test Custom Constraints with akkurate-test in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/extend.md Provides an example of unit testing custom constraints in Kotlin using the 'akkurate-test' artifact. It shows how to create a 'Validatable' instance and assert the satisfaction status of a constraint, verifying both successful and unsuccessful constraint applications. ```kotlin import dev.nesk.akkurate.test.Validatable import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue // Assuming hasWordCountGreaterThan is defined as shown previously private fun Validatable.hasWordCountGreaterThan(count: Int) = constrainIfNotNull { it.split(" ").size > count } otherwise { "Must contain more than $count words" } val validatable = Validatable("The Lord of the Rings") val satisfiedConstraint = validatable.hasWordCountGreaterThan(4) assertTrue(satisfiedConstraint.satisfied) val unsatisfiedConstraint = validatable.hasWordCountGreaterThan(5) assertFalse(unsatisfiedConstraint.satisfied) ``` -------------------------------- ### Kotlin Akkurate Ktor Integration Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Presents how to integrate Akkurate with Ktor to automatically validate incoming payloads. It involves installing the Akkurate and RequestValidation plugins and registering the custom validator. ```kotlin import io.ktor.server.application.* import io.ktor.server.plugins.RequestValidation import io.ktor.server.plugins.RequestValidation.* import akkurate.Validator fun Application.configureValidation() { install(Akkurate) install(RequestValidation) { registerValidator(validateBook) { bookDao } } } ``` -------------------------------- ### cURL Request for Long Title Book Creation Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Example cURL command to attempt creating a book with a title exceeding the maximum allowed character limit, intended to demonstrate validation failure. ```shell curl 127.0.0.1:8080/books -v \ --header 'Content-Type: application/json' \ --data '{"isbn": "456", "title": "this a really long title and it will not fit our database column"}' ``` -------------------------------- ### Ktor Routing and Book Validation Setup Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-server-integration.md This Kotlin code defines a Ktor route for handling POST requests to '/books'. It includes a `Book` data class annotated with `@Validate` and a validator `validateBook` to ensure the 'title' field is not empty. The validators are registered within the Ktor server configuration. ```kotlin routing { post("/books") { val book = call.receive() call.respond(book.title) } } @Validate @Serializable data class Book(val title: String) val validateBook = Validator { title.isNotEmpty() } install(Akkurate) install(ContentNegotiation) { json() } install(RequestValidation) { registerValidator(validateBook) } ``` -------------------------------- ### Custom Constraints and Reusable Validators Source: https://context7.com/nesk/akkurate/llms.txt This example showcases how to create custom validation constraints and reusable validators using Akkurate. It demonstrates defining custom constraint functions, composing validators for complex data structures, and utilizing existing validators. ```kotlin import dev.nesk.akkurate.Validator import dev.nesk.akkurate.constraints.constrain import dev.nesk.akkurate.constraints.builders.* import dev.nesk.akkurate.validatables.Validatable fun Validatable.isValidSlug() = constrain { it != null && it.matches(Regex("^[a-z0-9]+(?:-[a-z0-9]+)*$")) } otherwise { "Must be a valid URL slug (lowercase, numbers, hyphens only)" } fun Validatable.containsWord(word: String) = constrain { it != null && it.contains(word, ignoreCase = true) } otherwise { "Must contain the word '$word'" } val isValidUUID = Validator { isMatching(Regex("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")) } otherwise { "Must be a valid UUID" } data class Article(val id: String, val slug: String, val title: String, val content: String, val tags: List) val validateArticle = Validator
{ id { validateWith(isValidUUID) } slug { isNotBlank(); hasLengthBetween(3..100); isValidSlug() } title { isNotBlank(); hasLengthBetween(10..200) } content { isNotBlank(); hasLengthBetween(100..50000); containsWord(unwrap().title.split(" ").first()) } tags { isNotEmpty() otherwise { "At least one tag required" }; hasSizeBetween(1..10); each { isNotBlank(); hasLengthBetween(2..30); isMatching(Regex("^[a-z0-9-]+$")) } } } val article = Article( id = "550e8400-e29b-41d4-a716-446655440000", slug = "introduction-to-kotlin", title = "Introduction to Kotlin Programming", content = "This article provides an Introduction to Kotlin...", tags = listOf("kotlin", "programming", "tutorial") ) validateArticle(article) ``` -------------------------------- ### Access deep properties in Kotlin DSL Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/harness-the-dsl.md Shows how to access nested properties in a data structure using the validation DSL. The example traverses from Book to Author to User to emailAddress. Dependencies include the Akkurate validation library. The input is a Book instance and the output is a validation result. ```kotlin @Validate data class Book(val author: Author) @Validate data class Author(val user: User) @Validate data class User(val emailAddress: String) Validator { author.user.emailAddress.isNotEmpty() } ``` -------------------------------- ### Avoid path repetition in Kotlin DSL Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/harness-the-dsl.md Illustrates how to avoid repeating property paths when applying multiple constraints to a single property. The example applies multiple validations to emailAddress without repeating the full path. Dependencies include the Akkurate validation library. The input is a Book instance and the output is a validation result. ```kotlin Validator { // this: Validatable author.user.emailAddress { // this: Validatable isNotEmpty() isContaining("@") } } ``` -------------------------------- ### Transform data before validation using map Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/complex-structures.md Demonstrates how to normalize data before validation using the map function. The example shows trimming '#' characters from hashtag strings in a Book validation scenario. ```kotlin @Validate data class Book(val title: String, val hashtags: Set) val validateBook = Validator { title.isNotEmpty() hashtags.each { map { it.trimStart('#') }.isNotEmpty() } } ``` -------------------------------- ### Handle nullable types in Kotlin DSL Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/harness-the-dsl.md Shows how nullable types are handled in the validation DSL. The example demonstrates that nullability propagates to child properties and that constraints on nullable properties succeed when the value is null. Dependencies include the Akkurate validation library. The input is a Book instance with nullable author and the output is a validation result. ```kotlin @Validate data class Book(val author: Author?) Validator { // This constraint will always succeed when // the value of the property is `null`. author.user.emailAddress.isNotEmpty() } ``` -------------------------------- ### String Validation Constraints (Kotlin) Source: https://context7.com/nesk/akkurate/llms.txt Provides examples of Kotlin validators for strings using Akkurate's built-in constraints. Covers email format, password complexity, and URL validation, including checks for required fields, length, specific patterns, and protocols. ```kotlin import dev.nesk.akkurate.Validator import dev.nesk.akkurate.constraints.builders.* val validateEmail = Validator { isNotBlank() otherwise { "Email required" } isMatching(Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) otherwise { "Invalid email format" } hasLengthLowerThanOrEqualTo(100) otherwise { "Email too long" } } val validatePassword = Validator { isNotEmpty() otherwise { "Password required" } hasLengthBetween(8..64) otherwise { "Password must be 8-64 characters" } isMatching(Regex(".*[A-Z].*")) otherwise { "Must contain uppercase letter" } isMatching(Regex(".*[a-z].*")) otherwise { "Must contain lowercase letter" } isMatching(Regex(".*\\d.*" )) otherwise { "Must contain digit" } isMatching(Regex(".*[@#$%^&+=].*")) otherwise { "Must contain special character" } } val validateUrl = Validator { isNotBlank() otherwise { "URL required" } isStartingWith("https://") otherwise { "Must use HTTPS" } isNotContaining(" ") otherwise { "URL cannot contain spaces" } } // Test the validators validateEmail("user@example.com") // Success validateEmail("invalid-email") // Failure: Invalid email format validatePassword("SecureP@ss1") // Success validatePassword("weak") // Failure: Multiple constraint violations validateUrl("https://example.com") // Success ``` -------------------------------- ### Numeric and Range Validation (Kotlin) Source: https://context7.com/nesk/akkurate/llms.txt Demonstrates Kotlin validators for numeric types and ranges using Akkurate. This example validates a 'Product' object, applying constraints to price (positive, finite, upper bound), quantity (non-negative, within a range), and discount (between 0 and 100 if present). ```kotlin import dev.nesk.akkurate.Validator import dev.nesk.akkurate.constraints.builders.* data class Product( val name: String, val price: Double, val quantity: Int, val discount: Float? ) val validateProduct = Validator { name { isNotBlank() otherwise { "Product name required" } hasLengthBetween(3..100) } price { isPositive() otherwise { "Price must be positive" } isLowerThan(1_000_000.0) otherwise { "Price too high" } isFinite() otherwise { "Price must be a valid number" } } quantity { isPositiveOrZero() otherwise { "Quantity cannot be negative" } isBetween(0..10000) otherwise { "Invalid quantity range" } } discount { constrainIfNotNull { it >= 0f && it <= 100f } otherwise { "Discount must be between 0 and 100 percent" } } } // Test validation val validProduct = Product("Laptop", 999.99, 50, 10f) validateProduct(validProduct) // Success val invalidProduct = Product("", -50.0, -5, 150f) validateProduct(invalidProduct) // Failure with multiple violations ``` -------------------------------- ### Compare values using unwrap method Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/complex-structures.md Illustrates comparing values of Validatable objects by explicitly calling unwrap() to get the underlying values. Used when checking if book heights match shelf height in a Library validation. ```kotlin Validator { val library = this for (book in books) { constrain { book.height.unwrap() == library.shelfHeight.unwrap() } otherwise { "Book height must be equal to shelf height" } } } ``` -------------------------------- ### Using Multiple Contextual Objects in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/use-external-sources.md This example wraps multiple dependencies in a Context data class for a validator requiring both UserDao and TweetDao. It leverages Kotlin data classes for simplicity. The validator accesses context properties; inputs are a Context and a value; outputs are validation constraints. Assumes synchronous access and does not manage dependencies. ```kotlin interface TweetDao data class Context(val userDao: UserDao, val tweetDao: TweetDao) Validator { context -> context.userDao // UserDao context.tweetDao // TweetDao } ``` -------------------------------- ### Enforce non-null values in Kotlin DSL Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/harness-the-dsl.md Demonstrates how to require non-null values for properties using the isNotNull constraint. The example shows validating an email address that must not be null before checking if it's empty. Dependencies include the Akkurate validation library. The input is a Book instance and the output is a validation result. ```kotlin Validator { author.user.emailAddress { isNotNull() // Fails if the value is null. isNotEmpty() // When not null, checks for emptiness. } } ``` -------------------------------- ### Custom Path with Manual Path Construction in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/apply-constraints.md Shows how to manually construct a custom path by receiving the current path as a parameter. This allows for greater customization of the resulting path. ```kotlin author.fullName.isNotEmpty() withPath { path -> listOf(path.first, "a", "b", "c", path.last) } ``` -------------------------------- ### Show Default Configuration Validation Output Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/configuration.md Displays the formatted violation message when using the default configuration. Provides a comparison baseline for understanding the impact of custom configuration. ```text [length]: The value is invalid. ``` -------------------------------- ### Configure Validator with Custom Configuration in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/configuration.md Demonstrates creating a Configuration instance with custom violation message and root path, applying it to a Validator, and handling validation results. Shows how to override default behavior in the Akkurate validation library. ```kotlin val customConfig = Configuration { defaultViolationMessage = "The field contains an invalid value" rootPath("custom", "root", "path") } val validateString = Validator(customConfig) { length.constrain { false } } when (val result = validateString("foo")) { is ValidationResult.Success -> {} is ValidationResult.Failure -> { for ((message, path) in result.violations) { println("$path: $message") } } } ``` -------------------------------- ### Show Custom Configuration Validation Output Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/configuration.md Displays the formatted violation message when using a custom configuration. Shows how the custom root path and violation message appear in validation failure results. ```text [custom, root, path, length]: The field contains an invalid value ``` -------------------------------- ### Apply Basic Constraints in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/apply-constraints.md Demonstrates applying built-in constraints like isNotEmpty and hasLengthLowerThanOrEqualTo to a Validator for a Book data class. Requires the Akkurate library as a dependency. Takes a Validatable instance as input and outputs a constrained validation rule; limited to type-specific constraints for better type safety. ```kotlin @Validate data class Book(val title: String) Validator { title.isNotEmpty() } ``` ```kotlin Validator { title.hasLengthLowerThanOrEqualTo(50) } ``` -------------------------------- ### Fail on First Violation Configuration in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/apply-constraints.md Demonstrates how to enable failOnFirstViolation configuration option to stop validation after the first constraint violation. This optimizes performance in critical environments. ```kotlin val config = Configuration { failOnFirstViolation = true } val validateBook = Validator(config) { // ... } ``` -------------------------------- ### Compose validators for reusable validation logic Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/complex-structures.md Demonstrates how to define reusable validators and compose them within other validators to avoid code duplication. The example shows validating a Book class containing Person objects using a shared Person validator. ```kotlin @Validate data class Book(val author: Person, val reviewers: Set) @Validate data class Person(val fullName: String) val validatePerson = Validator { fullName.isNotEmpty() } val validateBook = Validator { author.validateWith(validatePerson) reviewers.each { validateWith(validatePerson) } } ``` -------------------------------- ### Add %product% Arrow Support Library Dependency Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/arrow-integration.md Add the %product%'s support library for Arrow to your Gradle build script to enable integration between %product% validation results and Arrow's functional types. This is a prerequisite for using the Arrow-specific conversion functions. ```kotlin implementation("dev.nesk.akkurate:akkurate-arrow:%version%") ``` -------------------------------- ### Create Basic Kotlin String Validators Source: https://context7.com/nesk/akkurate/llms.txt Demonstrates creating a basic validator for strings using Akkurate's DSL. It includes checks for emptiness, length, and regex matching. The results are handled as Success or Failure types. ```kotlin import dev.nesk.akkurate.Validator import dev.nesk.akkurate.constraints.builders.* // Define a simple string validator val validateUsername = Validator { isNotEmpty() otherwise { "Username cannot be empty" } hasLengthBetween(3..20) otherwise { "Username must be 3-20 characters" } isMatching(Regex("^[a-zA-Z0-9_]+$")) otherwise { "Username can only contain letters, numbers, and underscores" } } // Execute validation and handle results val result = validateUsername("john_doe") when (result) { is ValidationResult.Success -> println("Valid username: ${result.value}") is ValidationResult.Failure -> { result.violations.forEach { violation -> println("Error at ${violation.path}: ${violation.message}") } } } // Example with invalid data val failedResult = validateUsername("ab") // Output: Error at : Username must be 3-20 characters ``` -------------------------------- ### Add Akkurate Ktor Client Dependency Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-client-integration.md Add the Akkurate Ktor Client dependency to your Gradle build script to enable validation for Ktor client requests. This requires the Content Negotiation plugin. ```kotlin implementation("dev.nesk.akkurate:akkurate-ktor-client:%version%") ``` -------------------------------- ### Implement Book Data Access Object (DAO) with Exposed Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Creates a 'BookDao' class to interact with the 'Books' table using Exposed. Provides 'create' and 'list' methods for database operations within suspended transactions. ```kotlin import kotlinx.coroutines.Dispatchers import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction class BookDao(database: Database) { init { transaction(database) { SchemaUtils.create(Books) } } private suspend fun dbQuery(block: suspend () -> T): T = newSuspendedTransaction(Dispatchers.IO) { block() } private fun ResultRow.toBook() = Book( isbn = this[Books.isbn], title = this[Books.title], ) suspend fun create(book: Book): Unit = dbQuery { Books.insert { it[isbn] = book.isbn it[title] = book.title } } suspend fun list(): List = dbQuery { Books.selectAll().map { it.toBook() } } } ``` -------------------------------- ### Conditional Validation Based on Value in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/complex-structures.md Illustrates how to apply validation constraints conditionally based on the value of a property. This example checks if a maximum capacity is greater than zero before applying a size constraint to the book collection. It requires the data class to be annotated with @Validate and the Validator to be defined. ```kotlin @Validate data class Library(val books: Set, val maximumCapacity: Int) Validator { val (max) = maximumCapacity if (max > 0) { books.hasSizeLowerThanOrEqualTo(max) otherwise { "Too many books" } } } ``` -------------------------------- ### Validate API Response with Ktor Client Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-client-integration.md Demonstrates how to fetch release data using Ktor Client, define a validator for the response, and register it with the Akkurate plugin. If validation fails, a ValidationResult.Exception is thrown. ```kotlin @Validate @Serializable data class Release(val name: String) suspend fun main() { val client = HttpClient(CIO) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } } val apiUrl = "https://api.github.com/repos/nesk/akkurate/releases" val response: HttpResponse = client.get(apiUrl) val data: List = response.body() println(data) } val validateReleaseList = Validator> { each { name.isNotEmpty() } } install(Akkurate) { registerValidator(validateReleaseList) } try { val data: List = response.body() println(data) } catch (exception: ValidationResult.Exception) { System.err.println("Invalid response: ${exception.violations}") } ``` -------------------------------- ### Custom Path with Appended Method in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/apply-constraints.md Illustrates how to use withPath combined with appended to add components to the current path. The resulting path will be [author, fullName, a, b, c] where author.fullName is the original path. ```kotlin author.fullName.isNotEmpty() withPath { appended("a", "b", "c") } ``` -------------------------------- ### Setting Up Contextual Validator in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/use-external-sources.md This snippet demonstrates creating a validator that uses a UserDao context for username uniqueness validation. It builds on Akkurate's Validator class, requiring the UserDao context to be injected. Input is a UserUpdate object; output is validation results with custom error messages. It assumes synchronous DAO operations and does not handle exceptions from external sources. ```kotlin @Validate data class UserUpdate(val username: String) val validateUser = Validator { userDao -> // The context is passed as a parameter ^^^^^^^ val (isValidUsername) = username.hasLengthGreaterThanOrEqualTo(5) if (isValidUsername) { username.constrain { // Use the context wherever you want. !userDao.existsByUsername(it) } otherwise { "This username is already taken" } } } ``` -------------------------------- ### Define Exposed Database Schema for Books Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Sets up the 'Books' table schema using Exposed for database persistence. Defines 'isbn' as a primary key (char(13)) and 'title' (varchar(50)). ```kotlin import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.Table object Books : Table() { val isbn = char("isbn", length = 13) val title = varchar("title", length = 50) override val primaryKey = PrimaryKey(isbn) } ``` -------------------------------- ### Custom Path with Relative Method in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/apply-constraints.md Shows how to use withPath combined with relative to replace the property name with provided components. The resulting path will be [author, a, b, c] where author is the original property name. ```kotlin author.fullName.isNotEmpty() withPath { relative("a", "b", "c") } ``` -------------------------------- ### Custom Path with Absolute Method in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/apply-constraints.md Demonstrates how to use withPath combined with absolute to provide a custom path for constraint violations. The path will be [a, b, c] regardless of the actual property path. ```kotlin author.fullName.isNotEmpty() withPath { absolute("a", "b", "c") } ``` -------------------------------- ### Unprocessable Entity Response for Invalid Book Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-server-integration.md This JSON response indicates an HTTP 422 Unprocessable Entity status, returned when the book title validation fails. It details the specific validation error, including the field path and a descriptive message. ```json { "status": 422, "fields": [ { "message": "Must not be empty", "path": "title" } ], "type": "https://akkurate.dev/validation-error", "title": "The payload is invalid", "detail": "The payload has been successfully parsed, but the server is unable to accept it due to validation errors." } ``` -------------------------------- ### Bind %product% Validation Results in Arrow Raise Computation Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/arrow-integration.md Use the `bind` function within an Arrow `either` block to seamlessly handle %product% validation results. `bind` automatically short-circuits the computation upon encountering a validation failure, simplifying error propagation and allowing focus on the successful computation path. This is especially useful when validating multiple data structures sequentially. ```kotlin @Validate data class Book(val title: String) @Validate data class Author(val name: String) val validateBook = Validator { title.isNotEmpty() } val validateAuthor = Validator { name.isNotEmpty() } either { // Directly bind the validation results val book = bind(validateBook(Book("The Lord of the Rings"))) val author = bind(validateAuthor(Author("J.R.R. Tolkien"))) // Further computation with the validated data println("Validated book: $book") println("Validated author: $author") } ``` -------------------------------- ### Currying Validator for Context Reusability in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/use-external-sources.md This code illustrates currying a validator to fix the context, enabling multiple validations without repassing the context. It uses Kotlin's functional programming features and Akkurate's Validator. Input includes the context and multiple UserUpdate instances; it outputs validation results per call. Useful for batch processing but limited to non-suspendable contexts. ```kotlin val validateUserWithSomeDao = validateUser(someUserDao) val userUpdate1: UserUpdate = TODO() val userUpdate2: UserUpdate = TODO() validateUserWithSomeDao(userUpdate1) validateUserWithSomeDao(userUpdate2) ``` -------------------------------- ### Kotlin Data Class Validation with @Validate Annotation Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Demonstrates how to annotate a Kotlin data class with the `@Validate` annotation to enable validation. This requires the Akkurate dependency and defines a basic data class structure for a `Book` with `isbn` and `title` properties. ```kotlin import kotlinx.serialization.Serializable import akkurate.annotation.Validate @Validate @Serializable data class Book( val isbn: String = "", val title: String = "" ) ``` -------------------------------- ### cURL Request for Unprocessable Book Payload Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-server-integration.md This shell command sends a POST request to the Ktor server's '/books' endpoint with an empty 'title' field in the JSON payload. This request is expected to fail validation. ```shell curl 127.0.0.1:8080/books -v \ --header 'Content-Type: application/json' \ --data '{"title": ""}' ``` -------------------------------- ### Read Validation Results in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/apply-constraints.md Shows how to define a validator with nested constraints and handle ValidationResult outcomes, including success and failure cases. Depends on Akkurate for validation logic. Processes a Book object as input, outputting either the validated value or a list of violations; assumes valid data classes for nesting. ```kotlin @Validate data class Book(val title: String, val author: Author) @Validate data class Author(val fullName: String) val validateBook = Validator { title.isNotEmpty() author.fullName { isNotEmpty() isContaining(" ") // We expect at least a space in a full name. } } val book = Book(title = "", author = Author(fullName = "")) when (val result = validateBook(book)) { is ValidationResult.Success -> { println("The book is valid: ${result.value}") } is ValidationResult.Failure -> { println("The book is invalid, here are the errors:") for ((message, path) in result.violations) { println(" - $path: $message") } } } ``` -------------------------------- ### Kotlin Validator Implementation with ISBN and Title Constraints Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-validation-tutorial.md Shows how to create a `Validator` instance and define constraints for the `isbn` and `title` properties of the `Book` data class. The ISBN constraint uses a regular expression for validation and checks for uniqueness against a `BookDao`. The title constraint ensures it's not blank and has a maximum length. ```kotlin import akkurate.Validator import akkurate.validate import kotlin.text.Regex val validateBook = Validator.suspendable { dao -> isbn { val (isValidIsbn) = isMatching(Regex("\d{13}")) otherwise { "Must be a valid ISBN (13 digits)" if (isValidIsbn) { constrain { !dao.existsWithIsbn(it) } otherwise { "This ISBN is already registered" } } } title { isNotBlank() hasLengthLowerThanOrEqualTo(50) } } ``` -------------------------------- ### Setting Up Suspendable Validator in Kotlin Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/use-external-sources.md This creates a suspendable validator using Akkurate's Validator.suspendable for async context validation with UserApi. It requires coroutine support and a suspendable context. Inputs are UserApi and UserUpdate; it performs async checks. Must be called from suspend functions; handles async limitations but not network failures. ```kotlin val validateUser = Validator.suspendable { api -> username.constrain { !api.existsByUsername(it) } otherwise { "This username is already taken" } } ``` -------------------------------- ### Customize Akkurate Response in Ktor Source: https://github.com/nesk/akkurate/blob/main/documentation/topics/ktor-server-integration.md Demonstrates how to customize the response when validation fails in Ktor using the Akkurate plugin. You can set a custom HTTP status code and content type, or completely override the response builder to provide a tailored error payload. ```kotlin install(Akkurate) { status = HttpStatusCode.BadRequest contentType = ContentType.Application.Json } ``` ```kotlin install(Akkurate) { buildResponse { call, violations -> call.respond( HttpStatusCode.BadRequest, violations.byPath.toString() ) } } ```