Skip to content

Commit 6c2baf5

Browse files
authored
Add hostname and idn-hostname format validators (#82)
Related to #54
1 parent 8900520 commit 6c2baf5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+5527
-28
lines changed

.github/workflows/check.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ jobs:
2525
check:
2626
runs-on: ${{ inputs.run-on }}
2727
steps:
28+
- name: 'Install native dependencies'
29+
run: sudo apt-get install -y libunistring-dev
30+
if: runner.os == 'Linux'
2831
- name: 'Checkout Repository'
2932
uses: actions/checkout@v4
3033
with:
@@ -45,6 +48,13 @@ jobs:
4548
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
4649
restore-keys: |
4750
${{ runner.os }}-gradle-
51+
- name: Cache unicode data
52+
uses: actions/cache@v4
53+
with:
54+
path: build/unicode_dump
55+
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
56+
restore-keys: |
57+
unicode-dump-
4858
- name: Setup Gradle
4959
uses: gradle/actions/setup-gradle@v3
5060
with:

.github/workflows/platform-benchmark.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ jobs:
5757
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
5858
restore-keys: |
5959
${{ runner.os }}-gradle-
60+
- name: Cache unicode data
61+
uses: actions/cache@v4
62+
with:
63+
path: build/unicode_dump
64+
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
65+
restore-keys: |
66+
unicode-dump-
6067
- name: Setup Gradle
6168
uses: gradle/actions/setup-gradle@v3
6269
with:

.github/workflows/pull_request.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ jobs:
2929
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
3030
restore-keys: |
3131
${{ runner.os }}-gradle-
32+
- name: Cache unicode data
33+
uses: actions/cache@v4
34+
with:
35+
path: build/unicode_dump
36+
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
37+
restore-keys: |
38+
unicode-dump-
3239
- name: Setup Gradle
3340
uses: gradle/actions/setup-gradle@v3
3441
with:

.github/workflows/release.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ jobs:
4949
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
5050
restore-keys: |
5151
${{ runner.os }}-gradle-
52+
- name: Cache unicode data
53+
uses: actions/cache@v4
54+
with:
55+
path: build/unicode_dump
56+
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
57+
restore-keys: |
58+
unicode-dump-
5259
- name: Setup Gradle
5360
uses: gradle/actions/setup-gradle@v3
5461
with:

.github/workflows/snapshot_release.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ jobs:
3535
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
3636
restore-keys: |
3737
${{ runner.os }}-gradle-
38+
- name: Cache unicode data
39+
uses: actions/cache@v4
40+
with:
41+
path: build/unicode_dump
42+
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
43+
restore-keys: |
44+
unicode-dump-
3845
- name: Build with Gradle
3946
uses: gradle/actions/setup-gradle@v3
4047
with:

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ The library supports `format` assertion. For now only a few formats are supporte
342342
* ipv4
343343
* ipv6
344344
* uuid
345+
* hostname
346+
* idn-hostname
345347

346348
But there is an API to implement the user's defined format validation.
347349
The [FormatValidator](src/commonMain/kotlin/io/github/optimumcode/json/schema/ValidationError.kt) interface can be user for that.
@@ -389,6 +391,14 @@ You can see the results in the latest workflow execution.
389391
The update to Kotlin 1.9.22 came with an issue for JS incremental compilation.
390392
In case you see an error about main function that already bind please execute `clean` task.
391393

394+
When you build project for **linux** target you might get an error about missing native library.
395+
This is because `de.cketti.unicode:kotlin-codepoints` requires this library to perform string normalization.
396+
This is needed to support `idn-hostname` format. Install this library with the following command:
397+
398+
```bash
399+
sudo apt-get install -y libunistring-dev
400+
```
401+
392402
### Devcontainer
393403

394404
Devcontainers is a cool feature. However, by default in Codespaces and DevPod you will use [VS Code](https://code.visualstudio.com/).

build.gradle.kts

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
33
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
44
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests
55
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
6+
import java.util.Locale
67

78
plugins {
89
alias(libs.plugins.kotlin.mutliplatform)
@@ -22,9 +23,109 @@ repositories {
2223
}
2324

2425
apiValidation {
25-
ignoredProjects += listOf("benchmark", "test-suites")
26+
ignoredProjects += listOf("benchmark", "test-suites", "generator")
2627
}
2728

29+
val generatedSourceDirectory: Provider<Directory> = layout.buildDirectory.dir("generated/source/unicode")
30+
31+
//region Generation tasks block
32+
val generatorConfiguration: Configuration by configurations.creating
33+
34+
dependencies {
35+
generatorConfiguration(project(":generator"))
36+
}
37+
38+
val dumpDir: Provider<Directory> = layout.buildDirectory.dir("unicode_dump")
39+
40+
val dumpCharacterData by tasks.register<JavaExec>("dumpCharacterData") {
41+
onlyIf {
42+
dumpDir.get().asFile.run { !exists() || listFiles().isNullOrEmpty() }
43+
}
44+
outputs.dir(dumpDir)
45+
classpath(generatorConfiguration)
46+
mainClass.set("io.github.optimumcode.unocode.generator.Main")
47+
args(
48+
"dump",
49+
"-o",
50+
dumpDir.get(),
51+
)
52+
}
53+
54+
val generateCharacterDirectionData by tasks.register<JavaExec>("generateCharacterDirectionData") {
55+
inputs.dir(dumpDir)
56+
outputs.dir(generatedSourceDirectory)
57+
58+
dependsOn(dumpCharacterData)
59+
60+
classpath(generatorConfiguration)
61+
mainClass.set("io.github.optimumcode.unocode.generator.Main")
62+
args(
63+
"character-direction",
64+
"-p",
65+
"io.github.optimumcode.json.schema.internal.unicode",
66+
"-o",
67+
generatedSourceDirectory.get(),
68+
"-d",
69+
dumpDir.get(),
70+
)
71+
}
72+
73+
val generateCharacterCategoryData by tasks.register<JavaExec>("generateCharacterCategoryData") {
74+
inputs.dir(dumpDir)
75+
outputs.dir(generatedSourceDirectory)
76+
77+
dependsOn(dumpCharacterData)
78+
79+
classpath(generatorConfiguration)
80+
mainClass.set("io.github.optimumcode.unocode.generator.Main")
81+
args(
82+
"character-category",
83+
"-p",
84+
"io.github.optimumcode.json.schema.internal.unicode",
85+
"-o",
86+
generatedSourceDirectory.get(),
87+
"-d",
88+
dumpDir.get(),
89+
)
90+
}
91+
92+
val generateDerivedProperties by tasks.register<JavaExec>("generateDerivedProperties") {
93+
val dataFile = layout.projectDirectory.dir("generator").dir("data").file("rfc5895_appendix_b_1.txt")
94+
inputs.file(dataFile)
95+
outputs.dir(generatedSourceDirectory)
96+
97+
classpath(generatorConfiguration)
98+
mainClass.set("io.github.optimumcode.unocode.generator.Main")
99+
args(
100+
"derived-properties",
101+
"-p",
102+
"io.github.optimumcode.json.schema.internal.unicode",
103+
"-o",
104+
generatedSourceDirectory.get(),
105+
"-d",
106+
dataFile,
107+
)
108+
}
109+
110+
val generateJoiningTypes by tasks.register<JavaExec>("generateJoiningTypes") {
111+
val dataFile = layout.projectDirectory.dir("generator").dir("data").file("DerivedJoiningType.txt")
112+
inputs.file(dataFile)
113+
outputs.dir(generatedSourceDirectory)
114+
115+
classpath(generatorConfiguration)
116+
mainClass.set("io.github.optimumcode.unocode.generator.Main")
117+
args(
118+
"joining-types",
119+
"-p",
120+
"io.github.optimumcode.json.schema.internal.unicode",
121+
"-o",
122+
generatedSourceDirectory.get(),
123+
"-d",
124+
dataFile,
125+
)
126+
}
127+
//endregion
128+
28129
kotlin {
29130
explicitApi()
30131

@@ -74,9 +175,18 @@ kotlin {
74175

75176
sourceSets {
76177
commonMain {
178+
kotlin.srcDirs(generatedSourceDirectory)
179+
77180
dependencies {
78181
api(libs.kotlin.serialization.json)
79182
implementation(libs.uri)
183+
// When using approach like above you won't be able to add because block
184+
implementation(libs.kotlin.codepoints.get().toString()) {
185+
because("simplifies work with unicode codepoints")
186+
}
187+
implementation(libs.normalize.get().toString()) {
188+
because("provides normalization required by IDN-hostname format")
189+
}
80190
}
81191
}
82192
commonTest {
@@ -94,6 +204,19 @@ kotlin {
94204
}
95205
}
96206

207+
targets.configureEach {
208+
val capitalizedTargetName =
209+
name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
210+
tasks.named("compileKotlin$capitalizedTargetName") {
211+
dependsOn(
212+
generateCharacterDirectionData,
213+
generateCharacterCategoryData,
214+
generateDerivedProperties,
215+
generateJoiningTypes,
216+
)
217+
}
218+
}
219+
97220
afterEvaluate {
98221
fun Task.dependsOnTargetTests(targets: List<KotlinTarget>) {
99222
targets.forEach {
@@ -122,11 +245,48 @@ kotlin {
122245
}
123246
}
124247

248+
afterEvaluate {
249+
val taskNames = setOf("compile", "detekt", "runKtlint")
250+
tasks.configureEach {
251+
// There is something wrong with compileCommonMainKotlinMetadata task
252+
// Gradle cannot find it, but this task uses the generated source directory
253+
// and Gradle reports implicit dependency.
254+
// As a workaround I do this - seems like it is working.
255+
// However, I might be missing something. Need to revisit this later.
256+
257+
if (taskNames.any { name.startsWith(it) }) {
258+
mustRunAfter(
259+
generateCharacterDirectionData,
260+
generateCharacterCategoryData,
261+
generateDerivedProperties,
262+
generateJoiningTypes,
263+
)
264+
}
265+
}
266+
}
267+
268+
koverReport {
269+
filters {
270+
excludes {
271+
packages(
272+
"io.github.optimumcode.json.schema.internal.unicode.*",
273+
"io.github.optimumcode.json.schema.internal.unicode",
274+
)
275+
}
276+
}
277+
}
278+
125279
ktlint {
126280
version.set(libs.versions.ktlint)
127281
reporters {
128282
reporter(ReporterType.HTML)
129283
}
284+
filter {
285+
exclude { el ->
286+
val absolutePath = el.file.absolutePath
287+
absolutePath.contains("generated").and(!el.isDirectory)
288+
}
289+
}
130290
}
131291

132292
afterEvaluate {

generator/build.gradle.kts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import com.expediagroup.graphql.plugin.gradle.config.GraphQLSerializer
2+
import com.expediagroup.graphql.plugin.gradle.graphql
3+
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
4+
5+
plugins {
6+
// otherwise there is Gradle exception
7+
// https://github.com/gradle/gradle/issues/20084
8+
id(libs.plugins.kotlin.jvm.get().pluginId)
9+
alias(libs.plugins.kotlin.serialization)
10+
alias(libs.plugins.expediagroup.graphql)
11+
12+
alias(libs.plugins.detekt)
13+
alias(libs.plugins.ktlint)
14+
}
15+
16+
repositories {
17+
mavenCentral()
18+
}
19+
20+
kotlin {
21+
jvmToolchain(11)
22+
}
23+
24+
dependencies {
25+
implementation(libs.kotlinpoet)
26+
implementation(libs.graphql.ktor)
27+
implementation(libs.clikt) {
28+
because("cli for executing generation")
29+
}
30+
}
31+
32+
graphql {
33+
client {
34+
endpoint = "https://www.compart.com/en/unicode/graphql"
35+
packageName = "io.github.optimumcode.unicode.generator.internal.graphql"
36+
serializer = GraphQLSerializer.KOTLINX
37+
}
38+
}
39+
40+
ktlint {
41+
version.set(libs.versions.ktlint)
42+
debug.set(true)
43+
reporters {
44+
reporter(ReporterType.HTML)
45+
}
46+
filter {
47+
exclude { el ->
48+
val absolutePath = el.file.absolutePath
49+
absolutePath.contains("generated").and(!el.isDirectory)
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)