1+ package com.papsign.ktor.openapigen.annotations.type.string.pattern
2+
3+ import com.papsign.ktor.openapigen.classLogger
4+ import com.papsign.ktor.openapigen.getKType
5+ import com.papsign.ktor.openapigen.model.schema.SchemaModel
6+ import com.papsign.ktor.openapigen.schema.processor.SchemaProcessor
7+ import com.papsign.ktor.openapigen.validation.Validator
8+ import com.papsign.ktor.openapigen.validation.ValidatorBuilder
9+ import java.lang.Exception
10+ import kotlin.reflect.KType
11+ import kotlin.reflect.full.withNullability
12+
13+ abstract class RegularExpressionConstraintProcessor <A : Annotation >(): SchemaProcessor<A>, ValidatorBuilder<A> {
14+
15+ private val log = classLogger()
16+
17+ val types = listOf (getKType<String >().withNullability(true ), getKType<String >().withNullability(false ))
18+
19+ abstract fun process (model : SchemaModel .SchemaModelLitteral <* >, annotation : A ): SchemaModel .SchemaModelLitteral <* >
20+
21+ abstract fun getConstraint (annotation : A ): RegularExpressionConstraint
22+
23+ private class RegularExpressionConstraintValidator (private val constraint : RegularExpressionConstraint ): Validator {
24+ override fun <T > validate (subject : T ? ): T ? {
25+ if (subject is String? ) {
26+ if (subject == null || ! constraint.pattern.toRegex().containsMatchIn(subject)) {
27+ throw RegularExpressionConstraintViolation (subject, constraint)
28+ }
29+ } else {
30+ throw NotAStringViolation (subject)
31+ }
32+ return subject
33+ }
34+ }
35+
36+ override fun build (type : KType , annotation : A ): Validator {
37+ return if (types.contains(type)) {
38+ RegularExpressionConstraintValidator (getConstraint(annotation))
39+ } else {
40+ error(" ${annotation::class } can only be used on types: $types " )
41+ }
42+ }
43+
44+ override fun process (model : SchemaModel <* >, type : KType , annotation : A ): SchemaModel <* > {
45+ return if (model is SchemaModel .SchemaModelLitteral <* > && types.contains(type)) {
46+ process(model, annotation)
47+ } else {
48+ log.warn(" ${annotation::class } can only be used on types: $types " )
49+ model
50+ }
51+ }
52+ }
53+
54+ data class RegularExpressionConstraint (val pattern : String , val errorMessage : String? = null )
55+
56+ open class ConstraintViolation (message : String , cause : Throwable ? = null ): Exception(message, cause)
57+
58+ class RegularExpressionConstraintViolation (val actual : String? , val constraint : RegularExpressionConstraint ): ConstraintViolation(constraint.errorMessage ? : " Constraint violation: the string " +
59+ " \' $actual \' does not match the regular expression ${constraint.pattern} " )
60+
61+ class NotAStringViolation (val value : Any? ): ConstraintViolation(" Constraint violation: $value is not a string" )
0 commit comments