Skip to content

Commit

Permalink
Add proper support for unicode characters
Browse files Browse the repository at this point in the history
  • Loading branch information
OptimumCode committed Jul 27, 2023
1 parent e62c586 commit 640f122
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.github.optimumcode.json.schema.ErrorCollector
import io.github.optimumcode.json.schema.ValidationError
import io.github.optimumcode.json.schema.internal.AssertionContext
import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion
import io.github.optimumcode.json.schema.internal.factories.string.util.codePointCount
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.contentOrNull
Expand All @@ -20,14 +21,15 @@ internal class LengthAssertion(
return true
}
val content = element.contentOrNull ?: return true
if (check(content.length, lengthValue)) {
val codePointCount = content.codePointCount()
if (check(codePointCount, lengthValue)) {
return true
}
errorCollector.onError(
ValidationError(
schemaPath = path,
objectPath = context.objectPath,
message = "string length (${content.length}) $errorMessage $lengthValue",
message = "string length ($codePointCount) $errorMessage $lengthValue",
),
)
return false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.github.optimumcode.json.schema.internal.factories.string.util

internal fun CharSequence.codePointCount(): Int {
val endIndex = length
var index = 0
var count = 0
while (index < endIndex) {
val firstChar = this[index]
index++
if (firstChar.isHighSurrogate() && index < endIndex) {
val nextChar = this[index]
if (nextChar.isLowSurrogate()) {
index++
}
}

count++
}

return count
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.github.optimumcode.json.pointer.JsonPointer
import io.github.optimumcode.json.schema.JsonSchema
import io.github.optimumcode.json.schema.ValidationError
import io.github.optimumcode.json.schema.base.KEY
import io.github.optimumcode.json.schema.internal.factories.string.util.codePointCount
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.collections.shouldHaveSize
Expand Down Expand Up @@ -32,6 +33,8 @@ class JsonSchemaMaxLengthValidationTest : FunSpec() {
"",
"V",
"",
"💩".repeat(20),
"💩",
)
for (str in validStrings) {
test("'$str' passes validation") {
Expand All @@ -46,6 +49,7 @@ class JsonSchemaMaxLengthValidationTest : FunSpec() {
"EFDzZMRawYGD9eNfknAUB",
"⌻ⲝ⣞ℤ⸍⠗⠜ↈ✋☧⾛✩ⓥ⇩⡽⚘\u20FC◭┐⥸⒗",
"⠺⪒⑸⋶⥠⇀⨑⨋ⅸ⥼\u245F⏇Ⓙⴷ⻘⢢≧\u20C8⬫⡜⸁",
"💩".repeat(21),
)
for (str in invalidStrings) {
test("'$str' does not pass validation") {
Expand All @@ -56,7 +60,7 @@ class JsonSchemaMaxLengthValidationTest : FunSpec() {
ValidationError(
schemaPath = JsonPointer("/maxLength"),
objectPath = JsonPointer.ROOT,
message = "string length (${str.length}) must be less or equal to 20",
message = "string length (${str.codePointCount()}) must be less or equal to 20",
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.github.optimumcode.json.pointer.JsonPointer
import io.github.optimumcode.json.schema.JsonSchema
import io.github.optimumcode.json.schema.ValidationError
import io.github.optimumcode.json.schema.base.KEY
import io.github.optimumcode.json.schema.internal.factories.string.util.codePointCount
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.collections.shouldHaveSize
Expand Down Expand Up @@ -31,6 +32,8 @@ class JsonSchemaMinLengthValidationTest : FunSpec() {
"JpEblYiJE57H70qGNXs",
"ⅵ┡\u243A⢻␀⁾⡪∛⫑⏽",
"Si1kaAhdpS",
"💩".repeat(11),
"💩".repeat(10),
)
for (str in validStrings) {
test("'$str' passes validation") {
Expand All @@ -47,6 +50,7 @@ class JsonSchemaMinLengthValidationTest : FunSpec() {
" ⍘↽♔⚪➕ⷰ➖",
"",
"",
"💩".repeat(9),
)
for (str in invalidStrings) {
test("'$str' does not pass validation") {
Expand All @@ -57,7 +61,7 @@ class JsonSchemaMinLengthValidationTest : FunSpec() {
ValidationError(
schemaPath = JsonPointer("/minLength"),
objectPath = JsonPointer.ROOT,
message = "string length (${str.length}) must be greater or equal to 10",
message = "string length (${str.codePointCount()}) must be greater or equal to 10",
),
)
}
Expand Down

0 comments on commit 640f122

Please sign in to comment.