Skip to content

Instantly share code, notes, and snippets.

@dmcg
Created November 27, 2020 14:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dmcg/934263fb0fd5ca6d9708939b90b9d599 to your computer and use it in GitHub Desktop.
Save dmcg/934263fb0fd5ca6d9708939b90b9d599 to your computer and use it in GitHub Desktop.
import java.math.BigDecimal
fun main() {
val s: StringValue = StringValue.of("hello")
StringValue("") // ok as no validation
val nes: NonEmptyStringValue? = NonEmptyStringValue.of("hello") // nullable as validated
// private as validated, would throw if not private
// NonEmptyStringValue("")
val accountNumber: AccountNumber? = AccountNumber.of("dd")
// private as @Hidden, would not throw if not private
// AccountNumber("")
}
data class StringValue constructor(val value: String) {
companion object : Factory<StringValue, String>
by factory(::StringValue)
}
data class MaskedStringValue constructor(val value: String) {
companion object : Factory<StringValue, String>
by factory(::StringValue, renderer = ::masked)
override fun toString() = render(value)
}
data class NonEmptyStringValue private constructor(val value: String) {
companion object : ValidatingFactory<NonEmptyStringValue?, String>
by factory(::NonEmptyStringValue).validatedBy(String::isNotBlank)
init { validateOrThrow(value) }
}
data class PositiveIntValue private constructor(val value: Int) {
companion object : ValidatingFactory<PositiveIntValue?, Int>
by factory(::PositiveIntValue, String::toInt).validatedBy( { it > 0 } )
init { validateOrThrow(value) }
}
inline class AccountNumber @Hidden constructor(val value: String) {
@OptIn(Hidden::class)
companion object : ValidatingFactory<AccountNumber?, String>
by factory(::AccountNumber, renderer = ::masked).validatedBy(String::isNotBlank)
override fun toString() = render(value)
}
class MaskedPositiveBigDecimalValue(value: BigDecimal) : Value<BigDecimal>(value, renderer = ::masked) {
companion object : ValidatingFactory<MaskedPositiveBigDecimalValue?, BigDecimal>
by factory(::MaskedPositiveBigDecimalValue, String::toBigDecimal)
.validatedBy( { it > BigDecimal.ZERO } )
}
fun <PRIMITIVE> masked(v: PRIMITIVE) = "*".repeat(v.toString().length)
open class Value<PRIMITIVE>(
val value: PRIMITIVE,
val renderer: (PRIMITIVE) -> String = defaultRenderer
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Value<*>
if (value != other.value) return false
return true
}
override fun hashCode(): Int = value?.hashCode() ?: 0
override fun toString(): String = renderer(value)
}
interface Factory<DOMAIN, PRIMITIVE> {
fun of(v: PRIMITIVE): DOMAIN
fun parse(s: String): DOMAIN?
fun render(v: PRIMITIVE): String
}
interface ValidatingFactory<DOMAIN, PRIMITIVE> : Factory<DOMAIN, PRIMITIVE> {
fun validate(v: PRIMITIVE): Boolean
}
fun <PRIMITIVE> ValidatingFactory<*, PRIMITIVE>.validateOrThrow(v: PRIMITIVE) = require(validate(v))
fun <DOMAIN, PRIMITIVE> Factory<DOMAIN, PRIMITIVE>.validatedBy(
validation: (PRIMITIVE) -> Boolean
): ValidatingFactory<DOMAIN?, PRIMITIVE> =
object:
ValidatingFactory<DOMAIN?, PRIMITIVE>,
Factory<DOMAIN?, PRIMITIVE> by this@validatedBy as Factory<DOMAIN?, PRIMITIVE> {
override fun of(v: PRIMITIVE): DOMAIN? = if (validate(v)) this@validatedBy.of(v) else null
override fun validate(v: PRIMITIVE) = validation(v)
}
fun <DOMAIN, PRIMITIVE> factory(
f: (PRIMITIVE) -> DOMAIN,
parser: (String) -> PRIMITIVE,
renderer: (PRIMITIVE) -> String = defaultRenderer
): Factory<DOMAIN, PRIMITIVE> =
object : Factory<DOMAIN, PRIMITIVE> {
override fun of(v: PRIMITIVE) = f(v)
override fun parse(s: String): DOMAIN? =
try {
of(parser(s))
} catch (e: Exception) {
null
}
override fun render(v: PRIMITIVE) = renderer(v)
}
fun <DOMAIN> factory(
f: (String) -> DOMAIN,
renderer: (String) -> String = { it }
): Factory<DOMAIN, String> = factory(f, renderer){ it }
val defaultRenderer = Any?::toString
@RequiresOptIn
annotation class Hidden
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment