Open y9vad9 opened 2 years ago
I don't see any variants for wrapping concretely value
(with saving interface implementation for mutable & immutable realizations) via ksp
so I suggest next variant:
@ImmutableImpl
interface Foo {
val value: String
@Validator(propertyName = "value")
val validator: Validator<String> get() = StringLengthValidator(1..99)
}
Minuses I see:
abstract class
and protected
modifier (requires #3)Also, with same idea we can make companion object with next view:
@ImmutableImpl
interface Foo {
val value: String
@ValidatorsStorage
private companion object Validation {
val value: Validatable<String> = Validatable(property = Foo::value, validator = StringLengthValidator(0..99))
}
}
finally, I propose next variant:
/**
* Validator contract:
* - Validator always should be object.
*/
interface Validator<T> {
/**
* Checks [value] for correctness.
* If return value is `false` — implier will throw an exception.
*/
fun validate(value: T): Boolean
}
annotation class Validates<T>(val validator: KClass<Validator<T>>)
object DigitStringValidator : Validator<String> {
override fun validate(value: String): Boolean {
return value.all { it.isDigit() }
}
}
@ImmutableImpl
interface Entity {
@Validates(DigitStringValidator::class)
val value: String
}
Minuse I see is impossibility to provide some additional parameters to validator. But I think it can be avoided in next way:
abstract class IntValueValidator(val min: Int, val max: Int) : Validator<Int> {
override fun validate(value: Int): Boolean {
return value >= min && value =< max
}
companion object Month : IntValueValidator(1, 12)
companion object Hour24 : IntValueValidator(0, 23)
// etc
}
However it still not a case for some situations, but I don't see any other possible solutions.
Anyway, we can use init {}
block in abstract classes to validate the information.
Also, we can provide annotation that will provide safe way to institiate an object:
@Immutable
@SafeFactoryImpl
interface Entity {
@Validates(EmailValidator::class)
val email: String
@Validates(StringLengthValidator.FirstName::class)
val firstName: String
}
// generates
sealed interface EntityCreationResult {
object EmailIsInvalid : EntityCreationResult
object FirstNameIsInvalid : EntityCreationResult
class Success(val value: Entity) : EntityCreationResult
}
fun Entity(email: String, firstName: String): EntityCreationResult {
if(!EmailValidator.validate(email))
return EntityCreationResult.EmailIsInvalid
if(!StringLengthValidator.FirstName.validate(firstName))
return EntityLengthValidator.FirstNameIsInvalid
return EmailCreationResult.Success(ImmutableEntity(email, firstName))
}
The most complex thing here is how to design validators. I see three ways:
(Not convenient for me, but still the way) design it with notations and annotations:
Transformed to →
Transformed to →
Originally posted by @y9san9 in https://github.com/y9vad9/implier/issues/1#issuecomment-984492558