Property.kt

package com.hexagontk.shell

import com.hexagontk.core.text.parsedClasses
import kotlin.reflect.KClass

/**
 * TODO
 *
 * Properties passed by the command line.
 *
 * @param T
 */
sealed interface Property<T : Any> {
    val type: KClass<T>
    val names: Set<String>
    val description: String?
    val regex: Regex?
    val optional: Boolean
    val multiple: Boolean
    /**
     * TODO To group properties (for listing or alternatives). I.e.:
     *  '|alternate|' for grouping options that are exclusive or 'Common Options' to group in help
     */
    val tag: String?
    val values: List<T>

    fun addValues(value: Property<*>): Property<T>

    fun addValue(value: String): Property<T>

    fun clearValues(): Property<T>

    fun typeText(): String =
        type.simpleName ?: error("Unknown type name")

    fun check(component: String, namePattern: Regex) {
        require(type in parsedClasses) {
            val types = parsedClasses.map(KClass<*>::simpleName)
            "Type ${type.simpleName} not in allowed types: $types"
        }
        require(names.isNotEmpty()) { "$component must have a name" }
        require(names.all { it.matches(namePattern) }) {
            "Names must comply with ${namePattern.pattern} regex: $names"
        }
        require(regex == null || type == String::class) {
            "$component regex can only be used for 'string' type: ${type.simpleName}"
        }

        require(description?.isNotBlank() ?: true) { "$component description can not be blank" }

        if (regex != null)
            values.forEach {
                require(regex?.matches(it as String) ?: true) {
                    "Value should match the '${regex?.pattern}' regex: $it"
                }
            }

        if (!multiple)
            require(values.size <= 1) {
                "$component '${names.first()}' can only have one value: $values"
            }
    }

    companion object {
        val VERSION: Flag = Flag('v', "version", "Show the program's version along its description")
        val HELP: Flag = Flag('h', "help", "Display detailed information on running this command")
    }
}