Last active
July 28, 2020 10:44
-
-
Save dmcg/18bff338bb7e8c032bc3e574edf1a75a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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