From f059de2805bca1efb41dddd06891186541793042 Mon Sep 17 00:00:00 2001 From: Jens Pots Date: Thu, 4 Jul 2024 16:55:35 +0200 Subject: [PATCH] feat: use type safe args everywhere --- src/main/kotlin/runner/jvm/JVMRunner.kt | 8 +++++--- src/main/kotlin/runner/jvm/Processor.kt | 24 ++-------------------- src/main/kotlin/std/FileReader.kt | 7 ++++--- src/main/kotlin/std/FileWriter.kt | 18 ++++++++-------- src/main/kotlin/std/HttpFetch.kt | 11 +++++----- src/main/kotlin/std/RDFValidator.kt | 17 +++++++-------- src/main/kotlin/std/Transparent.kt | 7 ++++--- src/test/kotlin/processors/TappedReader.kt | 5 +++-- src/test/kotlin/processors/TappedWriter.kt | 5 +++-- 9 files changed, 46 insertions(+), 56 deletions(-) diff --git a/src/main/kotlin/runner/jvm/JVMRunner.kt b/src/main/kotlin/runner/jvm/JVMRunner.kt index 97f5745..7db075c 100644 --- a/src/main/kotlin/runner/jvm/JVMRunner.kt +++ b/src/main/kotlin/runner/jvm/JVMRunner.kt @@ -13,6 +13,7 @@ import runner.Runner import technology.idlab.parser.intermediate.IRArgument import technology.idlab.parser.intermediate.IRParameter import technology.idlab.parser.intermediate.IRStage +import technology.idlab.runner.jvm.Arguments import technology.idlab.util.Log import technology.idlab.util.Log.Cause.* @@ -42,8 +43,9 @@ class JVMRunner( val arguments = this.instantiate(stage.processor.parameters.zip(stage.arguments)) /** Initialize the stage with the new map. */ - val constructor = clazz.getConstructor(Map::class.java) - this.stages[stage.uri] = constructor.newInstance(arguments) as Processor + val constructor = clazz.getConstructor(Arguments::class.java) + val args = Arguments.from(arguments) + this.stages[stage.uri] = constructor.newInstance(args) as Processor } override suspend fun exec() = coroutineScope { @@ -86,7 +88,7 @@ class JVMRunner( private fun instantiate( serialized: Map> - ): Map { + ): Map> { return serialized.mapValues { (_, map) -> val (parameter, arguments) = map diff --git a/src/main/kotlin/runner/jvm/Processor.kt b/src/main/kotlin/runner/jvm/Processor.kt index 6afa58b..ba80e42 100644 --- a/src/main/kotlin/runner/jvm/Processor.kt +++ b/src/main/kotlin/runner/jvm/Processor.kt @@ -1,6 +1,6 @@ package runner.jvm -import java.util.Optional +import technology.idlab.runner.jvm.Arguments import technology.idlab.util.Log abstract class Processor( @@ -8,7 +8,7 @@ abstract class Processor( * The arguments of a processor are stored in a map and can be accessed by name. At the time of * writing, the user must manually cast the arguments to the correct type. */ - private val arguments: Map, + protected val arguments: Arguments, ) { /** * Processors which wish to log messages should use the logger provided by the template class. @@ -16,25 +16,5 @@ abstract class Processor( */ @JvmField protected val log = Log.shared - fun getArgument(name: String): T { - val result = arguments.get(name) as List ?: log.fatal("Argument $name is missing") - return result.get(0) ?: log.fatal("Argument $name is missing") - } - - fun getNullableArgument(name: String): T? { - val result = arguments.get(name) as List? - return result?.get(0) - } - - fun getOptionalArgument(name: String): Optional { - val result = (arguments.get(name) as List?)?.get(0) ?: log.fatal("Argument $name is missing") - - if (result is Optional<*>) { - return result as Optional - } - - log.fatal("Argument $name is not optional") - } - abstract suspend fun exec() } diff --git a/src/main/kotlin/std/FileReader.kt b/src/main/kotlin/std/FileReader.kt index 2f879d4..0b14b2d 100644 --- a/src/main/kotlin/std/FileReader.kt +++ b/src/main/kotlin/std/FileReader.kt @@ -3,11 +3,12 @@ package technology.idlab.std import java.io.File import runner.jvm.Processor import runner.jvm.Writer +import technology.idlab.runner.jvm.Arguments -class FileReader(args: Map) : Processor(args) { +class FileReader(args: Arguments) : Processor(args) { /** Arguments */ - private val path: String = this.getArgument("path") - private val output: Writer = this.getArgument("output") + private val path: String = arguments["path"] + private val output: Writer = arguments["output"] /** Read the file as a single byte array and push it down the pipeline. */ override suspend fun exec() { diff --git a/src/main/kotlin/std/FileWriter.kt b/src/main/kotlin/std/FileWriter.kt index f956f18..c2b62e0 100644 --- a/src/main/kotlin/std/FileWriter.kt +++ b/src/main/kotlin/std/FileWriter.kt @@ -3,31 +3,33 @@ package technology.idlab.std import java.io.File import runner.jvm.Processor import runner.jvm.Reader +import technology.idlab.runner.jvm.Arguments -class FileWriter(args: Map) : Processor(args) { +class FileWriter(args: Arguments) : Processor(args) { /** Processor default values. */ private val overwriteDefault = true private val appendDefault = false /** Arguments */ - private val file = File(this.getArgument("path")) - private val input: Reader = this.getArgument("input") - private val overwrite = this.getOptionalArgument("overwrite") - private val append = this.getOptionalArgument("append") + private val path: String = arguments["path"] + private val file = File(path) + private val input: Reader = arguments["input"] + private val overwrite: Boolean? = arguments["overwrite"] + private val append: Boolean? = arguments["append"] init { // Sanity check. - if (overwrite.orElse(false) && append.orElse(false)) { + if (overwrite == true && append == true) { log.fatal("Cannot overwrite and append at the same time") } // Do not overwrite the file if it exists. - if (file.exists() && !overwrite.orElse(overwriteDefault)) { + if (file.exists() && !(overwrite ?: overwriteDefault)) { log.fatal("File ${file.path} already exists") } // Overwrite file if not exists. - if (file.exists() && !append.orElse(appendDefault)) { + if (file.exists() && !(append ?: appendDefault)) { file.writeBytes(ByteArray(0)) } } diff --git a/src/main/kotlin/std/HttpFetch.kt b/src/main/kotlin/std/HttpFetch.kt index 20b96b2..59bb639 100644 --- a/src/main/kotlin/std/HttpFetch.kt +++ b/src/main/kotlin/std/HttpFetch.kt @@ -8,16 +8,17 @@ import io.ktor.client.statement.* import io.ktor.http.* import runner.jvm.Processor import runner.jvm.Writer +import technology.idlab.runner.jvm.Arguments -class HttpFetch(args: Map) : Processor(args) { +class HttpFetch(args: Arguments) : Processor(args) { /** Meta configuration. */ private var engine: HttpClientEngine = CIO.create() /** Parameters. */ - private val endpoint = this.getArgument("endpoint") - private val output = this.getArgument("output") - private val headers = this.getArgument>("headers") - private val method = this.getNullableArgument("method") ?: "GET" + private val endpoint: String = arguments["endpoint"] + private val output: Writer = arguments["output"] + private val headers: Array = arguments["headers"] + private val method: String = arguments.get("method") ?: "GET" /** Prebuild request. */ private val builder = HttpRequestBuilder() diff --git a/src/main/kotlin/std/RDFValidator.kt b/src/main/kotlin/std/RDFValidator.kt index ce8700c..f4083e3 100644 --- a/src/main/kotlin/std/RDFValidator.kt +++ b/src/main/kotlin/std/RDFValidator.kt @@ -10,18 +10,19 @@ import runner.jvm.Processor import runner.jvm.Reader import runner.jvm.Writer import technology.idlab.extensions.readModelRecursively +import technology.idlab.runner.jvm.Arguments import technology.idlab.util.Log -class RDFValidator(args: Map) : Processor(args) { +class RDFValidator(args: Arguments) : Processor(args) { /** Default values. */ private val errorIsFatalDefault = false private val printReportDefault = false /** Arguments. */ - private val errorIsFatal = this.getOptionalArgument("error_is_fatal") - private val printReport = this.getOptionalArgument("print_report") - private val input = this.getArgument("input") - private val output = this.getArgument("output") + private val errorIsFatal: Boolean? = arguments["error_is_fatal"] + private val printReport: Boolean? = arguments["print_report"] + private val input: Reader = arguments["input"] + private val output: Writer = arguments["output"] /** Runtime fields. */ private val shapes: Graph @@ -30,7 +31,7 @@ class RDFValidator(args: Map) : Processor(args) { // Initialize the shape graph and validator. init { - val path = this.getArgument("shapes") + val path: String = arguments["shapes"] val file = File(path) val shapesModel = @@ -65,14 +66,14 @@ class RDFValidator(args: Map) : Processor(args) { output.push(res) } else { // Print the report if required. - if (printReport.orElse(printReportDefault)) { + if (printReport ?: printReportDefault) { val out = ByteArrayOutputStream() report.model.write(out, "TURTLE") Log.shared.info(out.toString()) } // Check if we can continue after an error. - if (errorIsFatal.orElse(errorIsFatalDefault)) { + if (errorIsFatal ?: errorIsFatalDefault) { Log.shared.fatal("Validation error is fatal.") } } diff --git a/src/main/kotlin/std/Transparent.kt b/src/main/kotlin/std/Transparent.kt index a8cca73..d517598 100644 --- a/src/main/kotlin/std/Transparent.kt +++ b/src/main/kotlin/std/Transparent.kt @@ -3,10 +3,11 @@ package technology.idlab.std import runner.jvm.Processor import runner.jvm.Reader import runner.jvm.Writer +import technology.idlab.runner.jvm.Arguments -class Transparent(args: Map) : Processor(args) { - private val input = this.getArgument("input") - private val output = this.getArgument("output") +class Transparent(args: Arguments) : Processor(args) { + private val input: Reader = arguments["input"] + private val output: Writer = arguments["output"] override suspend fun exec() { output.push(input.read()) diff --git a/src/test/kotlin/processors/TappedReader.kt b/src/test/kotlin/processors/TappedReader.kt index 3c19bb2..0b3a168 100644 --- a/src/test/kotlin/processors/TappedReader.kt +++ b/src/test/kotlin/processors/TappedReader.kt @@ -8,15 +8,16 @@ import technology.idlab.parser.intermediate.IRArgument import technology.idlab.parser.intermediate.IRParameter import technology.idlab.parser.intermediate.IRProcessor import technology.idlab.parser.intermediate.IRStage +import technology.idlab.runner.jvm.Arguments /** * The TappedReader processor provides a convenient way to read data from the pipeline during * testing. All incoming data will be written to a global channel, which can be used directly during * testing to read data from. */ -class TappedReader(args: Map) : Processor(args) { +class TappedReader(args: Arguments) : Processor(args) { /** The channel which is exposed to the pipeline. */ - private val input = this.getArgument("input") + private val input: Reader = arguments["input"] /** Continuously read data from the input and write it to the global channel. */ override suspend fun exec() { diff --git a/src/test/kotlin/processors/TappedWriter.kt b/src/test/kotlin/processors/TappedWriter.kt index 4d02808..062bf0a 100644 --- a/src/test/kotlin/processors/TappedWriter.kt +++ b/src/test/kotlin/processors/TappedWriter.kt @@ -8,15 +8,16 @@ import technology.idlab.parser.intermediate.IRArgument import technology.idlab.parser.intermediate.IRParameter import technology.idlab.parser.intermediate.IRProcessor import technology.idlab.parser.intermediate.IRStage +import technology.idlab.runner.jvm.Arguments /** * The TappedWriter processor provides a convenient way to write data into the pipeline during * testing. All instances listen to a global channel, which can be used directly during testing to * write date to. */ -class TappedWriter(args: Map) : Processor(args) { +class TappedWriter(args: Arguments) : Processor(args) { /** Writer which is exposed to the pipeline. */ - private val output = this.getArgument("output") + private val output: Writer = arguments["output"] /** Continuously read data from the global channel and write it to the output. */ override suspend fun exec() {