Skip to content

Instantly share code, notes, and snippets.

@dmcg
Last active July 28, 2020 10:44
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/18bff338bb7e8c032bc3e574edf1a75a to your computer and use it in GitHub Desktop.
Save dmcg/18bff338bb7e8c032bc3e574edf1a75a to your computer and use it in GitHub Desktop.
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
class StringWrapperTests {
private val firstName = FirstName("Fred")
private val lastName = LastName("Flintstone")
@Test fun canAssignSameType() {
val doesCompile: FirstName = firstName
assertTrue(FirstName("Fred") == firstName)
assertEquals(FirstName("Fred"), firstName)
}
@Test fun cantAssignDifferentTypes() {
// val doesntCompile: FirstName = lastName
}
@Test fun canDoStringLikeThings() {
assertTrue(firstName.isNotBlank())
assertEquals("Fred Flintstone", "$firstName $lastName")
assertEquals("Fred Flintstone", firstName + " " + lastName)
}
@Test fun canValidate() {
assertTrue(firstName.validatesAs(FirstNameType))
assertFalse(FirstName("").validatesAs(FirstNameType))
assertEquals(FirstName("TBA"), FirstName("").validatedAs(FirstNameType) ?: FirstName("TBA"))
assertEquals(FirstName("TBA"), FirstName("").validatedAs(FirstNameType, "TBA"))
assertEquals(LastNameType.parse("Rubble"), LastName("Rubble"))
assertEquals(LastNameType.parse("", "TBA"), LastName("TBA"))
assertNull(LastNameType.parse(""))
// assertTrue(firstName.validatesAs(LastNameType)) // doesn't compile
}
}
interface Validator<T> {
fun isValid(value: String): Boolean
open class Of<T>(
private val factory: (String) -> StringWrapper<T>,
private val predicate: (String) -> Boolean
) : Validator<T> {
override fun isValid(value: String): Boolean = predicate(value)
fun parse(s: String): StringWrapper<T>? = factory(s).validatedAs(this)
fun parse(s: String, default: String): StringWrapper<T>? = factory(s).validatedAs(this, default)
}
}
object FirstNameType: Validator<FirstNameType> {
override fun isValid(value: String): Boolean = value.isNotBlank()
}
typealias FirstName = StringWrapper<FirstNameType>
typealias LastName = StringWrapper<LastNameType>
object LastNameType: Validator.Of<LastNameType>(::LastName, String::isNotBlank)
inline class StringWrapper<T>(override val value: String): CharSequenceWrapper, PlusMixin {
override fun toString() = value
fun validatesAs(validator: Validator<T>): Boolean =
validator.isValid(value)
fun validatedAs(validator: Validator<T>): StringWrapper<T>? =
if (validator.isValid(value)) this else null
fun validatedAs(validator: Validator<T>, default: String): StringWrapper<T> =
when {
validator.isValid(value) -> this
validator.isValid(default) -> StringWrapper<T>(default)
else -> error("Invalid default $default")
}
}
interface CharSequenceWrapper: CharSequence {
val value: CharSequence
override val length get() = value.length
override fun get(index: Int) = value[index]
override fun subSequence(startIndex: Int, endIndex: Int)
= value.subSequence(startIndex, endIndex)
}
interface PlusMixin {
val value: CharSequence
operator fun plus(other: CharSequence): String = value.toString() + other
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment