Skip to content

Commit

Permalink
Add hostname and idn-hostname format validators (#82)
Browse files Browse the repository at this point in the history
Related to #54
  • Loading branch information
OptimumCode authored Apr 15, 2024
1 parent 8900520 commit 6c2baf5
Show file tree
Hide file tree
Showing 43 changed files with 5,527 additions and 28 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ jobs:
check:
runs-on: ${{ inputs.run-on }}
steps:
- name: 'Install native dependencies'
run: sudo apt-get install -y libunistring-dev
if: runner.os == 'Linux'
- name: 'Checkout Repository'
uses: actions/checkout@v4
with:
Expand All @@ -45,6 +48,13 @@ jobs:
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache unicode data
uses: actions/cache@v4
with:
path: build/unicode_dump
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
restore-keys: |
unicode-dump-
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/platform-benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ jobs:
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache unicode data
uses: actions/cache@v4
with:
path: build/unicode_dump
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
restore-keys: |
unicode-dump-
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ jobs:
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache unicode data
uses: actions/cache@v4
with:
path: build/unicode_dump
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
restore-keys: |
unicode-dump-
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ jobs:
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache unicode data
uses: actions/cache@v4
with:
path: build/unicode_dump
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
restore-keys: |
unicode-dump-
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/snapshot_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ jobs:
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache unicode data
uses: actions/cache@v4
with:
path: build/unicode_dump
key: unicode-dump-${{ hashFiles('build/unicode_dump/*') }}
restore-keys: |
unicode-dump-
- name: Build with Gradle
uses: gradle/actions/setup-gradle@v3
with:
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ The library supports `format` assertion. For now only a few formats are supporte
* ipv4
* ipv6
* uuid
* hostname
* idn-hostname

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

When you build project for **linux** target you might get an error about missing native library.
This is because `de.cketti.unicode:kotlin-codepoints` requires this library to perform string normalization.
This is needed to support `idn-hostname` format. Install this library with the following command:

```bash
sudo apt-get install -y libunistring-dev
```

### Devcontainer

Devcontainers is a cool feature. However, by default in Codespaces and DevPod you will use [VS Code](https://code.visualstudio.com/).
Expand Down
162 changes: 161 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
import java.util.Locale

plugins {
alias(libs.plugins.kotlin.mutliplatform)
Expand All @@ -22,9 +23,109 @@ repositories {
}

apiValidation {
ignoredProjects += listOf("benchmark", "test-suites")
ignoredProjects += listOf("benchmark", "test-suites", "generator")
}

val generatedSourceDirectory: Provider<Directory> = layout.buildDirectory.dir("generated/source/unicode")

//region Generation tasks block
val generatorConfiguration: Configuration by configurations.creating

dependencies {
generatorConfiguration(project(":generator"))
}

val dumpDir: Provider<Directory> = layout.buildDirectory.dir("unicode_dump")

val dumpCharacterData by tasks.register<JavaExec>("dumpCharacterData") {
onlyIf {
dumpDir.get().asFile.run { !exists() || listFiles().isNullOrEmpty() }
}
outputs.dir(dumpDir)
classpath(generatorConfiguration)
mainClass.set("io.github.optimumcode.unocode.generator.Main")
args(
"dump",
"-o",
dumpDir.get(),
)
}

val generateCharacterDirectionData by tasks.register<JavaExec>("generateCharacterDirectionData") {
inputs.dir(dumpDir)
outputs.dir(generatedSourceDirectory)

dependsOn(dumpCharacterData)

classpath(generatorConfiguration)
mainClass.set("io.github.optimumcode.unocode.generator.Main")
args(
"character-direction",
"-p",
"io.github.optimumcode.json.schema.internal.unicode",
"-o",
generatedSourceDirectory.get(),
"-d",
dumpDir.get(),
)
}

val generateCharacterCategoryData by tasks.register<JavaExec>("generateCharacterCategoryData") {
inputs.dir(dumpDir)
outputs.dir(generatedSourceDirectory)

dependsOn(dumpCharacterData)

classpath(generatorConfiguration)
mainClass.set("io.github.optimumcode.unocode.generator.Main")
args(
"character-category",
"-p",
"io.github.optimumcode.json.schema.internal.unicode",
"-o",
generatedSourceDirectory.get(),
"-d",
dumpDir.get(),
)
}

val generateDerivedProperties by tasks.register<JavaExec>("generateDerivedProperties") {
val dataFile = layout.projectDirectory.dir("generator").dir("data").file("rfc5895_appendix_b_1.txt")
inputs.file(dataFile)
outputs.dir(generatedSourceDirectory)

classpath(generatorConfiguration)
mainClass.set("io.github.optimumcode.unocode.generator.Main")
args(
"derived-properties",
"-p",
"io.github.optimumcode.json.schema.internal.unicode",
"-o",
generatedSourceDirectory.get(),
"-d",
dataFile,
)
}

val generateJoiningTypes by tasks.register<JavaExec>("generateJoiningTypes") {
val dataFile = layout.projectDirectory.dir("generator").dir("data").file("DerivedJoiningType.txt")
inputs.file(dataFile)
outputs.dir(generatedSourceDirectory)

classpath(generatorConfiguration)
mainClass.set("io.github.optimumcode.unocode.generator.Main")
args(
"joining-types",
"-p",
"io.github.optimumcode.json.schema.internal.unicode",
"-o",
generatedSourceDirectory.get(),
"-d",
dataFile,
)
}
//endregion

kotlin {
explicitApi()

Expand Down Expand Up @@ -74,9 +175,18 @@ kotlin {

sourceSets {
commonMain {
kotlin.srcDirs(generatedSourceDirectory)

dependencies {
api(libs.kotlin.serialization.json)
implementation(libs.uri)
// When using approach like above you won't be able to add because block
implementation(libs.kotlin.codepoints.get().toString()) {
because("simplifies work with unicode codepoints")
}
implementation(libs.normalize.get().toString()) {
because("provides normalization required by IDN-hostname format")
}
}
}
commonTest {
Expand All @@ -94,6 +204,19 @@ kotlin {
}
}

targets.configureEach {
val capitalizedTargetName =
name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
tasks.named("compileKotlin$capitalizedTargetName") {
dependsOn(
generateCharacterDirectionData,
generateCharacterCategoryData,
generateDerivedProperties,
generateJoiningTypes,
)
}
}

afterEvaluate {
fun Task.dependsOnTargetTests(targets: List<KotlinTarget>) {
targets.forEach {
Expand Down Expand Up @@ -122,11 +245,48 @@ kotlin {
}
}

afterEvaluate {
val taskNames = setOf("compile", "detekt", "runKtlint")
tasks.configureEach {
// There is something wrong with compileCommonMainKotlinMetadata task
// Gradle cannot find it, but this task uses the generated source directory
// and Gradle reports implicit dependency.
// As a workaround I do this - seems like it is working.
// However, I might be missing something. Need to revisit this later.

if (taskNames.any { name.startsWith(it) }) {
mustRunAfter(
generateCharacterDirectionData,
generateCharacterCategoryData,
generateDerivedProperties,
generateJoiningTypes,
)
}
}
}

koverReport {
filters {
excludes {
packages(
"io.github.optimumcode.json.schema.internal.unicode.*",
"io.github.optimumcode.json.schema.internal.unicode",
)
}
}
}

ktlint {
version.set(libs.versions.ktlint)
reporters {
reporter(ReporterType.HTML)
}
filter {
exclude { el ->
val absolutePath = el.file.absolutePath
absolutePath.contains("generated").and(!el.isDirectory)
}
}
}

afterEvaluate {
Expand Down
52 changes: 52 additions & 0 deletions generator/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import com.expediagroup.graphql.plugin.gradle.config.GraphQLSerializer
import com.expediagroup.graphql.plugin.gradle.graphql
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType

plugins {
// otherwise there is Gradle exception
// https://github.com/gradle/gradle/issues/20084
id(libs.plugins.kotlin.jvm.get().pluginId)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.expediagroup.graphql)

alias(libs.plugins.detekt)
alias(libs.plugins.ktlint)
}

repositories {
mavenCentral()
}

kotlin {
jvmToolchain(11)
}

dependencies {
implementation(libs.kotlinpoet)
implementation(libs.graphql.ktor)
implementation(libs.clikt) {
because("cli for executing generation")
}
}

graphql {
client {
endpoint = "https://www.compart.com/en/unicode/graphql"
packageName = "io.github.optimumcode.unicode.generator.internal.graphql"
serializer = GraphQLSerializer.KOTLINX
}
}

ktlint {
version.set(libs.versions.ktlint)
debug.set(true)
reporters {
reporter(ReporterType.HTML)
}
filter {
exclude { el ->
val absolutePath = el.file.absolutePath
absolutePath.contains("generated").and(!el.isDirectory)
}
}
}
Loading

0 comments on commit 6c2baf5

Please sign in to comment.