summaryrefslogtreecommitdiff
path: root/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt
diff options
context:
space:
mode:
Diffstat (limited to 'support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt')
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt164
1 files changed, 164 insertions, 0 deletions
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt
new file mode 100644
index 0000000000..00313a7714
--- /dev/null
+++ b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.arara.utils
+
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.IOException
+import java.io.OutputStream
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import kotlin.time.Duration
+import kotlin.time.ExperimentalTime
+import org.islandoftex.arara.Arara
+import org.islandoftex.arara.configuration.AraraSpec
+import org.islandoftex.arara.localization.LanguageController
+import org.islandoftex.arara.localization.Messages
+import org.islandoftex.arara.model.AraraException
+import org.islandoftex.arara.ruleset.Command
+import org.islandoftex.arara.ruleset.Conditional
+import org.slf4j.LoggerFactory
+import org.zeroturnaround.exec.InvalidExitValueException
+import org.zeroturnaround.exec.ProcessExecutor
+import org.zeroturnaround.exec.listener.ShutdownHookProcessDestroyer
+
+/**
+ * Implements interpreter utilitary methods.
+ *
+ * @author Island of TeX
+ * @version 5.0
+ * @since 4.0
+ */
+object InterpreterUtils {
+ // the application messages obtained from the
+ // language controller
+ private val messages = LanguageController
+
+ // get the logger context from a factory
+ private val logger = LoggerFactory.getLogger(InterpreterUtils::class.java)
+
+ /**
+ * Checks if the current conditional has a prior evaluation.
+ *
+ * @param conditional The current conditional object.
+ * @return A boolean value indicating if the current conditional has a prior
+ * evaluation.
+ */
+ fun runPriorEvaluation(conditional: Conditional): Boolean {
+ return if (Arara.config[AraraSpec.Execution.dryrun]) {
+ false
+ } else {
+ when (conditional.type) {
+ Conditional.ConditionalType.IF,
+ Conditional.ConditionalType.WHILE,
+ Conditional.ConditionalType.UNLESS -> true
+ else -> false
+ }
+ }
+ }
+
+ @ExperimentalTime
+ private fun getProcessExecutorForCommand(
+ command: Command,
+ buffer: OutputStream
+ ):
+ ProcessExecutor {
+ val timeOutValue = Arara.config[AraraSpec.Execution.timeoutValue]
+ var executor = ProcessExecutor().command((command).elements)
+ .directory(command.workingDirectory.absoluteFile)
+ .addDestroyer(ShutdownHookProcessDestroyer())
+ if (Arara.config[AraraSpec.Execution.timeout]) {
+ if (timeOutValue == Duration.ZERO) {
+ throw AraraException(messages.getMessage(Messages
+ .ERROR_RUN_TIMEOUT_INVALID_RANGE))
+ }
+ executor = executor.timeout(timeOutValue.toLongNanoseconds(),
+ TimeUnit.NANOSECONDS)
+ }
+ val tee = if (Arara.config[AraraSpec.Execution.verbose]) {
+ executor = executor.redirectInput(System.`in`)
+ TeeOutputStream(System.out, buffer)
+ } else {
+ TeeOutputStream(buffer)
+ }
+ executor = executor.redirectOutput(tee).redirectError(tee)
+ return executor
+ }
+
+ /**
+ * Runs the command in the underlying operating system.
+ *
+ * @param command An object representing the command.
+ * @return An integer value representing the exit code.
+ * @throws AraraException Something wrong happened, to be caught in the
+ * higher levels.
+ */
+ @ExperimentalTime
+ @Throws(AraraException::class)
+ fun run(command: Command): Int {
+ val buffer = ByteArrayOutputStream()
+ val executor = getProcessExecutorForCommand(command, buffer)
+ return executor.runCatching {
+ val exit = execute().exitValue
+ logger.info(DisplayUtils.displayOutputSeparator(
+ messages.getMessage(Messages.LOG_INFO_BEGIN_BUFFER)))
+ logger.info(buffer.toString())
+ logger.info(DisplayUtils.displayOutputSeparator(
+ messages.getMessage(Messages.LOG_INFO_END_BUFFER)))
+ exit
+ }.getOrElse {
+ throw AraraException(messages.getMessage(
+ when (it) {
+ is IOException -> Messages.ERROR_RUN_IO_EXCEPTION
+ is InterruptedException ->
+ Messages.ERROR_RUN_INTERRUPTED_EXCEPTION
+ is InvalidExitValueException ->
+ Messages.ERROR_RUN_INVALID_EXIT_VALUE_EXCEPTION
+ is TimeoutException ->
+ Messages.ERROR_RUN_TIMEOUT_EXCEPTION
+ else -> Messages.ERROR_RUN_GENERIC_EXCEPTION
+ }), it)
+ }
+ }
+
+ /**
+ * Builds the rule path based on the rule name and returns the corresponding
+ * file location.
+ *
+ * @param name The rule name.
+ * @return The rule file.
+ * @throws AraraException Something wrong happened, to be caught in the
+ * higher levels.
+ */
+ @Throws(AraraException::class)
+ fun buildRulePath(name: String): File? {
+ Arara.config[AraraSpec.Execution.rulePaths].forEach { path ->
+ val location = File(construct(path, name))
+ if (location.exists())
+ return location
+ }
+ return null
+ }
+
+ /**
+ * Constructs the path given the current path and the rule name.
+ *
+ * @param path The current path.
+ * @param name The rule name.
+ * @return The constructed path.
+ * @throws AraraException Something wrong happened, to be caught in the
+ * higher levels.
+ */
+ @Throws(AraraException::class)
+ fun construct(path: String, name: String): String {
+ val fileName = "$name.yaml"
+ val location = File(path)
+ return if (location.isAbsolute) {
+ location.resolve(fileName).toString()
+ } else {
+ Arara.config[AraraSpec.Execution.workingDirectory]
+ // first resolve the path (rule path) against the working
+ // directory, then the rule name we want to resolve
+ .resolve(path).resolve(fileName).toAbsolutePath().toString()
+ }
+ }
+}