Compare commits
11
Commits
1ee8dd356f
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8598ead36 | ||
|
|
09a000239c | ||
|
|
c5b7dbab8b | ||
|
|
561fe601a9 | ||
|
|
66244db097 | ||
|
|
b580e64e76 | ||
|
|
9df3e0701d | ||
|
|
02db9910ff | ||
|
|
fdde00b40a | ||
|
|
9f051d57e2 | ||
|
|
4a73301eb3 |
No files matched your search
+18
-4
@@ -1,5 +1,19 @@
|
|||||||
# Ignore Gradle project-specific cache directory
|
*.iml
|
||||||
|
.kotlin
|
||||||
.gradle
|
.gradle
|
||||||
|
**/build/
|
||||||
# Ignore Gradle build output directory
|
xcuserdata
|
||||||
build
|
!src/**/build/
|
||||||
|
local.properties
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
*.xcodeproj/*
|
||||||
|
!*.xcodeproj/project.pbxproj
|
||||||
|
!*.xcodeproj/xcshareddata/
|
||||||
|
!*.xcodeproj/project.xcworkspace/
|
||||||
|
!*.xcworkspace/contents.xcworkspacedata
|
||||||
|
**/xcshareddata/WorkspaceSettings.xcsettings
|
||||||
|
node_modules/
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidProjectSystem">
|
||||||
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-js-0.2.8">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-js-0.2.8.jar">
|
||||||
|
<element id="module-output" name="util.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-js-0.3.0-beta.1">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-js-0.3.0-beta.1.jar" />
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-js-0.3.0-beta.11">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-js-0.3.0-beta.11.jar">
|
||||||
|
<element id="module-output" name="util.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-js-0.3.0-beta.19">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-js-0.3.0-beta.19.jar" />
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-js-0.3.0-beta.2">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-js-0.3.0-beta.2.jar">
|
||||||
|
<element id="module-output" name="util.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-js-0.3.0-beta.4">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-js-0.3.0-beta.4.jar">
|
||||||
|
<element id="module-output" name="util.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-js-0.3.0-beta.8">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-js-0.3.0-beta.8.jar">
|
||||||
|
<element id="module-output" name="util.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-jvm-0.2.8">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-jvm-0.2.8.jar">
|
||||||
|
<element id="module-output" name="util.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-jvm-0.3.0-beta.1">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-jvm-0.3.0-beta.1.jar" />
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-jvm-0.3.0-beta.11">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-jvm-0.3.0-beta.11.jar">
|
||||||
|
<element id="module-output" name="util.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-jvm-0.3.0-beta.19">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-jvm-0.3.0-beta.19.jar" />
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-jvm-0.3.0-beta.2">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-jvm-0.3.0-beta.2.jar">
|
||||||
|
<element id="module-output" name="util.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-jvm-0.3.0-beta.4">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-jvm-0.3.0-beta.4.jar">
|
||||||
|
<element id="module-output" name="util.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-jvm-0.3.0-beta.8">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-jvm-0.3.0-beta.8.jar">
|
||||||
|
<element id="module-output" name="util.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-js-0.2.8">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-js-0.2.8.jar">
|
||||||
|
<element id="module-output" name="util.wasmJsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-js-0.3.0-beta.1">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-js-0.3.0-beta.1.jar" />
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-js-0.3.0-beta.11">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-js-0.3.0-beta.11.jar">
|
||||||
|
<element id="module-output" name="util.wasmJsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-js-0.3.0-beta.19">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-js-0.3.0-beta.19.jar" />
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-js-0.3.0-beta.2">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-js-0.3.0-beta.2.jar">
|
||||||
|
<element id="module-output" name="util.wasmJsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-js-0.3.0-beta.4">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-js-0.3.0-beta.4.jar">
|
||||||
|
<element id="module-output" name="util.wasmJsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-js-0.3.0-beta.8">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-js-0.3.0-beta.8.jar">
|
||||||
|
<element id="module-output" name="util.wasmJsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-wasi-0.2.8">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-wasi-0.2.8.jar">
|
||||||
|
<element id="module-output" name="util.wasmWasiMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-wasi-0.3.0-beta.1">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-wasi-0.3.0-beta.1.jar" />
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-wasi-0.3.0-beta.11">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-wasi-0.3.0-beta.11.jar">
|
||||||
|
<element id="module-output" name="util.wasmWasiMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-wasi-0.3.0-beta.19">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-wasi-0.3.0-beta.19.jar" />
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-wasi-0.3.0-beta.2">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-wasi-0.3.0-beta.2.jar">
|
||||||
|
<element id="module-output" name="util.wasmWasiMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-wasi-0.3.0-beta.4">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-wasi-0.3.0-beta.4.jar">
|
||||||
|
<element id="module-output" name="util.wasmWasiMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="util-wasm-wasi-0.3.0-beta.8">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="util-wasm-wasi-0.3.0-beta.8.jar">
|
||||||
|
<element id="module-output" name="util.wasmWasiMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
Generated
+1
-1
@@ -4,5 +4,5 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="21" project-jdk-type="JavaSDK" />
|
||||||
</project>
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module version="4">
|
||||||
|
<component name="AdditionalModuleElements">
|
||||||
|
<content url="file://$MODULE_DIR$/../../src/jvmMain" dumb="true">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/../../src/jvmMain/kotlin" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+97
-24
@@ -1,10 +1,12 @@
|
|||||||
@file:OptIn(ExperimentalKotlinGradlePluginApi::class)
|
//@file:OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||||
|
@file:OptIn(ExperimentalWasmDsl::class)
|
||||||
|
|
||||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version masterLibs.versions.kotlinVersion
|
kotlin("multiplatform") version masterLibs.versions.kotlinVersion
|
||||||
`java-library`
|
// `java-library`
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
alias(masterLibs.plugins.dokkaPlugin)
|
alias(masterLibs.plugins.dokkaPlugin)
|
||||||
alias(masterLibs.plugins.versionPlugin)
|
alias(masterLibs.plugins.versionPlugin)
|
||||||
@@ -20,27 +22,98 @@ repositories {
|
|||||||
maven("https://nexus.keinekekse.net/repository/maven-public/")
|
maven("https://nexus.keinekekse.net/repository/maven-public/")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
//dependencies {
|
||||||
implementation(masterLibs.kotlinxSerialization)
|
// implementation(masterLibs.kotlinxSerialization)
|
||||||
// f. cmd line via annotation KMP?
|
// // f. cmd line via annotation KMP?
|
||||||
implementation(kotlin("reflect"))
|
// implementation(kotlin("reflect"))
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion.set(JavaLanguageVersion.of(masterLibs.versions.javaVersion.get()))
|
|
||||||
}
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(masterLibs.versions.javaVersion.get().toInt())
|
// explicitApi()
|
||||||
// compilerOptions {
|
|
||||||
// languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3)
|
jvm {
|
||||||
|
compilerOptions.jvmTarget =
|
||||||
|
JvmTarget.valueOf("JVM_" + masterLibs.versions.javaVersion.get()) //JvmTarget.JVM_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
js {
|
||||||
|
browser {
|
||||||
|
testTask {
|
||||||
|
useKarma {
|
||||||
|
useChromeHeadless()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodejs()
|
||||||
|
}
|
||||||
|
|
||||||
|
wasmJs {
|
||||||
|
browser {
|
||||||
|
testTask {
|
||||||
|
useKarma {
|
||||||
|
useChromeHeadless()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodejs()
|
||||||
|
}
|
||||||
|
|
||||||
|
wasmWasi {
|
||||||
|
nodejs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// macosX64()
|
||||||
|
macosArm64()
|
||||||
|
|
||||||
|
// iosX64()
|
||||||
|
// iosArm64()
|
||||||
|
// iosSimulatorArm64()
|
||||||
|
|
||||||
|
// watchosArm32()
|
||||||
|
// watchosArm64()
|
||||||
|
// watchosX64()
|
||||||
|
// watchosSimulatorArm64()
|
||||||
|
// watchosDeviceArm64()
|
||||||
|
|
||||||
|
// tvosArm64()
|
||||||
|
// tvosX64()
|
||||||
|
// tvosSimulatorArm64()
|
||||||
|
|
||||||
|
// mingwX64()
|
||||||
|
|
||||||
|
linuxX64()
|
||||||
|
// linuxArm64()
|
||||||
|
|
||||||
|
// androidNativeArm32()
|
||||||
|
// androidNativeArm64()
|
||||||
|
// androidNativeX86()
|
||||||
|
// androidNativeX64()
|
||||||
|
|
||||||
|
applyDefaultHierarchyTemplate()
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain.dependencies {
|
||||||
|
implementation(masterLibs.kotlinxSerialization)
|
||||||
|
}
|
||||||
|
jvmMain.dependencies {
|
||||||
|
implementation(kotlin("reflect"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// commonTest.dependencies {
|
||||||
|
// implementation(libs.kotlin.test)
|
||||||
|
// implementation(libs.serialization.json)
|
||||||
// }
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//kotlin {
|
||||||
|
// jvmToolchain(masterLibs.versions.javaVersion.get().toInt())
|
||||||
|
//// compilerOptions {
|
||||||
|
//// languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3)
|
||||||
|
//// }
|
||||||
|
//}
|
||||||
|
|
||||||
/* Generierte Dokka-Doku als javadoc.jar ausgeben (s.a. "publishing")
|
/* Generierte Dokka-Doku als javadoc.jar ausgeben (s.a. "publishing")
|
||||||
tasks.register<Jar>("dokkaJar") {
|
tasks.register<Jar>("dokkaJar") {
|
||||||
group = "build"
|
group = "build"
|
||||||
@@ -62,10 +135,10 @@ publishing {
|
|||||||
url = uri("https://nexus.keinekekse.net/repository/maven-releases/")
|
url = uri("https://nexus.keinekekse.net/repository/maven-releases/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
publications.create<MavenPublication>("maven") {
|
// publications.create<MavenPublication>("maven") {
|
||||||
from(components["java"])
|
// from(components["java"])
|
||||||
// artifact(tasks["dokkaJar"])
|
//// artifact(tasks["dokkaJar"])
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ./gradlew dokkaGenerate
|
// ./gradlew dokkaGenerate
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
#auto generated
|
#auto generated
|
||||||
#Sun Jan 18 02:02:24 CET 2026
|
#Mon Jan 19 22:42:11 CET 2026
|
||||||
build.number=106
|
build.number=130
|
||||||
+6
-1
@@ -1,3 +1,8 @@
|
|||||||
|
#Kotlin
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
org.gradle.configuration-cache=true
|
kotlin.daemon.jvmargs=-Xmx3072M
|
||||||
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
|
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
|
||||||
|
#Gradle
|
||||||
|
org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8
|
||||||
|
org.gradle.configuration-cache=true
|
||||||
|
org.gradle.caching=true
|
||||||
+1
-1
@@ -19,7 +19,7 @@ dependencyResolutionManagement {
|
|||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
create("masterLibs") {
|
create("masterLibs") {
|
||||||
// from(files("gradle/masterlibs.versions.toml"))
|
// from(files("gradle/masterlibs.versions.toml"))
|
||||||
from("net.keinekekse:version-catalog:107")
|
from("net.keinekekse:version-catalog:128")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
package net.keinekekse.args
|
||||||
|
|
||||||
|
import net.keinekekse.util.VERSION
|
||||||
|
import net.keinekekse.version.Version
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminates the currently running application with the specified exit code.
|
||||||
|
* This function does not return as it stops the application process.
|
||||||
|
*
|
||||||
|
* Works only for JVM and macOS-/Linux-native targets.
|
||||||
|
*
|
||||||
|
* @param rc The exit code to return when the application terminates. Defaults to 0,
|
||||||
|
* indicating normal termination.
|
||||||
|
* @return This function does not return as the application is terminated.
|
||||||
|
*/
|
||||||
|
expect fun exitApplication(rc: Int = 0): Nothing
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if standard input is ready for reading.
|
||||||
|
* This function is used to determine if there is data available to be read from standard input.
|
||||||
|
*
|
||||||
|
* Works only for JVM and macOS-/Linux-native targets.
|
||||||
|
*
|
||||||
|
* @return True if standard input is ready for reading, false otherwise.
|
||||||
|
*/
|
||||||
|
expect fun isStdInputReady(): Boolean
|
||||||
|
|
||||||
|
class VerifyParameterException(message: String) : RuntimeException(message)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws a [VerifyParameterException] with the specified error message.
|
||||||
|
* This function is used to signal that there was an error during argument parsing.
|
||||||
|
*
|
||||||
|
* For use in the application. The exception will be caught by the command-line parser
|
||||||
|
* and displayed to the user together with the help information.
|
||||||
|
*
|
||||||
|
* @param msg The error message to include in the exception.
|
||||||
|
* @throws VerifyParameterException with the provided error message.
|
||||||
|
*/
|
||||||
|
fun showParsingError(msg: String): Nothing {
|
||||||
|
throw VerifyParameterException(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional interface representing a runnable program execution logic.
|
||||||
|
*
|
||||||
|
* This interface allows defining a lambda function or block of code that
|
||||||
|
* processes a list of remaining arguments. It is intended to be used as
|
||||||
|
* a mechanism to encapsulate the execution logic for commands or subcommands
|
||||||
|
* in a command-line parsing framework.
|
||||||
|
*
|
||||||
|
* The primary use case for this interface is to define execution behavior
|
||||||
|
* for subcommands or global commands, commonly used in CLI applications.
|
||||||
|
*
|
||||||
|
* @constructor A single abstract method is provided to execute the specified logic.
|
||||||
|
*/
|
||||||
|
fun interface RunProgram {
|
||||||
|
fun run(remaining: List<String>)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a subcommand that can be added to a command-line argument parser.
|
||||||
|
*
|
||||||
|
* A `SubCommand` is used to define and configure subcommands in a CLI application,
|
||||||
|
* allowing for structured argument parsing and execution of specific logic based
|
||||||
|
* on the subcommand invoked. It provides mechanisms to add parameters to the
|
||||||
|
* subcommand, define execution logic, and manage case sensitivity for argument
|
||||||
|
* names.
|
||||||
|
*
|
||||||
|
* @property obj Internal data container for subcommand-specific configuration and parameters.
|
||||||
|
* @property ignoreCase Defines whether argument names are case-insensitive. Defaults to `false`.
|
||||||
|
*/
|
||||||
|
class SubCommand private constructor(
|
||||||
|
internal val obj: SubCommandData,
|
||||||
|
val ignoreCase: Boolean = false,
|
||||||
|
) : AddParameter {
|
||||||
|
companion object {
|
||||||
|
internal fun create(obj: SubCommandData, ignoreCase: Boolean = false): SubCommand =
|
||||||
|
SubCommand(obj, ignoreCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addStringParameter(
|
||||||
|
long: String,
|
||||||
|
short: Char?,
|
||||||
|
description: String,
|
||||||
|
isMandatory: Boolean,
|
||||||
|
): ParameterValue<String> {
|
||||||
|
return createParameter(ParameterType.STRING, long, short, description, isMandatory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addLongParameter(
|
||||||
|
long: String,
|
||||||
|
short: Char?,
|
||||||
|
description: String,
|
||||||
|
isMandatory: Boolean,
|
||||||
|
): ParameterValue<Long> {
|
||||||
|
return createParameter(ParameterType.LONG, long, short, description, isMandatory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addBooleanParameter(
|
||||||
|
long: String,
|
||||||
|
short: Char?,
|
||||||
|
description: String,
|
||||||
|
isMandatory: Boolean,
|
||||||
|
): ParameterValue<Boolean> {
|
||||||
|
return createParameter(ParameterType.BOOLEAN, long, short, description, isMandatory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setExecution(run: RunProgram) {
|
||||||
|
obj.setExecution(run)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> createParameter(
|
||||||
|
type: ParameterType<T>,
|
||||||
|
long: String,
|
||||||
|
short: Char?,
|
||||||
|
description: String,
|
||||||
|
isMandatory: Boolean,
|
||||||
|
): ParameterValue<T> {
|
||||||
|
val processedLong = long.handleCase(ignoreCase)
|
||||||
|
val processedShort = short?.toString()?.handleCase(ignoreCase)
|
||||||
|
|
||||||
|
return runCatching {
|
||||||
|
type.createOption(processedLong, processedShort, description, isMandatory).apply {
|
||||||
|
verifyParameter(this)
|
||||||
|
registerParameter(this, processedLong, processedShort)
|
||||||
|
}.prop
|
||||||
|
}.getOrElse { showDefiningError(it.message ?: "") }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerParameter(parameter: ParameterData<*>, long: String, short: String?) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val data = parameter as ParameterData<Any>
|
||||||
|
obj.params[long] = data
|
||||||
|
short?.let { obj.params[it] = data }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifyParameter(parameter: ParameterData<*>) {
|
||||||
|
val longName = parameter.long
|
||||||
|
if (!NAME_PATTERN.matches(longName)) {
|
||||||
|
throw ArgumentParserException.invalidName(longName)
|
||||||
|
}
|
||||||
|
if (obj.params.containsKey(longName)) {
|
||||||
|
throw ArgumentParserException.optionExists(longName)
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter.short?.let { shortName ->
|
||||||
|
if (!SHORT_NAME_PATTERN.matches(shortName)) {
|
||||||
|
throw ArgumentParserException.invalidShortName(shortName)
|
||||||
|
}
|
||||||
|
if (obj.params.containsKey(shortName)) {
|
||||||
|
throw ArgumentParserException.optionExists(shortName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val lowerLong = longName.lowercase()
|
||||||
|
if (lowerLong == "help" || lowerLong == "version") {
|
||||||
|
throw ArgumentParserException.noHelpOrVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a command-line interface for parsing and executing commands, subcommands, and parameters.
|
||||||
|
*
|
||||||
|
* This class facilitates defining global parameters and subcommands, parsing provided arguments,
|
||||||
|
* validating inputs, and executing the corresponding actions. It supports various types of parameters,
|
||||||
|
* including strings, long integers, and booleans, with both optional and mandatory requirements.
|
||||||
|
* Additionally, it provides options for ignoring case sensitivity and handling remaining parameters.
|
||||||
|
*
|
||||||
|
* @param programVersion The version information of the program, including display name and copyright.
|
||||||
|
* @param ignoreCase Determines whether the parsing should ignore case sensitivity for commands and parameters.
|
||||||
|
* @param remainingDescription The description for remaining command-line arguments, or null if no arguments are allowed.
|
||||||
|
* @param emptyCommandLineAllowed Indicates whether the parser should allow an empty command line to be parsed. Defaults to false.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* // global parameters
|
||||||
|
*
|
||||||
|
* val commandLine = CommandLine(
|
||||||
|
* programVersion = VERSION,
|
||||||
|
* remainingDescription = "EPUB-Dateien",
|
||||||
|
* ignoreCase = true,
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* val param1 by commandLine.addStringParameter(
|
||||||
|
* long = "param",
|
||||||
|
* short = 'P',
|
||||||
|
* description = "parameter description"
|
||||||
|
* )
|
||||||
|
* // more global parameters
|
||||||
|
*
|
||||||
|
* commandLine.setExecution { additionalParameters: List<String> ->
|
||||||
|
* // called when no subcommand is specified
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // subcommand
|
||||||
|
*
|
||||||
|
* val commandSubCommand = commandLine.addSubCommand(
|
||||||
|
* "command",
|
||||||
|
* "My custom command",
|
||||||
|
* listOf("cmd") // aliases
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* val par1 by commandSubCommand.addStringParameter(
|
||||||
|
* long = "par1",
|
||||||
|
* description = "Parameter Par1 ",
|
||||||
|
* isMandatory = true
|
||||||
|
* )
|
||||||
|
* // more parameters
|
||||||
|
*
|
||||||
|
* commandSubCommand.setExecution { additionalParameters ->
|
||||||
|
* // called when this subcommand is found in the command line
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // more subcommands
|
||||||
|
*
|
||||||
|
* // start program
|
||||||
|
* commandLine.parseAndRun(*args)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class CommandLine(
|
||||||
|
val programVersion: Version,
|
||||||
|
val ignoreCase: Boolean = false,
|
||||||
|
val remainingDescription: String? = null, // null -> no remaining allowed
|
||||||
|
val emptyCommandLineAllowed: Boolean = false,
|
||||||
|
) : AddParameter {
|
||||||
|
private var hasRun = false
|
||||||
|
private val globalSubCommand = SubCommand.create(SubCommandData(), ignoreCase)
|
||||||
|
private var subCommands = mutableMapOf<String, SubCommandData>()
|
||||||
|
|
||||||
|
override fun addStringParameter(
|
||||||
|
long: String,
|
||||||
|
short: Char?,
|
||||||
|
description: String,
|
||||||
|
isMandatory: Boolean,
|
||||||
|
): ParameterValue<String> {
|
||||||
|
return globalSubCommand.addStringParameter(long, short, description, isMandatory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addLongParameter(
|
||||||
|
long: String,
|
||||||
|
short: Char?,
|
||||||
|
description: String,
|
||||||
|
isMandatory: Boolean,
|
||||||
|
): ParameterValue<Long> {
|
||||||
|
return globalSubCommand.addLongParameter(long, short, description, isMandatory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addBooleanParameter(
|
||||||
|
long: String,
|
||||||
|
short: Char?,
|
||||||
|
description: String,
|
||||||
|
isMandatory: Boolean,
|
||||||
|
): ParameterValue<Boolean> {
|
||||||
|
return globalSubCommand.addBooleanParameter(long, short, description, isMandatory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setExecution(run: RunProgram) {
|
||||||
|
globalSubCommand.setExecution(run)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addSubCommand(
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
aliases: List<String> = emptyList(),
|
||||||
|
): SubCommand = runCatching {
|
||||||
|
val normalizedName = normalizeAndVerifySubCommand(name, ignoreCase)
|
||||||
|
val subData = SubCommandData(normalizedName, description, aliases)
|
||||||
|
subCommands[normalizedName] = subData
|
||||||
|
aliases.forEach { alias ->
|
||||||
|
val alias = normalizeAndVerifySubCommand(alias, ignoreCase)
|
||||||
|
subCommands[alias] = subData
|
||||||
|
}
|
||||||
|
SubCommand.create(subData, ignoreCase)
|
||||||
|
}.getOrElse { showDefiningError(it.message ?: "") }
|
||||||
|
|
||||||
|
fun parseAndRun(vararg args: String) {
|
||||||
|
runCatching {
|
||||||
|
val selectedSubCommand = parse(args.toList())
|
||||||
|
selectedSubCommand.execute(selectedSubCommand.additionalParameters)
|
||||||
|
}.onFailure { exception ->
|
||||||
|
handleErrorAndShowHelp(exception as Exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun normalizeAndVerifySubCommand(cmd: String, ignoreCase: Boolean): String {
|
||||||
|
val normalized = cmd.handleCase(ignoreCase)
|
||||||
|
if (!matchSubCommand(normalized)) throw ArgumentParserException.invalidCommand(normalized)
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parse(args: List<String>): SubCommandData {
|
||||||
|
if (hasRun) throw ArgumentParserException.noMoreParsing()
|
||||||
|
hasRun = true
|
||||||
|
if (args.isEmpty() && !emptyCommandLineAllowed) throw ArgumentParserException.commandRequired()
|
||||||
|
checkExecutors()
|
||||||
|
|
||||||
|
val remainingAfterGlobal = parseParameters(args, globalSubCommand.obj)
|
||||||
|
validateMandatoryParameters(globalSubCommand.obj)
|
||||||
|
|
||||||
|
val resultSubCommand = parseSubCommand(remainingAfterGlobal)
|
||||||
|
validateMandatoryParameters(resultSubCommand)
|
||||||
|
|
||||||
|
if (remainingDescription != null) {
|
||||||
|
resultSubCommand.additionalParameters += resolveRemainingFromPipe()
|
||||||
|
} else if (resultSubCommand.additionalParameters.isNotEmpty()) {
|
||||||
|
throw ArgumentParserException.noMoreOptions()
|
||||||
|
}
|
||||||
|
return resultSubCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveRemainingFromPipe(): List<String> {
|
||||||
|
return if (isStdInputReady()) getRemainingParametersFromPipe() else emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseSubCommand(args: List<String>): SubCommandData {
|
||||||
|
val firstArg = args.firstOrNull()?.handleCase(ignoreCase)
|
||||||
|
val sub = subCommands[firstArg] ?: return globalSubCommand.obj.apply {
|
||||||
|
additionalParameters = args
|
||||||
|
}
|
||||||
|
return sub.apply { additionalParameters = parseParameters(args.drop(1), this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateMandatoryParameters(subCommand: SubCommandData) {
|
||||||
|
subCommand.params.filter { it.key == it.value.long }.forEach { (_, parameter) ->
|
||||||
|
if (parameter.isMandatory && parameter.prop.value == null) {
|
||||||
|
throw ArgumentParserException.optionRequired(parameter.long)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseParameters(args: List<String>, subCommand: SubCommandData): List<String> {
|
||||||
|
var index = 0
|
||||||
|
while (index < args.size) {
|
||||||
|
val (key, inlineValue) = matchArgument(args[index]) ?: return args.drop(index)
|
||||||
|
|
||||||
|
checkForHelpOrVersion(key)
|
||||||
|
val normalizedKey = key.handleCase(ignoreCase)
|
||||||
|
val param =
|
||||||
|
subCommand.params[normalizedKey] ?: throw ArgumentParserException.unknownOption(
|
||||||
|
normalizedKey
|
||||||
|
)
|
||||||
|
|
||||||
|
val (value, consumedExtra) = resolveParameterValue(
|
||||||
|
args,
|
||||||
|
index,
|
||||||
|
normalizedKey,
|
||||||
|
inlineValue,
|
||||||
|
param
|
||||||
|
)
|
||||||
|
param.type.convertAndSet(value, param.prop)
|
||||||
|
index += 1 + consumedExtra
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveParameterValue(
|
||||||
|
args: List<String>,
|
||||||
|
currentIndex: Int,
|
||||||
|
key: String,
|
||||||
|
inlineValue: String,
|
||||||
|
param: ParameterData<Any>,
|
||||||
|
): Pair<String, Int> {
|
||||||
|
return when {
|
||||||
|
param.type == ParameterType.BOOLEAN -> {
|
||||||
|
if (inlineValue.isNotBlank()) throw ArgumentParserException.optionNoValue(key)
|
||||||
|
TRUE to 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// --name=--irgendwas ist OK
|
||||||
|
inlineValue.isNotBlank() -> inlineValue to 0
|
||||||
|
|
||||||
|
// --name --irgendwas ist nicht OK
|
||||||
|
currentIndex < args.size - 1
|
||||||
|
&& !ARGUMENT_PATTERN.matches(args[currentIndex + 1])
|
||||||
|
&& args[currentIndex + 1].isNotBlank() -> {
|
||||||
|
args[currentIndex + 1] to 1
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw ArgumentParserException.optionRequiresValue(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRemainingParametersFromPipe(): List<String> {
|
||||||
|
if (isStdInputReady()) {
|
||||||
|
val pipe = mutableListOf<String>()
|
||||||
|
while (true) {
|
||||||
|
val input = readlnOrNull() ?: break
|
||||||
|
pipe.add(input)
|
||||||
|
}
|
||||||
|
return pipe
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkExecutors() {
|
||||||
|
globalSubCommand.obj.checkExecutors()
|
||||||
|
subCommands.values.forEach { it.checkExecutors() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkForHelpOrVersion(key: String) {
|
||||||
|
val k = key.lowercase()
|
||||||
|
if (k == "help") {
|
||||||
|
throw ArgumentParserException.showHelp()
|
||||||
|
} else if (k == "version") {
|
||||||
|
showVersion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleErrorAndShowHelp(
|
||||||
|
e: Exception?,
|
||||||
|
): Nothing {
|
||||||
|
val hasSubCommands = subCommands.isNotEmpty()
|
||||||
|
val hasGlobalParams = globalSubCommand.obj.params.isNotEmpty()
|
||||||
|
val allowsRemaining = remainingDescription != null
|
||||||
|
|
||||||
|
println("\n${programVersion.displayName} ${programVersion.version} ${programVersion.copyright}\n")
|
||||||
|
println("${programVersion.description}\n")
|
||||||
|
|
||||||
|
val usagePrefix = "usage: ${programVersion.name}"
|
||||||
|
val globalPart =
|
||||||
|
if (hasGlobalParams) "[${if (hasSubCommands) "GLOBAL " else ""}PARAMETER]" else ""
|
||||||
|
val subPart = if (hasSubCommands) " subcommand [PARAMETER]" else ""
|
||||||
|
val remainPart = if (allowsRemaining) " <$remainingDescription>" else ""
|
||||||
|
|
||||||
|
println("$usagePrefix $globalPart$subPart$remainPart\n")
|
||||||
|
if (hasGlobalParams) {
|
||||||
|
println("${if (hasSubCommands) "Global " else ""}Parameters:")
|
||||||
|
}
|
||||||
|
showParameters(globalSubCommand.obj.params)
|
||||||
|
if (hasSubCommands) {
|
||||||
|
println()
|
||||||
|
println("Subcommands:")
|
||||||
|
showSubcommands(subCommands)
|
||||||
|
}
|
||||||
|
e?.message?.let {
|
||||||
|
println()
|
||||||
|
println(ArgumentParserException.PARSING_ERROR_MSG + it + "\n")
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
exitApplication(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSubcommands(parameters: MutableMap<String, SubCommandData>) {
|
||||||
|
parameters.filter { it.key == it.value.name }.forEach { (_, param) ->
|
||||||
|
val aliases = buildString {
|
||||||
|
param.aliases.forEach { alias ->
|
||||||
|
append(" | $alias")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("$INDENT${param.name}$aliases ${param.description}")
|
||||||
|
showParameters(param.params)
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showParameters(parameters: MutableMap<String, ParameterData<Any>>) {
|
||||||
|
var len = 0
|
||||||
|
parameters.filter { it.key == it.value.long }.map { (_, param) ->
|
||||||
|
val parmStr = INDENT + INDENT +
|
||||||
|
(param.short?.let { "-$it, " } ?: " ") +
|
||||||
|
"--${param.long}" + if (param.type != ParameterType.BOOLEAN) " <...>" else ""
|
||||||
|
len = maxOf(len, parmStr.length)
|
||||||
|
Pair(parmStr, "${param.description} ${if (param.isMandatory) "*" else ""}")
|
||||||
|
}.forEach {
|
||||||
|
println(it.first.padEnd(len + 3) + it.second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showVersion(): Nothing {
|
||||||
|
println("\n${programVersion.displayName} ${programVersion.fullVersion} ${programVersion.copyright}\n\n(${VERSION.displayName} ${VERSION.fullVersion} ${VERSION.copyright})\n")
|
||||||
|
exitApplication(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+6
-25
@@ -1,7 +1,6 @@
|
|||||||
package net.keinekekse.args
|
package net.keinekekse.args
|
||||||
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
interface ValueDelegate<T> {
|
interface ValueDelegate<T> {
|
||||||
var value: T?
|
var value: T?
|
||||||
@@ -152,48 +151,29 @@ interface AddParameter {
|
|||||||
// Fehler beim Check der Parameter-Definitionen und beim Parsen der Kommandozeile
|
// Fehler beim Check der Parameter-Definitionen und beim Parsen der Kommandozeile
|
||||||
internal class ArgumentParserException(message: String?) : RuntimeException(message) {
|
internal class ArgumentParserException(message: String?) : RuntimeException(message) {
|
||||||
companion object {
|
companion object {
|
||||||
const val PARSING_ERROR_MSG = "\n*** ERROR parsing arguments: %s\n"
|
const val PARSING_ERROR_MSG = "\n*** ERROR parsing arguments: "
|
||||||
const val DEFINITION_ERROR_MSG = "\n*** ERROR defining arguments: %s\n"
|
const val DEFINITION_ERROR_MSG = "\n***\n*** ERROR defining arguments in source code: "
|
||||||
fun showHelp() = ArgumentParserException(null)
|
fun showHelp() = ArgumentParserException(null)
|
||||||
fun noHelpOrVersion() =
|
fun noHelpOrVersion() =
|
||||||
ArgumentParserException("Option 'help' and option 'version' are not allowed.")
|
ArgumentParserException("Option 'help' and option 'version' are not allowed.")
|
||||||
|
|
||||||
fun noParametersDefined() = ArgumentParserException("No parameters found for command line.")
|
|
||||||
fun noExecutorDefinedGlobal() = ArgumentParserException("No lambda found for command line.")
|
|
||||||
fun noExecutorDefined(name: String?) =
|
fun noExecutorDefined(name: String?) =
|
||||||
ArgumentParserException("No lambda found for '$name'.")
|
ArgumentParserException("No lambda found for '$name'.")
|
||||||
|
|
||||||
fun optionExists(name: String) = ArgumentParserException("Option '$name' already exists.")
|
fun optionExists(name: String) = ArgumentParserException("Option '$name' already exists.")
|
||||||
|
|
||||||
fun invalidAnnotationTargetParameter(name: String) =
|
|
||||||
ArgumentParserException("Annotation @CmdLineParameter can only be used on mutable properties, '$name' isn't a mutable property.")
|
|
||||||
|
|
||||||
fun notNullableAnnotationTargetParameter(name: String) =
|
|
||||||
ArgumentParserException("Annotation @CmdLineParameter can only be used on nullable properties, '$name' isn't a nullable property.")
|
|
||||||
|
|
||||||
fun invalidAnnotationTargetOption() =
|
|
||||||
ArgumentParserException("Annotation @CmdLineOption can only be used on CommandLine Objects.")
|
|
||||||
|
|
||||||
fun optionNameRequired() = ArgumentParserException("Option name is required.")
|
|
||||||
fun unknownOption(name: String) = ArgumentParserException("Unknown option '$name'.")
|
fun unknownOption(name: String) = ArgumentParserException("Unknown option '$name'.")
|
||||||
fun unknownCommand(name: String) = ArgumentParserException("Unknown command '$name'.")
|
|
||||||
fun optionRequiresValue(name: String) =
|
fun optionRequiresValue(name: String) =
|
||||||
ArgumentParserException("Option '$name' requires a value.")
|
ArgumentParserException("Option '$name' requires a value.")
|
||||||
|
|
||||||
fun optionNoValue(name: String) =
|
fun optionNoValue(name: String) =
|
||||||
ArgumentParserException("Option '$name' does not accept a value.")
|
ArgumentParserException("Option '$name' does not accept a value.")
|
||||||
|
|
||||||
fun booleanNotMandatory(name: String) =
|
|
||||||
ArgumentParserException("Boolean option '$name' can not be mandatory.")
|
|
||||||
|
|
||||||
fun optionRequired(name: String) = ArgumentParserException("Option '$name' is required.")
|
fun optionRequired(name: String) = ArgumentParserException("Option '$name' is required.")
|
||||||
fun commandRequired() = ArgumentParserException("At least one command is required.")
|
fun commandRequired() = ArgumentParserException("At least one command is required.")
|
||||||
fun invalidLongValue(value: String) =
|
fun invalidLongValue(value: String) =
|
||||||
ArgumentParserException("Value '$value' is not a long integer.")
|
ArgumentParserException("Value '$value' is not a long integer.")
|
||||||
|
|
||||||
fun invalidType(name: String, type: String) =
|
|
||||||
ArgumentParserException("Unsupported parameter type: ${type} for parameter $name")
|
|
||||||
|
|
||||||
fun invalidShortName(name: String) =
|
fun invalidShortName(name: String) =
|
||||||
ArgumentParserException("Option short name '$name' is invalid.")
|
ArgumentParserException("Option short name '$name' is invalid.")
|
||||||
|
|
||||||
@@ -211,7 +191,7 @@ internal const val TRUE = "true"
|
|||||||
|
|
||||||
internal const val NAME = """[a-zA-Z][\w-]+"""
|
internal const val NAME = """[a-zA-Z][\w-]+"""
|
||||||
internal const val SHORT_NAME = """[a-zA-Z]"""
|
internal const val SHORT_NAME = """[a-zA-Z]"""
|
||||||
internal val ARGUMENT_PATTERN = """^(?:-(${SHORT_NAME})|--(${NAME}))(?:=(.+)|$)$""".toRegex()
|
internal val ARGUMENT_PATTERN = """^(?:-(${SHORT_NAME})|--(${NAME}))(?:[=:](.*)|$)$""".toRegex()
|
||||||
internal val NAME_PATTERN = """^${NAME}$""".toRegex()
|
internal val NAME_PATTERN = """^${NAME}$""".toRegex()
|
||||||
internal val SHORT_NAME_PATTERN = """^${SHORT_NAME}$""".toRegex()
|
internal val SHORT_NAME_PATTERN = """^${SHORT_NAME}$""".toRegex()
|
||||||
|
|
||||||
@@ -225,7 +205,8 @@ internal fun matchSubCommand(cmd: String) =
|
|||||||
internal fun matchArgument(arg: String): Pair<String, String>? =
|
internal fun matchArgument(arg: String): Pair<String, String>? =
|
||||||
ARGUMENT_PATTERN.matchEntire(arg)?.groupValues?.let { Pair(it[1] + it[2], it[3]) }
|
ARGUMENT_PATTERN.matchEntire(arg)?.groupValues?.let { Pair(it[1] + it[2], it[3]) }
|
||||||
|
|
||||||
|
// Fehler im Quellcode anzeigen
|
||||||
internal fun showDefiningError(msg: String): Nothing {
|
internal fun showDefiningError(msg: String): Nothing {
|
||||||
println(ArgumentParserException.DEFINITION_ERROR_MSG.format(msg))
|
println(ArgumentParserException.DEFINITION_ERROR_MSG + msg + "\n")
|
||||||
exitProcess(1)
|
exitApplication(1)
|
||||||
}
|
}
|
||||||
+6
-6
@@ -3,14 +3,14 @@ package net.keinekekse.util
|
|||||||
import net.keinekekse.version.Version
|
import net.keinekekse.version.Version
|
||||||
|
|
||||||
object VERSION: Version {
|
object VERSION: Version {
|
||||||
override val version = "0.2.8"
|
override val version = "0.3.0"
|
||||||
override val fullVersion = "0.2.8+build.106"
|
override val fullVersion = "0.3.0+build.130"
|
||||||
override val major = 0
|
override val major = 0
|
||||||
override val minor = 2
|
override val minor = 3
|
||||||
override val patch = 8
|
override val patch = 0
|
||||||
override val preRelease = ""
|
override val preRelease = ""
|
||||||
override val buildMetadata = "build.106"
|
override val buildMetadata = "build.130"
|
||||||
override val buildDate = "2026-01-18T01:02:24.139170Z"
|
override val buildDate = "2026-01-19T21:42:11.032491Z"
|
||||||
override val displayName = "Utility Library"
|
override val displayName = "Utility Library"
|
||||||
override val copyright = "Copyright (c) 2025-2026 Hartmut Bauer"
|
override val copyright = "Copyright (c) 2025-2026 Hartmut Bauer"
|
||||||
override val name = "util"
|
override val name = "util"
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+3
-6
@@ -386,19 +386,16 @@ private fun resolveParameterValue(
|
|||||||
args: List<String>,
|
args: List<String>,
|
||||||
currentIndex: Int,
|
currentIndex: Int,
|
||||||
key: String,
|
key: String,
|
||||||
inlineValue: String,
|
parameterValue: String,
|
||||||
param: CmdParameter,
|
param: CmdParameter,
|
||||||
): Pair<String, Int> {
|
): Pair<String, Int> {
|
||||||
return when {
|
return when {
|
||||||
param.type == ParameterType.BOOLEAN -> {
|
param.type == ParameterType.BOOLEAN -> {
|
||||||
if (inlineValue.isNotBlank()) throw ArgumentParserException.optionNoValue(key)
|
if (parameterValue.isNotBlank()) throw ArgumentParserException.optionNoValue(key)
|
||||||
TRUE to 0
|
TRUE to 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// --name=--irgendwas ist OK
|
parameterValue.isNotBlank() -> parameterValue to 0
|
||||||
inlineValue.isNotBlank() -> inlineValue to 0
|
|
||||||
|
|
||||||
// --name --irgendwas ist nicht OK
|
|
||||||
currentIndex < args.size - 1 && !ARGUMENT_PATTERN.matches(args[currentIndex + 1]) -> {
|
currentIndex < args.size - 1 && !ARGUMENT_PATTERN.matches(args[currentIndex + 1]) -> {
|
||||||
args[currentIndex + 1] to 1
|
args[currentIndex + 1] to 1
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package net.keinekekse.args
|
||||||
|
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
actual fun isStdInputReady(): Boolean =
|
||||||
|
(System.`in`.available() > 0)
|
||||||
|
|
||||||
|
actual fun exitApplication(rc: Int): Nothing =
|
||||||
|
exitProcess(rc)
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,352 +0,0 @@
|
|||||||
package net.keinekekse.args
|
|
||||||
|
|
||||||
import net.keinekekse.version.Version
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
class VerifyParameterException(message: String) : RuntimeException(message)
|
|
||||||
|
|
||||||
fun showParsingError(msg: String): Nothing {
|
|
||||||
println(ArgumentParserException.PARSING_ERROR_MSG.format(msg))
|
|
||||||
exitProcess(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Functional interface representing a runnable program execution logic.
|
|
||||||
*
|
|
||||||
* This interface allows defining a lambda function or block of code that
|
|
||||||
* processes a list of remaining arguments. It is intended to be used as
|
|
||||||
* a mechanism to encapsulate the execution logic for commands or subcommands
|
|
||||||
* in a command-line parsing framework.
|
|
||||||
*
|
|
||||||
* The primary use case for this interface is to define execution behavior
|
|
||||||
* for subcommands or global commands, commonly used in CLI applications.
|
|
||||||
*
|
|
||||||
* @constructor A single abstract method is provided to execute the specified logic.
|
|
||||||
*/
|
|
||||||
fun interface RunProgram {
|
|
||||||
fun run(remaining: List<String>)
|
|
||||||
}
|
|
||||||
|
|
||||||
class SubCommand private constructor(
|
|
||||||
internal val obj: SubCommandData,
|
|
||||||
val ignoreCase: Boolean = false,
|
|
||||||
) : AddParameter {
|
|
||||||
companion object {
|
|
||||||
internal fun create(obj: SubCommandData, ignoreCase: Boolean = false): SubCommand =
|
|
||||||
SubCommand(obj, ignoreCase)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addStringParameter(
|
|
||||||
long: String,
|
|
||||||
short: Char?,
|
|
||||||
description: String,
|
|
||||||
isMandatory: Boolean,
|
|
||||||
): ParameterValue<String> {
|
|
||||||
return createParameter(ParameterType.STRING, long, short, description, isMandatory)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addLongParameter(
|
|
||||||
long: String,
|
|
||||||
short: Char?,
|
|
||||||
description: String,
|
|
||||||
isMandatory: Boolean,
|
|
||||||
): ParameterValue<Long> {
|
|
||||||
return createParameter(ParameterType.LONG, long, short, description, isMandatory)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addBooleanParameter(
|
|
||||||
long: String,
|
|
||||||
short: Char?,
|
|
||||||
description: String,
|
|
||||||
isMandatory: Boolean,
|
|
||||||
): ParameterValue<Boolean> {
|
|
||||||
return createParameter(ParameterType.BOOLEAN, long, short, description, isMandatory)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setExecution(run: RunProgram) {
|
|
||||||
obj.setExecution(run)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> createParameter(
|
|
||||||
type: ParameterType<T>,
|
|
||||||
long: String,
|
|
||||||
short: Char?,
|
|
||||||
description: String,
|
|
||||||
isMandatory: Boolean,
|
|
||||||
): ParameterValue<T> {
|
|
||||||
val result = runCatching {
|
|
||||||
val long = long.handleCase(ignoreCase)
|
|
||||||
val short = short?.toString()?.handleCase(ignoreCase)
|
|
||||||
type.createOption(long, short, description, isMandatory).apply {
|
|
||||||
verifyParameter(this)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
obj.params[long] = this as ParameterData<Any>
|
|
||||||
short?.let {
|
|
||||||
obj.params[it] = this
|
|
||||||
}
|
|
||||||
}.prop
|
|
||||||
}
|
|
||||||
return result.getOrElse { showDefiningError(it.message ?: "") }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun verifyParameter(parameter: ParameterData<*>) {
|
|
||||||
if (!NAME_PATTERN.matches(parameter.long)) throw ArgumentParserException.invalidName(
|
|
||||||
parameter.long
|
|
||||||
)
|
|
||||||
if (obj.params.containsKey(parameter.long)) throw ArgumentParserException.optionExists(
|
|
||||||
parameter.long
|
|
||||||
)
|
|
||||||
parameter.short?.let {
|
|
||||||
if (!SHORT_NAME_PATTERN.matches(it)) {
|
|
||||||
throw ArgumentParserException.invalidShortName(it)
|
|
||||||
}
|
|
||||||
if (obj.params.containsKey(it)) throw ArgumentParserException.optionExists(it)
|
|
||||||
}
|
|
||||||
val n = parameter.long.lowercase()
|
|
||||||
if (n == "help" || n == "version") {
|
|
||||||
throw ArgumentParserException.noHelpOrVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class CommandLine(
|
|
||||||
val programVersion: Version,
|
|
||||||
val ignoreCase: Boolean = false,
|
|
||||||
val remainingDescription: String? = null, // null -> no remaining allowed
|
|
||||||
) : AddParameter {
|
|
||||||
private val globalSubCommand = SubCommand.create(SubCommandData(), ignoreCase)
|
|
||||||
private var subCommands = mutableMapOf<String, SubCommandData>()
|
|
||||||
|
|
||||||
override fun addStringParameter(
|
|
||||||
long: String,
|
|
||||||
short: Char?,
|
|
||||||
description: String,
|
|
||||||
isMandatory: Boolean,
|
|
||||||
): ParameterValue<String> {
|
|
||||||
return globalSubCommand.addStringParameter(long, short, description, isMandatory)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addLongParameter(
|
|
||||||
long: String,
|
|
||||||
short: Char?,
|
|
||||||
description: String,
|
|
||||||
isMandatory: Boolean,
|
|
||||||
): ParameterValue<Long> {
|
|
||||||
return globalSubCommand.addLongParameter(long, short, description, isMandatory)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addBooleanParameter(
|
|
||||||
long: String,
|
|
||||||
short: Char?,
|
|
||||||
description: String,
|
|
||||||
isMandatory: Boolean,
|
|
||||||
): ParameterValue<Boolean> {
|
|
||||||
return globalSubCommand.addBooleanParameter(long, short, description, isMandatory)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setExecution(run: RunProgram) {
|
|
||||||
globalSubCommand.setExecution(run)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addSubCommand(
|
|
||||||
name: String,
|
|
||||||
description: String,
|
|
||||||
aliases: List<String> = emptyList(),
|
|
||||||
): SubCommand {
|
|
||||||
val result = runCatching {
|
|
||||||
val name = verifySubCommand(name, ignoreCase)
|
|
||||||
val subData = SubCommandData(name, description, aliases)
|
|
||||||
subCommands[name] = subData
|
|
||||||
aliases.forEach { alias ->
|
|
||||||
val alias = verifySubCommand(alias, ignoreCase)
|
|
||||||
subCommands[alias] = subData
|
|
||||||
}
|
|
||||||
SubCommand.create(subData, ignoreCase)
|
|
||||||
}
|
|
||||||
return result.getOrElse { showDefiningError(it.message ?: "") }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseAndRun(vararg args: String) {
|
|
||||||
val result = runCatching {
|
|
||||||
val selectedSubCommand = parse(args.toList())
|
|
||||||
selectedSubCommand.execute(selectedSubCommand.additionalParameters)
|
|
||||||
}
|
|
||||||
result.exceptionOrNull()?.let { showHelp(it as Exception) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun verifySubCommand(cmd: String, ignoreCase: Boolean): String {
|
|
||||||
val cmd = cmd.handleCase(ignoreCase)
|
|
||||||
if (!matchSubCommand(cmd)) throw ArgumentParserException.invalidCommand(cmd)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parse(args: List<String>): SubCommandData {
|
|
||||||
if (args.isEmpty()) {
|
|
||||||
throw ArgumentParserException.commandRequired()
|
|
||||||
}
|
|
||||||
checkExecutors()
|
|
||||||
|
|
||||||
val remaining = parseParameters(args, globalSubCommand.obj)
|
|
||||||
validateMandatoryParameters(globalSubCommand.obj)
|
|
||||||
|
|
||||||
val resultSubCommand = parseSubCommand(remaining)
|
|
||||||
validateMandatoryParameters(resultSubCommand)
|
|
||||||
|
|
||||||
if (resultSubCommand.additionalParameters.isNotEmpty() && remainingDescription != null) {
|
|
||||||
throw ArgumentParserException.noMoreOptions()
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultSubCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseSubCommand(args: List<String>): SubCommandData {
|
|
||||||
val sub = args.firstOrNull()?.handleCase(ignoreCase)?.let { subCommands[it] }
|
|
||||||
?: return globalSubCommand.obj.apply { additionalParameters = args }
|
|
||||||
return sub.apply { additionalParameters = parseParameters(args.drop(1), this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun validateMandatoryParameters(subCommand: SubCommandData) {
|
|
||||||
subCommand.params.forEach { (_, parameter) ->
|
|
||||||
if (parameter.isMandatory && parameter.prop.value == null) {
|
|
||||||
throw ArgumentParserException.optionRequiresValue(parameter.long)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseParameters(args: List<String>, subCommand: SubCommandData): List<String> {
|
|
||||||
var index = 0
|
|
||||||
while (index < args.size) {
|
|
||||||
val parseResult = matchArgument(args[index]) ?: return args.drop(index)
|
|
||||||
|
|
||||||
var (key, inlineValue) = parseResult
|
|
||||||
checkForHelpOrVersion(key)
|
|
||||||
key = key.handleCase(ignoreCase)
|
|
||||||
|
|
||||||
val param = subCommand.params[key] ?: throw ArgumentParserException.unknownOption(key)
|
|
||||||
|
|
||||||
val (value, consumedExtra) = resolveParameterValue(args, index, key, inlineValue, param)
|
|
||||||
param.type.convertAndSet(value, param.prop)
|
|
||||||
|
|
||||||
index += 1 + consumedExtra
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveParameterValue(
|
|
||||||
args: List<String>,
|
|
||||||
currentIndex: Int,
|
|
||||||
key: String,
|
|
||||||
inlineValue: String,
|
|
||||||
param: ParameterData<Any>,
|
|
||||||
): Pair<String, Int> {
|
|
||||||
return when {
|
|
||||||
param.type == ParameterType.BOOLEAN -> {
|
|
||||||
if (inlineValue.isNotBlank()) throw ArgumentParserException.optionNoValue(key)
|
|
||||||
TRUE to 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// --name=--irgendwas ist OK
|
|
||||||
inlineValue.isNotBlank() -> inlineValue to 0
|
|
||||||
|
|
||||||
// --name --irgendwas ist nicht OK
|
|
||||||
currentIndex < args.size - 1 && !ARGUMENT_PATTERN.matches(args[currentIndex + 1]) -> {
|
|
||||||
args[currentIndex + 1] to 1
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> throw ArgumentParserException.optionRequiresValue(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkExecutors() {
|
|
||||||
globalSubCommand.obj.checkExecutors()
|
|
||||||
subCommands.values.forEach { it.checkExecutors() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkForHelpOrVersion(key: String) {
|
|
||||||
val k = key.lowercase()
|
|
||||||
if (k == "help") {
|
|
||||||
throw ArgumentParserException.showHelp()
|
|
||||||
} else if (k == "version") {
|
|
||||||
showVersion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showHelp(
|
|
||||||
e: Exception?,
|
|
||||||
): Nothing {
|
|
||||||
val sub = !subCommands.isEmpty()
|
|
||||||
val globalParam = globalSubCommand.obj.params.isNotEmpty()
|
|
||||||
val remain = remainingDescription != null
|
|
||||||
|
|
||||||
println("${programVersion.displayName} ${programVersion.version}, ${programVersion.copyright}\n")
|
|
||||||
println(programVersion.description)
|
|
||||||
println()
|
|
||||||
println(
|
|
||||||
"usage: ${programVersion.name} ${
|
|
||||||
if (sub)
|
|
||||||
if (globalParam) {
|
|
||||||
"[GLOBAL PARAMETER]"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
} + " subcommand [PARAMETER]"
|
|
||||||
else "[PARAMETER]"
|
|
||||||
} ${if (remain) "<$remainingDescription>" else ""}"
|
|
||||||
)
|
|
||||||
println()
|
|
||||||
if (globalParam) {
|
|
||||||
if (sub) {
|
|
||||||
println("Global Parameters:")
|
|
||||||
} else {
|
|
||||||
println("Parameters:")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showParameters(globalSubCommand.obj.params)
|
|
||||||
if (sub) {
|
|
||||||
println()
|
|
||||||
println("Subcommands:")
|
|
||||||
showSubcommands(subCommands)
|
|
||||||
}
|
|
||||||
e?.message?.let {
|
|
||||||
println()
|
|
||||||
println(ArgumentParserException.PARSING_ERROR_MSG.format(it))
|
|
||||||
println()
|
|
||||||
}
|
|
||||||
exitProcess(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showSubcommands(parameters: MutableMap<String, SubCommandData>) {
|
|
||||||
parameters.filter { it.key == it.value.name }.forEach { (_, param) ->
|
|
||||||
val aliases = buildString {
|
|
||||||
param.aliases.forEach { alias ->
|
|
||||||
append(" | $alias")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println("$INDENT${param.name}$aliases ${param.description}")
|
|
||||||
showParameters(param.params)
|
|
||||||
println()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showParameters(parameters: MutableMap<String, ParameterData<Any>>) {
|
|
||||||
var len = 0
|
|
||||||
parameters.filter { it.key == it.value.long }.map { (_, param) ->
|
|
||||||
len = maxOf(len, param.long.length + INDENT.length * 2 + 4 + 2)
|
|
||||||
Pair(INDENT + INDENT + (param.short?.let { "-$it, " } ?: " ") + "--${param.long}",
|
|
||||||
"${param.description} ${if (param.isMandatory) "*" else ""}")
|
|
||||||
}.forEach {
|
|
||||||
println(it.first.padEnd(len + 3) + it.second)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showVersion(): Nothing {
|
|
||||||
println("${programVersion.displayName} ${programVersion.fullVersion}, ${programVersion.copyright}\n")
|
|
||||||
exitProcess(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package net.keinekekse.args
|
||||||
|
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
import platform.posix.*
|
||||||
|
|
||||||
|
// macOS + Linux
|
||||||
|
|
||||||
|
@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
|
||||||
|
actual fun isStdInputReady(): Boolean {
|
||||||
|
return memScoped {
|
||||||
|
val pfd = alloc<pollfd>()
|
||||||
|
pfd.fd = STDIN_FILENO
|
||||||
|
pfd.events = POLLIN.toShort()
|
||||||
|
// timeout 0 means return immediately
|
||||||
|
poll(pfd.ptr.reinterpret(), 1u, 10) > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun exitApplication(rc: Int): Nothing {
|
||||||
|
exit(rc)
|
||||||
|
return Unit as Nothing
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package net.keinekekse.args
|
||||||
|
|
||||||
|
actual fun isStdInputReady(): Boolean {
|
||||||
|
TODO("Not implemented on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun exitApplication(rc: Int): Nothing {
|
||||||
|
TODO("Not implemented on this platform")
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package net.keinekekse.args
|
||||||
|
|
||||||
|
// js + wasmJs
|
||||||
|
|
||||||
|
actual fun isStdInputReady(): Boolean {
|
||||||
|
TODO("Not implemented for this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun exitApplication(rc: Int): Nothing {
|
||||||
|
TODO("Not implemented for this platform")
|
||||||
|
}
|
||||||
Reference in new issue
Block a user