diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 8a90dc0..87a9233 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -46,4 +46,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew sonar --info -Dsonar.verbose=true + run: ./gradlew sonar --info diff --git a/app/build.gradle.kts b/app/build.gradle.kts deleted file mode 100644 index 53beb10..0000000 --- a/app/build.gradle.kts +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com) - * Copyright 2022-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import org.jetbrains.dokka.gradle.DokkaTask - -plugins { - buildsrc.conventions.lang.`kotlin-jvm` - buildsrc.conventions.publishing - buildsrc.conventions.sonarqube - id("application") - id("com.github.ben-manes.versions") -} - -description = "A simple defensive application to encode/decode URL components" - -val deployDir = project.layout.projectDirectory.dir("deploy") -val urlEncoderMainClass = "net.thauvin.erik.urlencoder.UrlEncoder" - -dependencies { - implementation(projects.lib) - kover(projects.lib) - -// testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.25") - testImplementation("org.junit.jupiter:junit-jupiter:5.9.1") -} - -base { - archivesName.set(rootProject.name) -} - -application { - mainClass.set(urlEncoderMainClass) -} - -tasks { - jar { - manifest { - attributes["Main-Class"] = urlEncoderMainClass - } - } - - val fatJar by registering(Jar::class) { - group = LifecycleBasePlugin.BUILD_GROUP - dependsOn.addAll(listOf("compileJava", "compileKotlin", "processResources")) - archiveClassifier.set("all") - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - manifest { attributes(mapOf("Main-Class" to application.mainClass)) } - from(sourceSets.main.get().output) - dependsOn(configurations.runtimeClasspath) - from(configurations.runtimeClasspath.map { classpath -> - classpath.incoming.artifacts.artifactFiles.files.filter { it.name.endsWith("jar") }.map { zipTree(it) } - }) - } - - build { - dependsOn(fatJar) - } - - withType().configureEach { - destination = file("$projectDir/pom.xml") - } - - clean { - delete(deployDir) - } - - withType().configureEach { - dokkaSourceSets { - named("main") { - moduleName.set("UrlEncoder Application") - } - } - } - - val copyToDeploy by registering(Sync::class) { - group = PublishingPlugin.PUBLISH_TASK_GROUP - from(configurations.runtimeClasspath) { - exclude("annotations-*.jar") - } - from(jar) - into(deployDir) - } - - register("deploy") { - description = "Copies all needed files to the 'deploy' directory." - group = PublishingPlugin.PUBLISH_TASK_GROUP - dependsOn(build, copyToDeploy) - } -} - -publishing { - publications { - create("mavenJava") { - from(components["java"]) - artifactId = rootProject.name - artifact(tasks.javadocJar) - } - } -} diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml deleted file mode 100644 index fd4e62e..0000000 --- a/app/detekt-baseline.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - ComplexCondition:UrlEncoder.kt$UrlEncoder$hasOption && args.size == 2 || !hasOption && args.size == 1 - MaxLineLength:UrlEncoder.kt$UrlEncoder$* - - diff --git a/app/pom.xml b/app/pom.xml deleted file mode 100644 index e610169..0000000 --- a/app/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - 4.0.0 - net.thauvin.erik - urlencoder - 1.3.1-SNAPSHOT - UrlEncoder for Kotlin - A simple defensive application to encode/decode URL components - https://github.com/ethauvin/urlencoder - - - The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - gbevin - Geert Bevin - gbevin@uwyn.com - https://github.com/gbevin - - - ethauvin - Erik C. Thauvin - erik@thauvin.net - https://erik.thauvin.net/ - - - - scm:git://github.com/ethauvin/urlencoder.git - scm:git@github.com:ethauvin/urlencoder.git - https://github.com/ethauvin/urlencoder - - - GitHub - https://github.com/ethauvin/urlencoder/issues - - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - 1.8.0 - compile - - - net.thauvin.erik - urlencoder-lib - 1.3.1-SNAPSHOT - runtime - - - diff --git a/app/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt b/app/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt deleted file mode 100644 index 902bc45..0000000 --- a/app/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com) - * Copyright 2022-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.thauvin.erik.urlencoder - -import kotlin.system.exitProcess - -/** - * Most defensive approach to URL encoding and decoding. - * - * - Rules determined by combining the unreserved character set from - * [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#page-13) with the percent-encode set from - * [application/x-www-form-urlencoded](https://url.spec.whatwg.org/#application-x-www-form-urlencoded-percent-encode-set). - * - * - Both specs above support percent decoding of two hexadecimal digits to a binary octet, however their unreserved - * set of characters differs and `application/x-www-form-urlencoded` adds conversion of space to `+`, which has the - * potential to be misunderstood. - * - * - This library encodes with rules that will be decoded correctly in either case. - * - * @author Geert Bevin (gbevin(remove) at uwyn dot com) - * @author Erik C. Thauvin (erik@thauvin.net) - **/ -object UrlEncoder { - - internal val usage = - "Usage : java -jar urlencoder-*all.jar [-ed] text" + System.lineSeparator() + - "Encode and decode URL components defensively." + System.lineSeparator() + - " -e encode (default) " + System.lineSeparator() + - " -d decode" - - /** - * Encodes and decodes URLs from the command line. - * - * - `java -jar urlencoder-*all.jar [-ed] text` - */ - @JvmStatic - fun main(args: Array) { - try { - val result = processMain(args) - if (result.status == 1) { - System.err.println(result.output) - } else { - println(result.output) - } - exitProcess(result.status) - } catch (e: IllegalArgumentException) { - System.err.println("${UrlEncoder::class.java.simpleName}: ${e.message}") - exitProcess(1) - } - } - - internal data class MainResult(var output: String = usage, var status: Int = 1) - - internal fun processMain(args: Array): MainResult { - val result = MainResult() - if (args.isNotEmpty() && args[0].isNotEmpty()) { - val hasDecode = (args[0] == "-d") - val hasOption = (hasDecode || args[0] == "-e") - if (hasOption && args.size == 2 || !hasOption && args.size == 1) { - val arg = if (hasOption) args[1] else args[0] - if (hasDecode) { - result.output = decode(arg) - } else { - result.output = UrlEncoderUtil.encode(arg) - } - result.status = 0 - } - } - return result - } - - /** - * Transforms a provided [String] into a new string, containing decoded URL characters in the UTF-8 - * encoding. - */ - @JvmStatic - @JvmOverloads - fun decode(source: String, plusToSpace: Boolean = false): String = - // delegate to UrlEncoderFunctions for backwards compatibility - UrlEncoderUtil.decode(source, plusToSpace) - - /** - * Transforms a provided [String] object into a new string, containing only valid URL characters in the UTF-8 - * encoding. - * - * - Letters, numbers, unreserved (`_-!.'()*`) and allowed characters are left intact. - */ - @JvmStatic - @JvmOverloads - fun encode(source: String, allow: String = "", spaceToPlus: Boolean = false): String = - UrlEncoderUtil.encode(source, allow, spaceToPlus) -} diff --git a/build.gradle.kts b/build.gradle.kts index 3636913..9eae0de 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,7 +17,6 @@ plugins { buildsrc.conventions.base - buildsrc.conventions.sonarqube } group = "net.thauvin.erik" diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 8e6b07f..e862322 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -5,8 +5,8 @@ plugins { dependencies { implementation("com.github.ben-manes:gradle-versions-plugin:0.46.0") implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.0") - implementation("org.jetbrains.dokka:dokka-gradle-plugin:1.8.20") + implementation("org.jetbrains.dokka:dokka-gradle-plugin:1.8.10") implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21") - implementation("org.jetbrains.kotlinx:kover-gradle-plugin:0.7.1") + implementation("org.jetbrains.kotlinx:kover-gradle-plugin:0.7.0") implementation("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.2.0.3129") -} +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/buildsrc/conventions/sonarqube.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/conventions/code-quality.gradle.kts similarity index 73% rename from buildSrc/src/main/kotlin/buildsrc/conventions/sonarqube.gradle.kts rename to buildSrc/src/main/kotlin/buildsrc/conventions/code-quality.gradle.kts index 06ca6af..f78c963 100644 --- a/buildSrc/src/main/kotlin/buildsrc/conventions/sonarqube.gradle.kts +++ b/buildSrc/src/main/kotlin/buildsrc/conventions/code-quality.gradle.kts @@ -17,23 +17,18 @@ package buildsrc.conventions +import buildsrc.utils.Rife2TestListener +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent import org.sonarqube.gradle.SonarTask -/** - * Convention plugin for SonarQube analysis. - * - * SonarQube depends on an aggregated XML coverage report from - * [Kotlinx Kover](https://github.com/Kotlin/kotlinx-kover). - * See the Kover docs for - * [how to aggregate coverage reports](https://kotlin.github.io/kotlinx-kover/gradle-plugin/#multiproject-build). - */ - plugins { id("org.sonarqube") + id("io.gitlab.arturbosch.detekt") id("org.jetbrains.kotlinx.kover") } -sonar { +sonarqube { properties { property("sonar.projectName", rootProject.name) property("sonar.projectKey", "ethauvin_${rootProject.name}") @@ -48,6 +43,14 @@ sonar { } tasks.withType().configureEach { - // workaround for https://github.com/Kotlin/kotlinx-kover/issues/394 dependsOn(tasks.matching { it.name == "koverXmlReport" }) } + +tasks.withType().configureEach { + val testsBadgeApiKey = providers.gradleProperty("testsBadgeApiKey") + addTestListener(Rife2TestListener(testsBadgeApiKey)) + testLogging { + exceptionFormat = TestExceptionFormat.FULL + events = setOf(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) + } +} diff --git a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-jvm.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-jvm.gradle.kts index eb302e8..790e694 100644 --- a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-jvm.gradle.kts +++ b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-jvm.gradle.kts @@ -1,10 +1,15 @@ package buildsrc.conventions.lang import buildsrc.utils.Rife2TestListener +import org.gradle.api.JavaVersion +import org.gradle.api.tasks.testing.Test import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.sonarqube.gradle.SonarTask /** * Common configuration for Kotlin/JVM projects @@ -15,8 +20,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("buildsrc.conventions.base") kotlin("jvm") - id("io.gitlab.arturbosch.detekt") - id("org.jetbrains.kotlinx.kover") + id("buildsrc.conventions.code-quality") } java { @@ -35,11 +39,4 @@ tasks.withType().configureEach { tasks.withType().configureEach { useJUnitPlatform() - - val testsBadgeApiKey = providers.gradleProperty("testsBadgeApiKey") - addTestListener(Rife2TestListener(testsBadgeApiKey)) - testLogging { - exceptionFormat = TestExceptionFormat.FULL - events = setOf(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) - } } diff --git a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-base.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-base.gradle.kts index 3a98de0..4af978b 100644 --- a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-base.gradle.kts +++ b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-base.gradle.kts @@ -1,8 +1,5 @@ package buildsrc.conventions.lang -import buildsrc.utils.Rife2TestListener -import org.gradle.api.tasks.testing.logging.TestExceptionFormat -import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget @@ -16,8 +13,6 @@ import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget plugins { id("buildsrc.conventions.base") kotlin("multiplatform") - id("io.gitlab.arturbosch.detekt") - id("org.jetbrains.kotlinx.kover") } @@ -48,12 +43,3 @@ kotlin { } } } - -tasks.withType().configureEach { - val testsBadgeApiKey = providers.gradleProperty("testsBadgeApiKey") - addTestListener(Rife2TestListener(testsBadgeApiKey)) - testLogging { - exceptionFormat = TestExceptionFormat.FULL - events = setOf(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) - } -} diff --git a/buildSrc/src/main/kotlin/buildsrc/conventions/publishing.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/conventions/publishing.gradle.kts index 9155ad4..2390479 100644 --- a/buildSrc/src/main/kotlin/buildsrc/conventions/publishing.gradle.kts +++ b/buildSrc/src/main/kotlin/buildsrc/conventions/publishing.gradle.kts @@ -36,7 +36,7 @@ publishing { licenses { license { name.set("The Apache License, Version 2.0") - url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") } } developers { @@ -84,8 +84,6 @@ signing { sign(publishing.publications) setRequired({ - // only enable signing for non-snapshot versions, or when publishing to a non-local repo, otherwise - // publishing to Maven Local requires signing for users without access to the signing key. !isSnapshotVersion() || gradle.taskGraph.hasTask("publish") }) } @@ -101,7 +99,6 @@ tasks.withType().configureEach { } val javadocJar by tasks.registering(Jar::class) { - description = "Generate Javadoc using Dokka" dependsOn(tasks.dokkaJavadoc) from(tasks.dokkaJavadoc) archiveClassifier.set("javadoc") diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index aeb5eb3..eb0edae 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,32 +1,17 @@ -/* - * Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com) - * Copyright 2022-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - import org.jetbrains.dokka.gradle.DokkaTask + plugins { buildsrc.conventions.lang.`kotlin-jvm` buildsrc.conventions.publishing - buildsrc.conventions.sonarqube + id("application") id("com.github.ben-manes.versions") } description = "A simple defensive library to encode/decode URL components" val deployDir = project.layout.projectDirectory.dir("deploy") +val urlEncoderMainClass = "net.thauvin.erik.urlencoder.UrlEncoder" dependencies { // testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.25") @@ -34,27 +19,50 @@ dependencies { } base { - archivesName.set("${rootProject.name}-lib") + archivesName.set(rootProject.name) +} + +application { + mainClass.set(urlEncoderMainClass) } tasks { - withType().configureEach { - destination = file("$projectDir/pom.xml") + jar { + manifest { + attributes["Main-Class"] = urlEncoderMainClass + } } - clean { - delete(deployDir) + val fatJar by registering(Jar::class) { + group = LifecycleBasePlugin.BUILD_GROUP + dependsOn.addAll(listOf("compileJava", "compileKotlin", "processResources")) + archiveClassifier.set("all") + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + manifest { attributes(mapOf("Main-Class" to application.mainClass)) } + val sourcesMain = sourceSets.main.get() + val contents = configurations.runtimeClasspath.get() + .map { if (it.isDirectory) it else zipTree(it) } + sourcesMain.output + from(contents) + } + + build { + dependsOn(fatJar) + } + + withType().configureEach { + destination = file("$projectDir/pom.xml") } withType().configureEach { dokkaSourceSets { named("main") { - moduleName.set("UrlEncoder Library") + moduleName.set("UrlEncoder API") } } } val copyToDeploy by registering(Sync::class) { + description = "Copies all needed files to the 'deploy' directory." group = PublishingPlugin.PUBLISH_TASK_GROUP from(configurations.runtimeClasspath) { exclude("annotations-*.jar") @@ -68,13 +76,17 @@ tasks { group = PublishingPlugin.PUBLISH_TASK_GROUP dependsOn(build, copyToDeploy) } + + clean { + delete(deployDir) + } } publishing { publications { create("mavenJava") { from(components["java"]) - artifactId = "${rootProject.name}-lib" + artifactId = rootProject.name artifact(tasks.javadocJar) } } diff --git a/lib/detekt-baseline.xml b/lib/detekt-baseline.xml index 2562c74..700d787 100644 --- a/lib/detekt-baseline.xml +++ b/lib/detekt-baseline.xml @@ -2,13 +2,14 @@ - MagicNumber:UrlEncoderUtil.kt$UrlEncoderUtil$0x80 - MagicNumber:UrlEncoderUtil.kt$UrlEncoderUtil$0xFF - MagicNumber:UrlEncoderUtil.kt$UrlEncoderUtil$16 - MagicNumber:UrlEncoderUtil.kt$UrlEncoderUtil$3 - MagicNumber:UrlEncoderUtil.kt$UrlEncoderUtil$4 - MaxLineLength:UrlEncoderUtil.kt$UrlEncoderUtil$* - NestedBlockDepth:UrlEncoderUtil.kt$UrlEncoderUtil$@JvmStatic @JvmOverloads fun decode(source: String, plusToSpace: Boolean = false): String - NestedBlockDepth:UrlEncoderUtil.kt$UrlEncoderUtil$@JvmStatic @JvmOverloads fun encode(source: String, allow: String = "", spaceToPlus: Boolean = false): String + ComplexCondition:UrlEncoder.kt$UrlEncoder$hasOption && args.size == 2 || !hasOption && args.size == 1 + MagicNumber:UrlEncoder.kt$UrlEncoder$0x80 + MagicNumber:UrlEncoder.kt$UrlEncoder$0xFF + MagicNumber:UrlEncoder.kt$UrlEncoder$16 + MagicNumber:UrlEncoder.kt$UrlEncoder$3 + MagicNumber:UrlEncoder.kt$UrlEncoder$4 + MaxLineLength:UrlEncoder.kt$UrlEncoder$* + NestedBlockDepth:UrlEncoder.kt$UrlEncoder$@JvmStatic @JvmOverloads fun decode(source: String, plusToSpace: Boolean = false): String + NestedBlockDepth:UrlEncoder.kt$UrlEncoder$@JvmStatic @JvmOverloads fun encode(source: String, allow: String = "", spaceToPlus: Boolean = false): String diff --git a/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtil.kt b/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt similarity index 80% rename from lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtil.kt rename to lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt index 7994cb1..d03145e 100644 --- a/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtil.kt +++ b/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt @@ -19,6 +19,7 @@ package net.thauvin.erik.urlencoder import java.nio.charset.StandardCharsets import java.util.BitSet +import kotlin.system.exitProcess /** * Most defensive approach to URL encoding and decoding. @@ -36,8 +37,13 @@ import java.util.BitSet * @author Geert Bevin (gbevin(remove) at uwyn dot com) * @author Erik C. Thauvin (erik@thauvin.net) **/ -object UrlEncoderUtil { +object UrlEncoder { private val hexDigits = "0123456789ABCDEF".toCharArray() + internal val usage = + "Usage : java -jar urlencoder-*all.jar [-ed] text" + System.lineSeparator() + + "Encode and decode URL components defensively." + System.lineSeparator() + + " -e encode (default) " + System.lineSeparator() + + " -d decode" // see https://www.rfc-editor.org/rfc/rfc3986#page-13 // and https://url.spec.whatwg.org/#application-x-www-form-urlencoded-percent-encode-set @@ -194,4 +200,45 @@ object UrlEncoderUtil { return out?.toString() ?: source } + + /** + * Encodes and decodes URLs from the command line. + * + * - `java -jar urlencoder-*all.jar [-ed] text` + */ + @JvmStatic + fun main(args: Array) { + try { + val result = processMain(args) + if (result.status == 1) { + System.err.println(result.output) + } else { + println(result.output) + } + exitProcess(result.status) + } catch (e: IllegalArgumentException) { + System.err.println("${UrlEncoder::class.java.simpleName}: ${e.message}") + exitProcess(1) + } + } + + internal data class MainResult(var output: String = usage, var status: Int = 1) + + internal fun processMain(args: Array): MainResult { + val result = MainResult() + if (args.isNotEmpty() && args[0].isNotEmpty()) { + val hasDecode = (args[0] == "-d") + val hasOption = (hasDecode || args[0] == "-e") + if (hasOption && args.size == 2 || !hasOption && args.size == 1) { + val arg = if (hasOption) args[1] else args[0] + if (hasDecode) { + result.output = decode(arg) + } else { + result.output = encode(arg) + } + result.status = 0 + } + } + return result + } } diff --git a/app/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt b/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt similarity index 64% rename from app/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt rename to lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt index 08e78b7..8439010 100644 --- a/app/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt +++ b/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt @@ -17,10 +17,14 @@ package net.thauvin.erik.urlencoder +import net.thauvin.erik.urlencoder.UrlEncoder.decode +import net.thauvin.erik.urlencoder.UrlEncoder.encode import net.thauvin.erik.urlencoder.UrlEncoder.processMain import net.thauvin.erik.urlencoder.UrlEncoder.usage import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertSame import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -30,6 +34,7 @@ import org.junit.jupiter.params.provider.ValueSource import java.util.stream.Stream class UrlEncoderTest { + private val same = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_." companion object { @JvmStatic @@ -49,6 +54,67 @@ class UrlEncoderTest { ) } + @ParameterizedTest(name = "decode({0}) should be {1}") + @MethodSource("validMap") + fun `Decode URL`(expected: String, source: String) { + assertEquals(expected, decode(source)) + } + + @ParameterizedTest(name = "decode({0})") + @MethodSource("invalid") + fun `Decode with Exception`(source: String) { + assertThrows(IllegalArgumentException::class.java, { decode(source) }, "decode($source)") + } + + @Test + fun `Decode when None needed`() { + assertSame(same, decode(same)) + assertEquals("", decode(""), "decode('')") + assertEquals(" ", decode(" "), "decode(' ')") + } + + @Test + fun `Decode with Plus to Space`() { + assertEquals("foo bar", decode("foo+bar", true)) + assertEquals("foo bar foo", decode("foo+bar++foo", true)) + assertEquals("foo bar foo", decode("foo+%20bar%20+foo", true)) + assertEquals("foo + bar", decode("foo+%2B+bar", plusToSpace = true)) + assertEquals("foo+bar", decode("foo%2Bbar", plusToSpace = true)) + } + + @ParameterizedTest(name = "encode({0}) should be {1}") + @MethodSource("validMap") + fun `Encode URL`(source: String, expected: String) { + assertEquals(expected, encode(source)) + } + + @Test + fun `Encode Empty or Blank`() { + assertTrue(encode("", allow = "").isEmpty(), "encode('','')") + assertEquals("", encode(""), "encode('')") + assertEquals("%20", encode(" "), "encode(' ')") + } + + @Test + fun `Encode when None needed`() { + assertSame(same, encode(same)) + assertSame(same, encode(same, allow = ""), "with empty allow") + } + + @Test + fun `Encode with Allow Arg`() { + assertEquals("?test=a%20test", encode("?test=a test", allow = "=?"), "encode(x, =?)") + assertEquals("aaa", encode("aaa", "a"), "encode(aaa, a)") + assertEquals(" ", encode(" ", " "), "encode(' ', ' ')") + } + + @Test + fun `Encode with Space to Plus`() { + assertEquals("foo+bar", encode("foo bar", spaceToPlus = true)) + assertEquals("foo+bar++foo", encode("foo bar foo", spaceToPlus = true)) + assertEquals("foo bar", encode("foo bar", " ", true)) + } + @ParameterizedTest(name = "processMain(-d {1}) should be {0}") @MethodSource("validMap") fun `Main Decode`(expected: String, source: String) { diff --git a/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtilTest.kt b/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtilTest.kt deleted file mode 100644 index 20d8e4d..0000000 --- a/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtilTest.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com) - * Copyright 2022-2023 Erik C. Thauvin (erik@thauvin.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.thauvin.erik.urlencoder - -import net.thauvin.erik.urlencoder.UrlEncoderUtil.decode -import net.thauvin.erik.urlencoder.UrlEncoderUtil.encode -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertSame -import org.junit.jupiter.api.Assertions.assertThrows -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.Arguments.arguments -import org.junit.jupiter.params.provider.MethodSource -import java.util.stream.Stream - -class UrlEncoderUtilTest { - private val same = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_." - - companion object { - @JvmStatic - fun invalid() = arrayOf("sdkjfh%", "sdkjfh%6", "sdkjfh%xx", "sdfjfh%-1") - - @JvmStatic - fun validMap(): Stream = Stream.of( - arguments("a test &", "a%20test%20%26"), - arguments( - "!abcdefghijklmnopqrstuvwxyz%%ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.~=", - "%21abcdefghijklmnopqrstuvwxyz%25%25ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.%7E%3D" - ), - arguments("%#okékÉȢ smile!😁", "%25%23ok%C3%A9k%C3%89%C8%A2%20smile%21%F0%9F%98%81"), - arguments( - "\uD808\uDC00\uD809\uDD00\uD808\uDF00\uD808\uDD00", "%F0%92%80%80%F0%92%94%80%F0%92%8C%80%F0%92%84%80" - ) - ) - } - - @ParameterizedTest(name = "decode({0}) should be {1}") - @MethodSource("validMap") - fun `Decode URL`(expected: String, source: String) { - assertEquals(expected, decode(source)) - } - - @ParameterizedTest(name = "decode({0})") - @MethodSource("invalid") - fun `Decode with Exception`(source: String) { - assertThrows(IllegalArgumentException::class.java, { decode(source) }, "decode($source)") - } - - @Test - fun `Decode when None needed`() { - assertSame(same, decode(same)) - assertEquals("", decode(""), "decode('')") - assertEquals(" ", decode(" "), "decode(' ')") - } - - @Test - fun `Decode with Plus to Space`() { - assertEquals("foo bar", decode("foo+bar", true)) - assertEquals("foo bar foo", decode("foo+bar++foo", true)) - assertEquals("foo bar foo", decode("foo+%20bar%20+foo", true)) - assertEquals("foo + bar", decode("foo+%2B+bar", plusToSpace = true)) - assertEquals("foo+bar", decode("foo%2Bbar", plusToSpace = true)) - } - - @ParameterizedTest(name = "encode({0}) should be {1}") - @MethodSource("validMap") - fun `Encode URL`(source: String, expected: String) { - assertEquals(expected, encode(source)) - } - - @Test - fun `Encode Empty or Blank`() { - assertTrue(encode("", allow = "").isEmpty(), "encode('','')") - assertEquals("", encode(""), "encode('')") - assertEquals("%20", encode(" "), "encode(' ')") - } - - @Test - fun `Encode when None needed`() { - assertSame(same, encode(same)) - assertSame(same, encode(same, allow = ""), "with empty allow") - } - - @Test - fun `Encode with Allow Arg`() { - assertEquals("?test=a%20test", encode("?test=a test", allow = "=?"), "encode(x, =?)") - assertEquals("aaa", encode("aaa", "a"), "encode(aaa, a)") - assertEquals(" ", encode(" ", " "), "encode(' ', ' ')") - } - - @Test - fun `Encode with Space to Plus`() { - assertEquals("foo+bar", encode("foo bar", spaceToPlus = true)) - assertEquals("foo+bar++foo", encode("foo bar foo", spaceToPlus = true)) - assertEquals("foo bar", encode("foo bar", " ", true)) - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts index 86af703..e75db1d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,9 +20,6 @@ dependencyResolutionManagement { } } -enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") - include( - ":app", ":lib", )