Skip to content

Commit

Permalink
feat: use type safe args everywhere
Browse files Browse the repository at this point in the history
  • Loading branch information
jenspots committed Jul 4, 2024
1 parent de311d1 commit f059de2
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 56 deletions.
8 changes: 5 additions & 3 deletions src/main/kotlin/runner/jvm/JVMRunner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.*

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -86,7 +88,7 @@ class JVMRunner(

private fun instantiate(
serialized: Map<String, Pair<IRParameter, IRArgument>>
): Map<String, Any> {
): Map<String, List<Any>> {
return serialized.mapValues { (_, map) ->
val (parameter, arguments) = map

Expand Down
24 changes: 2 additions & 22 deletions src/main/kotlin/runner/jvm/Processor.kt
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
package runner.jvm

import java.util.Optional
import technology.idlab.runner.jvm.Arguments
import technology.idlab.util.Log

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<String, Any>,
protected val arguments: Arguments,
) {
/**
* Processors which wish to log messages should use the logger provided by the template class.
* This logger is created with the name of the class which extends the template.
*/
@JvmField protected val log = Log.shared

fun <T> getArgument(name: String): T {
val result = arguments.get(name) as List<T> ?: log.fatal("Argument $name is missing")
return result.get(0) ?: log.fatal("Argument $name is missing")
}

fun <T> getNullableArgument(name: String): T? {
val result = arguments.get(name) as List<T>?
return result?.get(0)
}

fun <T> getOptionalArgument(name: String): Optional<T> {
val result = (arguments.get(name) as List<T>?)?.get(0) ?: log.fatal("Argument $name is missing")

if (result is Optional<*>) {
return result as Optional<T>
}

log.fatal("Argument $name is not optional")
}

abstract suspend fun exec()
}
7 changes: 4 additions & 3 deletions src/main/kotlin/std/FileReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Any>) : 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() {
Expand Down
18 changes: 10 additions & 8 deletions src/main/kotlin/std/FileWriter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Any>) : 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<String>("path"))
private val input: Reader = this.getArgument("input")
private val overwrite = this.getOptionalArgument<Boolean>("overwrite")
private val append = this.getOptionalArgument<Boolean>("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))
}
}
Expand Down
11 changes: 6 additions & 5 deletions src/main/kotlin/std/HttpFetch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Any>) : Processor(args) {
class HttpFetch(args: Arguments) : Processor(args) {
/** Meta configuration. */
private var engine: HttpClientEngine = CIO.create()

/** Parameters. */
private val endpoint = this.getArgument<String>("endpoint")
private val output = this.getArgument<Writer>("output")
private val headers = this.getArgument<Array<String>>("headers")
private val method = this.getNullableArgument<String>("method") ?: "GET"
private val endpoint: String = arguments["endpoint"]
private val output: Writer = arguments["output"]
private val headers: Array<String> = arguments["headers"]
private val method: String = arguments.get<String?>("method") ?: "GET"

/** Prebuild request. */
private val builder = HttpRequestBuilder()
Expand Down
17 changes: 9 additions & 8 deletions src/main/kotlin/std/RDFValidator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Any>) : Processor(args) {
class RDFValidator(args: Arguments) : Processor(args) {
/** Default values. */
private val errorIsFatalDefault = false
private val printReportDefault = false

/** Arguments. */
private val errorIsFatal = this.getOptionalArgument<Boolean>("error_is_fatal")
private val printReport = this.getOptionalArgument<Boolean>("print_report")
private val input = this.getArgument<Reader>("input")
private val output = this.getArgument<Writer>("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
Expand All @@ -30,7 +31,7 @@ class RDFValidator(args: Map<String, Any>) : Processor(args) {

// Initialize the shape graph and validator.
init {
val path = this.getArgument<String>("shapes")
val path: String = arguments["shapes"]
val file = File(path)

val shapesModel =
Expand Down Expand Up @@ -65,14 +66,14 @@ class RDFValidator(args: Map<String, Any>) : 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.")
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/main/kotlin/std/Transparent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Any>) : Processor(args) {
private val input = this.getArgument<Reader>("input")
private val output = this.getArgument<Writer>("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())
Expand Down
5 changes: 3 additions & 2 deletions src/test/kotlin/processors/TappedReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Any>) : Processor(args) {
class TappedReader(args: Arguments) : Processor(args) {
/** The channel which is exposed to the pipeline. */
private val input = this.getArgument<Reader>("input")
private val input: Reader = arguments["input"]

/** Continuously read data from the input and write it to the global channel. */
override suspend fun exec() {
Expand Down
5 changes: 3 additions & 2 deletions src/test/kotlin/processors/TappedWriter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Any>) : Processor(args) {
class TappedWriter(args: Arguments) : Processor(args) {
/** Writer which is exposed to the pipeline. */
private val output = this.getArgument<Writer>("output")
private val output: Writer = arguments["output"]

/** Continuously read data from the global channel and write it to the output. */
override suspend fun exec() {
Expand Down

0 comments on commit f059de2

Please sign in to comment.