
Ultra-fast, zero-dependency validation, parsing, formatting, and generation of Chilean RUT/RUN numbers with Modulo‑11 accuracy, zero‑allocation scanning, exhaustive error diagnostics, and real-time input masking.
rut-validator-kmp is an ultra-fast, zero-dependency, pure Kotlin Multiplatform (KMP) library for validating, parsing, formatting, and generating Chilean RUT / RUN (Rol Único Tributario / Rol Único Nacional) numbers.
Built adhering to the strictest Kotlin library best practices—including Explicit API mode, exhaustive sealed result hierarchies, zero-allocation character scanners, and binary stability guarantees.
| Platform | Target Identifier | Minimum Version / Architecture |
|---|---|---|
| JVM | jvm |
Java 11+ |
| Android | android |
API 24+ (Android 7.0+) |
| iOS |
iosArm64, iosSimulatorArm64
|
iOS 12+ (64-bit ARM & Simulator) |
| Linux | linuxX64 |
x86_64 Linux |
| WebAssembly | wasmJs |
Browser (Wasm GC) |
| JavaScript | js |
Browser (IR) |
commonMain.'0', 'K', and '1'..'9'.Rut value representation.RutValidationResult hierarchy returning specific failure reasons (EmptyInput, InvalidLength, InvalidCharacter, InvalidCheckDigit, InvalidNumber).DOTS_AND_HYPHEN (12.345.678-5), HYPHEN_ONLY (12345678-5), and UNFORMATTED (123456785).String, CharSequence, Long, and Int.Add the dependency to your commonMain source set:
// build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.ezer-mackenzie:rut-validator-kmp:1.0.1")
}
}
}implementation 'io.github.ezer-mackenzie:rut-validator-kmp:1.0.1'<dependency>
<groupId>io.github.ezer-mackenzie</groupId>
<artifactId>rut-validator-kmp</artifactId>
<version>1.0.1</version>
</dependency>import com.ezermackenzie.rut.isValidRut
// Direct extension call
val isValid = "12.345.678-5".isValidRut() // true
val isInvalid = "12.345.678-9".isValidRut() // false
// Handles unformatted, dotted, and lowercase 'k'
"21305614k".isValidRut() // true
" 12.345.678-5 ".isValidRut() // true (leading/trailing whitespace trimmed)import com.ezermackenzie.rut.Rut
import com.ezermackenzie.rut.toRut
import com.ezermackenzie.rut.toRutOrNull
// Throws RutParseException if invalid
val rut: Rut = "12.345.678-5".toRut()
println(rut.number) // 12345678
println(rut.checkDigit) // '5'
// Safe parsing returning null on error
val safeRut: Rut? = "invalid-rut".toRutOrNull() // nullFor user-facing forms, inspect the exact failure reason:
import com.ezermackenzie.rut.Rut
import com.ezermackenzie.rut.RutValidationResult
when (val result = Rut.validate("12.345.678-9")) {
is RutValidationResult.Valid -> {
println("Valid RUT: ${result.rut.formatted}")
}
is RutValidationResult.Invalid.InvalidCheckDigit -> {
println("Wrong DV! Expected ${result.expected}, got ${result.actual}")
}
is RutValidationResult.Invalid.InvalidCharacter -> {
println("Illegal character '${result.char}' at index ${result.index}")
}
is RutValidationResult.Invalid.InvalidLength -> {
println("RUT length is invalid: ${result.actualLength}")
}
is RutValidationResult.Invalid.EmptyInput -> {
println("Please provide a RUT.")
}
is RutValidationResult.Invalid.InvalidNumber -> {
println("Number error: ${result.reason}")
}
}import com.ezermackenzie.rut.Rut
import com.ezermackenzie.rut.RutFormatStyle
import com.ezermackenzie.rut.formatRut
val rut = Rut.parse("123456785")
println(rut.format(RutFormatStyle.DOTS_AND_HYPHEN)) // "12.345.678-5"
println(rut.formatted) // "12.345.678-5"
println(rut.format(RutFormatStyle.HYPHEN_ONLY)) // "12345678-5"
println(rut.canonical) // "12345678-5"
println(rut.format(RutFormatStyle.UNFORMATTED)) // "123456785"
println(rut.unformatted) // "123456785"
// Direct string extension
val canonical = "12.345.678-5".formatRut(RutFormatStyle.HYPHEN_ONLY) // "12345678-5"import com.ezermackenzie.rut.Rut
import com.ezermackenzie.rut.toRut
// Calculate Modulo 11 check digit for any positive Long
val checkDigit = Rut.calculateCheckDigit(12345678L) // '5'
val kDigit = Rut.calculateCheckDigit(6L) // 'K'
val zeroDigit = Rut.calculateCheckDigit(14L) // '0'
// Create Rut directly from number
val rutFromLong = 12345678L.toRut()
println(rutFromLong.formatted) // "12.345.678-5"
// Create Rut verifying given check digit
val customRut = Rut.of(12345678L, '5')Rut implements Comparable<Rut>, ordering instances numerically:
val r1 = Rut.parse("1-9")
val r2 = Rut.parse("14-0")
val r3 = Rut.parse("12.345.678-5")
val sorted = listOf(r3, r1, r2).sorted()
// Result: [1-9, 14-0, 12.345.678-5]Decompose any Rut instance into its numeric body and check digit:
val (number, checkDigit) = Rut.parse("12.345.678-5")
println(number) // 12345678
println(checkDigit) // '5'Extract unformatted digits and normalize 'k' to uppercase 'K' from dirty inputs:
import com.ezermackenzie.rut.cleanRut
val clean = " 12.345.678-k ".cleanRut() // "12345678K"
val direct = Rut.clean("12.345.678-5") // "123456785"Effortlessly format input in real time as users type in mobile or web forms:
import com.ezermackenzie.rut.formatPartialRut
"12345".formatPartialRut() // "1.234-5"
"123456785".formatPartialRut() // "12.345.678-5"
"21305614k".formatPartialRut() // "21.305.614-K"Generate mathematically valid RUTs for unit tests, mock databases, and seeding:
// Random RUT in standard range (1,000,000 to 99,999,999)
val randomRut = Rut.random()
println(randomRut.formatted) // e.g. "18.492.103-7"
// Custom number range
val smallRut = Rut.random(range = 100L..999L)Full API documentation is generated using Dokka:
# Generate HTML API docs in build/dokka/html
./gradlew dokkaGeneratePublicationHtmlFor more details on the API evolution, SemVer stability policy, and future milestones, read the Roadmap & API Stability Blueprint.
We welcome contributions! Please review:
Copyright 2026 Eli-ezer Reuven Ramirez Ruiz
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.
rut-validator-kmp is an ultra-fast, zero-dependency, pure Kotlin Multiplatform (KMP) library for validating, parsing, formatting, and generating Chilean RUT / RUN (Rol Único Tributario / Rol Único Nacional) numbers.
Built adhering to the strictest Kotlin library best practices—including Explicit API mode, exhaustive sealed result hierarchies, zero-allocation character scanners, and binary stability guarantees.
| Platform | Target Identifier | Minimum Version / Architecture |
|---|---|---|
| JVM | jvm |
Java 11+ |
| Android | android |
API 24+ (Android 7.0+) |
| iOS |
iosArm64, iosSimulatorArm64
|
iOS 12+ (64-bit ARM & Simulator) |
| Linux | linuxX64 |
x86_64 Linux |
| WebAssembly | wasmJs |
Browser (Wasm GC) |
| JavaScript | js |
Browser (IR) |
commonMain.'0', 'K', and '1'..'9'.Rut value representation.RutValidationResult hierarchy returning specific failure reasons (EmptyInput, InvalidLength, InvalidCharacter, InvalidCheckDigit, InvalidNumber).DOTS_AND_HYPHEN (12.345.678-5), HYPHEN_ONLY (12345678-5), and UNFORMATTED (123456785).String, CharSequence, Long, and Int.Add the dependency to your commonMain source set:
// build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.ezer-mackenzie:rut-validator-kmp:1.0.1")
}
}
}implementation 'io.github.ezer-mackenzie:rut-validator-kmp:1.0.1'<dependency>
<groupId>io.github.ezer-mackenzie</groupId>
<artifactId>rut-validator-kmp</artifactId>
<version>1.0.1</version>
</dependency>import com.ezermackenzie.rut.isValidRut
// Direct extension call
val isValid = "12.345.678-5".isValidRut() // true
val isInvalid = "12.345.678-9".isValidRut() // false
// Handles unformatted, dotted, and lowercase 'k'
"21305614k".isValidRut() // true
" 12.345.678-5 ".isValidRut() // true (leading/trailing whitespace trimmed)import com.ezermackenzie.rut.Rut
import com.ezermackenzie.rut.toRut
import com.ezermackenzie.rut.toRutOrNull
// Throws RutParseException if invalid
val rut: Rut = "12.345.678-5".toRut()
println(rut.number) // 12345678
println(rut.checkDigit) // '5'
// Safe parsing returning null on error
val safeRut: Rut? = "invalid-rut".toRutOrNull() // nullFor user-facing forms, inspect the exact failure reason:
import com.ezermackenzie.rut.Rut
import com.ezermackenzie.rut.RutValidationResult
when (val result = Rut.validate("12.345.678-9")) {
is RutValidationResult.Valid -> {
println("Valid RUT: ${result.rut.formatted}")
}
is RutValidationResult.Invalid.InvalidCheckDigit -> {
println("Wrong DV! Expected ${result.expected}, got ${result.actual}")
}
is RutValidationResult.Invalid.InvalidCharacter -> {
println("Illegal character '${result.char}' at index ${result.index}")
}
is RutValidationResult.Invalid.InvalidLength -> {
println("RUT length is invalid: ${result.actualLength}")
}
is RutValidationResult.Invalid.EmptyInput -> {
println("Please provide a RUT.")
}
is RutValidationResult.Invalid.InvalidNumber -> {
println("Number error: ${result.reason}")
}
}import com.ezermackenzie.rut.Rut
import com.ezermackenzie.rut.RutFormatStyle
import com.ezermackenzie.rut.formatRut
val rut = Rut.parse("123456785")
println(rut.format(RutFormatStyle.DOTS_AND_HYPHEN)) // "12.345.678-5"
println(rut.formatted) // "12.345.678-5"
println(rut.format(RutFormatStyle.HYPHEN_ONLY)) // "12345678-5"
println(rut.canonical) // "12345678-5"
println(rut.format(RutFormatStyle.UNFORMATTED)) // "123456785"
println(rut.unformatted) // "123456785"
// Direct string extension
val canonical = "12.345.678-5".formatRut(RutFormatStyle.HYPHEN_ONLY) // "12345678-5"import com.ezermackenzie.rut.Rut
import com.ezermackenzie.rut.toRut
// Calculate Modulo 11 check digit for any positive Long
val checkDigit = Rut.calculateCheckDigit(12345678L) // '5'
val kDigit = Rut.calculateCheckDigit(6L) // 'K'
val zeroDigit = Rut.calculateCheckDigit(14L) // '0'
// Create Rut directly from number
val rutFromLong = 12345678L.toRut()
println(rutFromLong.formatted) // "12.345.678-5"
// Create Rut verifying given check digit
val customRut = Rut.of(12345678L, '5')Rut implements Comparable<Rut>, ordering instances numerically:
val r1 = Rut.parse("1-9")
val r2 = Rut.parse("14-0")
val r3 = Rut.parse("12.345.678-5")
val sorted = listOf(r3, r1, r2).sorted()
// Result: [1-9, 14-0, 12.345.678-5]Decompose any Rut instance into its numeric body and check digit:
val (number, checkDigit) = Rut.parse("12.345.678-5")
println(number) // 12345678
println(checkDigit) // '5'Extract unformatted digits and normalize 'k' to uppercase 'K' from dirty inputs:
import com.ezermackenzie.rut.cleanRut
val clean = " 12.345.678-k ".cleanRut() // "12345678K"
val direct = Rut.clean("12.345.678-5") // "123456785"Effortlessly format input in real time as users type in mobile or web forms:
import com.ezermackenzie.rut.formatPartialRut
"12345".formatPartialRut() // "1.234-5"
"123456785".formatPartialRut() // "12.345.678-5"
"21305614k".formatPartialRut() // "21.305.614-K"Generate mathematically valid RUTs for unit tests, mock databases, and seeding:
// Random RUT in standard range (1,000,000 to 99,999,999)
val randomRut = Rut.random()
println(randomRut.formatted) // e.g. "18.492.103-7"
// Custom number range
val smallRut = Rut.random(range = 100L..999L)Full API documentation is generated using Dokka:
# Generate HTML API docs in build/dokka/html
./gradlew dokkaGeneratePublicationHtmlFor more details on the API evolution, SemVer stability policy, and future milestones, read the Roadmap & API Stability Blueprint.
We welcome contributions! Please review:
Copyright 2026 Eli-ezer Reuven Ramirez Ruiz
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.