Parameter.kt

package com.hexagontk.shell

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

class Parameter<T : Any>(
    override val type: KClass<T>,
    val name: String,
    override val description: String? = null,
    override val regex: Regex? = null,
    override val optional: Boolean = true,
    override val multiple: Boolean = false,
    override val tag: String? = null,
    override val values: List<T> = emptyList(),
) : Property<T> {

    override val names: Set<String> = setOf(name)

    companion object {
        private val parameterRegex = "[a-z0-9\\-]{2,}".toRegex()
    }

    constructor(
        type: KClass<T>,
        name: String,
        description: String? = null,
        regex: Regex? = null,
        tag: String? = null,
        value: T,
    ) : this(type, name, description, regex, true, false, tag, listOf(value))

    constructor(
        type: KClass<T>,
        name: String,
        description: String? = null,
        regex: Regex? = null,
        tag: String? = null,
        values: List<T>,
    ) : this(type, name, description, regex, true, true, tag, values)

    init {
        check("Parameter", parameterRegex)
    }

    @Suppress("UNCHECKED_CAST") // Types checked at runtime
    override fun addValues(value: Property<*>): Property<T> =
        Parameter(
            type,
            name,
            description,
            regex,
            optional,
            multiple,
            tag,
            values + value.values as List<T>
        )

    override fun addValue(value: String): Parameter<T> =
        value.parseOrNull(type)
            ?.let {
                Parameter(type, name, description, regex, optional, multiple, tag, values + it)
            }
            ?: error("Parameter '$name' of type '${typeText()}' can not hold '$value'")

    override fun clearValues(): Parameter<T> =
        Parameter(type, name, description, regex, optional, multiple, tag, emptyList())

    // TODO Only used in tests
    fun copy(
        type: KClass<T> = this.type,
        name: String = this.name,
        description: String? = this.description,
        regex: Regex? = this.regex,
        optional: Boolean = this.optional,
        multiple: Boolean = this.multiple,
        tag: String? = this.tag,
        values: List<T> = this.values,
    ): Parameter<T> =
        Parameter(type, name, description, regex, optional, multiple, tag, values)

    // TODO Only used in tests
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as Parameter<*>

        if (type != other.type) return false
        if (name != other.name) return false
        if (description != other.description) return false
        if (regex != other.regex) return false
        if (optional != other.optional) return false
        if (multiple != other.multiple) return false
        if (tag != other.tag) return false
        if (values != other.values) return false

        return true
    }

    // TODO Only used in tests
    override fun hashCode(): Int {
        var result = type.hashCode()
        result = 31 * result + name.hashCode()
        result = 31 * result + (description?.hashCode() ?: 0)
        result = 31 * result + (regex?.hashCode() ?: 0)
        result = 31 * result + optional.hashCode()
        result = 31 * result + multiple.hashCode()
        result = 31 * result + (tag?.hashCode() ?: 0)
        result = 31 * result + values.hashCode()
        return result
    }
}