Skip to content

Latest commit

 

History

History
229 lines (202 loc) · 5.82 KB

README.md

File metadata and controls

229 lines (202 loc) · 5.82 KB

logo

Kotlin Validator

validate your inputs or objects with power of the kotlin typesafe builders

Kotlin Validator Jitpack

Usage

quick introduction

val result=email.validate("yourEmail@gmail.com")
if(result.isValid){
    println("provided email is valid ,user:${result.user},host:${result.host}")
}else{
    println(result.reason!!.translate())
}

or

val result=compositeRule<User>{
    User::email mustBe email
    User::password mustBe inRange(8..64)
}.validate(user)
if(!result.isValid){
    println(result[User::email]?.translate())
    println(result[User::password]?.translate())
}

Setup

Dependency

put this into your gradle script

repositories {
    //...
    maven { url "https://jitpack.io" }
}
dependencies {
    //...
    //android usage
    implementation 'com.github.amir1376.kotlin-validator:android:$version'
    
    //core (jvm)
    implementation 'com.github.amir1376.kotlin-validator:core:$version'
}

Android

in your app entry point initialize validator translation

ValidatedTranslation.initDefaultAndroidAdapter(context)
    //include default translations
    .applyDefaultTranslations()
    

JVM

before any use of translation provide a TranslationAdapter here is the default adapter

ValidatedTranslation.initWithDefault()

Features

Combining rules together

you can validate your input by multiple rules

Here is an example

val result=(email or empty).validate(input)

Validate nested objects

sometimes you want to validate a model the library has support this too for example you have the following models

    data class User(
        val login:String,
        val name:Name,
        val password:String,
        val confirmPassword:String,
        val gender:String
    )
    data class Name(
        val first:String,
        val last:String,
    )
    enum class Gender{
        Male,Female
    }

you can validate this with this rule

//inside a suspend function
val userValidation=compositeRule<User>{
    User::login mustBe (startWithEnglishCharacter and inRange(6..64)) 
    User::name mustBe compositeRule<Name>{
        Name::first mustBe notEmpty
        Name::last mustBe notEmpty
    }
    User::password mustBe (
            containsAtLeastLowerCase(1) and
            containsAtLeastUpperCase(1) and 
            (containsAtLeastNumber(1)) and 
            inRange(8..64)
            )
    User::confirmPassword mustBe sameAs(User::password)
    User::gender mustBe oneOf<Gender>()
}
//user input
val user:User/*retrieve user object*/
val result=userValidation.validate(user)
if(!result.isValid){
//    you can get reason for each property
    result[User::login] 
}

as you can see in the above code, user password has a complex rule , but you can extract it to a variable and because these rules are stateless (they don't store any reference of input) you can safely use this combination multiple times

val strongPassword = containsAtLeastLowerCase(1) and
        containsAtLeastUpperCase(1) and
        (containsAtLeastNumber(1)) and
        inRange(8..64)
            //.... then replace
        User::password mustBe strongPassword

Customization

Creating your own rules

Of course, you can create your own rules with ease here is an example

val phone get() = rule<String>{ input->
    if(phonePattern.matchEntire(input)){
        thenValid()
    }else{
        because("your provided phone number is not valid")
    }
}

Localization

if your app has support of multiple language when building your rules ,you have to provide Reason instead of raw string

object PhoneInvalidReason:SingleReason
val phone get() = rule<String>{ input->
    if(phonePattern.matchEntire(input)){
        thenValid()
    }else{
        because(PhoneInvalidReason)
    }
}

then you have to provide PhoneInvalidReason translation to the adapter

ValidatedTranslation.adapter.apply{
    //declare translation here
    //this is up to you that how you want to translate that message
    PhoneInvalidReason::class providedBy {
        "your provided phone number is not valid"
    }
}

otherwise, if you are not interested on default translation approach, then you can create your own translation by implementing ValidatedTranslationAdapter

Android support

at the moment ,we have separated android module that contains an Android translation adapter, it has some useful extensions to provide translation from string resources

Validator.android().apply{
    //declare translation here
    //this is up to you that how you want to translate that message
    PhoneInvalidReason::class providedByResource (R.string.my_validation_phone_invalid)
}

if you use this library for android, you can use default translation provided by the android module.

currently supported languages are

  • English (default)
  • Persian

Coroutines support

the validate method is a suspend function accordingly, rules are all suspend functions too, so you can have suspended calls on them and because of that, you have to call validate only in a coroutine scope the core artifact doesn't have any suspend calls this aproach is choosen for support coroutines

Attention

this library is still under beta so that may have bugs

Contribution

you can consider a pull request, if you see unexpected behaviors in the library or write more common plugins

otherwise, if you have suggestions or have seen something weird out there (😁), please submit an issue

TODOS

  • write tests
  • write more plugins
  • support kotlin multiplatform