All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.github.lyxnx.util.Validator.kt Maven / Gradle / Ivy

There is a newer version: 1.6.1
Show newest version
package io.github.lyxnx.util

import kotlin.jvm.JvmField
import kotlin.jvm.JvmStatic
import kotlin.reflect.KClass

/**
 * A validator that takes [T] as the input to validate, and returns [R] as its validation result
 *
 * @param T the input to validate
 * @param R the result from validating
 */
public fun interface TypeValidator {
    /**
     * Validates [input], returning [R] as its result
     */
    public fun validate(input: T?): R
}

/**
 * An implementation of a [TypeValidator] that takes a string as its input and returns some result, [R]
 */
public fun interface StringInputValidator : TypeValidator

/**
 * Represents the result of an input validation
 */
public interface ValidationResult {
    /**
     * Whether the result is a valid, successful result
     */
    public val isValid: Boolean
}

/**
 * Used to validate an email input
 */
public open class EmailValidator : StringInputValidator {

    public companion object {
        @JvmField
        public val EMAIL_PATTERN: Regex =
            "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}\\@[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}(\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25})+".toRegex()
    }

    /**
     * Represents the result of an email validation
     */
    public enum class Result : ValidationResult {

        /**
         * The result of an input being [null or blank][String.isNullOrBlank]
         */
        EMPTY,

        /**
         * The result of an input being correct but not a valid email
         */
        INVALID,

        /**
         * The result of an input being a correctly formatted email
         */
        VALID;

        override val isValid: Boolean get() = this == VALID
    }

    override fun validate(input: String?): Result =
        when {
            input.isNullOrBlank() -> Result.EMPTY
            !EMAIL_PATTERN.matches(input) -> Result.INVALID
            else -> Result.VALID
        }

}

/**
 * Used to validate that a number is in `minimum ≤ x ≤ maximum`
 *
 * @param T required number type
 * @property type number class type
 * @property minimum minimum value the input can be
 * @property maximum maximum value the input can be
 */
public open class NumberValidator @PublishedApi internal constructor(
    private val type: KClass,
    private val minimum: T,
    private val maximum: T
) : StringInputValidator where T : Number, T : Comparable {

    public companion object {

        /**
         * Used to validate that a number is in `minimum ≤ x ≤ maximum`
         *
         * @param minimum minimum value the input can be
         * @param maximum maximum value the input can be
         */
        public inline operator fun  invoke(
            minimum: T,
            maximum: T
        ): NumberValidator where T : Number, T : Comparable = NumberValidator(
            T::class,
            minimum,
            maximum
        )

        /**
         * Used to validate that an integer is in `minimum ≤ x ≤ maximum`
         */
        @JvmStatic
        public fun of(minimum: Int, maximum: Int): NumberValidator = NumberValidator(minimum, maximum)

        /**
         * Used to validate that a double is in `minimum ≤ x ≤ maximum`
         */
        @JvmStatic
        public fun of(minimum: Double, maximum: Double): NumberValidator = NumberValidator(minimum, maximum)

        /**
         * Used to validate that a float is in `minimum ≤ x ≤ maximum`
         */
        @JvmStatic
        public fun of(minimum: Float, maximum: Float): NumberValidator = NumberValidator(minimum, maximum)

        /**
         * Used to validate that a long is in `minimum ≤ x ≤ maximum`
         */
        @JvmStatic
        public fun of(minimum: Long, maximum: Long): NumberValidator = NumberValidator(minimum, maximum)

        /**
         * Used to validate that a short is in `minimum ≤ x ≤ maximum`
         */
        @JvmStatic
        public fun of(minimum: Short, maximum: Short): NumberValidator = NumberValidator(minimum, maximum)

        /**
         * Used to validate that a byte is in `minimum ≤ x ≤ maximum`
         */
        @JvmStatic
        public fun of(minimum: Byte, maximum: Byte): NumberValidator = NumberValidator(minimum, maximum)
    }

    /**
     * Represents the result of number validation
     */
    public enum class Result : ValidationResult {

        /**
         * The result of an input being [null or blank][String.isNullOrBlank]
         */
        EMPTY,

        /**
         * The result of an input being being correct but not a number
         */
        NAN,

        /**
         * The result of an input being being correct but less than [minimum]
         */
        TOO_SMALL,

        /**
         * The result of an input being correct but greater than [maximum]
         */
        TOO_HIGH,

        /**
         * The result of an input being a number in the range `minimum ≤ x ≤ maximum`
         */
        VALID;

        override val isValid: Boolean get() = this == VALID
    }

    @Suppress("UNCHECKED_CAST")
    override fun validate(input: String?): Result {
        if (input.isNullOrBlank()) {
            return Result.EMPTY
        }

        val parsed = when (type) {
            Int::class -> input.toIntOrNull() as T?
            Double::class -> input.toDoubleOrNull() as T?
            Float::class -> input.toFloatOrNull() as T?
            Long::class -> input.toLongOrNull() as T?
            Short::class -> input.toShortOrNull() as T?
            Byte::class -> input.toByteOrNull() as T?
            else -> throw IllegalStateException("Unsupported type: $type")
        } ?: return Result.NAN

        if (parsed < minimum) {
            return Result.TOO_SMALL
        }

        if (parsed > maximum) {
            return Result.TOO_HIGH
        }

        return Result.VALID
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy