Skip to content

Commit

Permalink
Refactor context and allow destructring
Browse files Browse the repository at this point in the history
- Moving Context from ValidationBuilder to Validation
- This commit includes a failing test, destructuring doesn't work yet.
  • Loading branch information
lnhrdt committed May 12, 2024
1 parent 00b0c0b commit 96b7ea2
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 11 deletions.
35 changes: 33 additions & 2 deletions src/commonMain/kotlin/io/konform/validation/Validation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,46 @@ import io.konform.validation.internal.ValidationBuilderImpl

public interface Validation<T> {
public companion object {
public operator fun <T> invoke(init: ValidationBuilder<T>.() -> Unit): Validation<T> {
public operator fun <T> invoke(init: ValidationBuilder<T>.(Context<T>) -> Unit): Validation<T> {
val context = Context<T>()
val builder = ValidationBuilderImpl<T>()
return builder.apply(init).build()
init(builder, context)
val validation = builder.build()
return object : Validation<T> by validation {
override fun validate(value: T): ValidationResult<T> {
context.subject = value
return validation.validate(value)
}

override fun invoke(value: T): ValidationResult<T> {
return validate(value)
}
}
}
}

public fun validate(value: T): ValidationResult<T>

public operator fun invoke(value: T): ValidationResult<T> = validate(value)

public class Context<T> {
private var _subjectHolder: SubjectHolder<T>? = null

public var subject: T
get() {
return when (val subjectHolder = _subjectHolder) {
null -> throw IllegalStateException("Subject not initialized")
else -> subjectHolder.value
}
}
set(value) {
_subjectHolder = SubjectHolder(value)
}

public operator fun component1(): T = subject

private data class SubjectHolder<T>(val value: T)
}
}

public class Constraint<R> internal constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,6 @@ public abstract class ValidationBuilder<T> {
public abstract fun run(validation: Validation<T>)

public abstract val <R> KProperty1<T, R>.has: ValidationBuilder<R>

public val context: Context<T> get() = TODO("not implemented")

public class Context<T>(public val value: T)
}

public fun <T : Any> ValidationBuilder<T?>.ifPresent(init: ValidationBuilder<T>.() -> Unit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,29 @@ class ValidationBuilderTest {
}

@Test
fun validatingDependentFields() {
val nullableFieldValidation =
Validation<Register> {
val register = context.value
fun validatingFieldsWithContext() {
val fieldValidation =
Validation<Register> { context ->
Register::password {
addConstraint("cannot equal email") { it != context.subject.email }
}
}

Register(email = "sillyuser@test.com", password = "sillyuser@test.com")
.let { assertEquals(1, countErrors(fieldValidation(it), Register::password)) }
}

@Test
fun validatingFieldsWithDestructuredContext() {
val fieldValidation =
Validation<Register> { (register) ->
Register::password {
addConstraint("cannot equal email") { it != register.email }
}
}

Register(email = "sillyuser@test.com", password = "sillyuser@test.com")
.let { assertEquals(1, countErrors(nullableFieldValidation(it), Register::password)) }
.let { assertEquals(1, countErrors(fieldValidation(it), Register::password)) }
}

@Test
Expand All @@ -144,6 +156,17 @@ class ValidationBuilderTest {
Register(home = Address("")).let { assertEquals(1, countErrors(nestedTypeValidation(it), Register::home, Address::address)) }
}

@Test
fun validatingNullableValues() {
val nullableValueValidation =
Validation<String?> {
addConstraint("cannot be null") { it != null}
}

"poweruser@test.com".let { assertEquals(Valid(it), nullableValueValidation(it)) }
null.let { assertEquals(1, countErrors(nullableValueValidation(it))) }
}

@Test
fun validatingOptionalNullableValues() {
val nullableTypeValidation =
Expand Down

0 comments on commit 96b7ea2

Please sign in to comment.