Skip to content

Commit

Permalink
Set pipeline working directory
Browse files Browse the repository at this point in the history
  • Loading branch information
iamgio committed Jun 26, 2024
1 parent 9e986ba commit 31a0582
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class QuarkdownCommand : CliktCommand() {
PipelineOptions(
prettyOutput = prettyOutput,
wrapOutput = !noWrap,
workingDirectory = source?.parentFile,
errorHandler =
when {
strict -> StrictPipelineErrorHandler()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ package eu.iamgio.quarkdown.pipeline

import eu.iamgio.quarkdown.pipeline.error.BasePipelineErrorHandler
import eu.iamgio.quarkdown.pipeline.error.PipelineErrorHandler
import java.io.File

/**
* Read-only settings that affect different behaviors of a [Pipeline].
* @param prettyOutput whether the rendering stage should produce pretty output code
* @param wrapOutput whether the rendered code should be wrapped in a template code.
* For example, an HTML wrapper may add `<html><head>...</head><body>...</body></html>`,
* with the actual content injected in `body`
* @param workingDirectory the starting directory to use when resolving relative paths from function calls
* @param errorHandler the error handler strategy to use when an error occurs in the pipeline,
* during the processing of a Quarkdown file
*/
data class PipelineOptions(
val prettyOutput: Boolean = false,
val wrapOutput: Boolean = true,
val workingDirectory: File? = null,
val errorHandler: PipelineErrorHandler = BasePipelineErrorHandler(),
)
43 changes: 40 additions & 3 deletions stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Data.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ package eu.iamgio.quarkdown.stdlib
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
import eu.iamgio.quarkdown.ast.Table
import eu.iamgio.quarkdown.ast.Text
import eu.iamgio.quarkdown.context.Context
import eu.iamgio.quarkdown.function.error.FunctionRuntimeException
import eu.iamgio.quarkdown.function.reflect.Injected
import eu.iamgio.quarkdown.function.reflect.Name
import eu.iamgio.quarkdown.function.value.NodeValue
import eu.iamgio.quarkdown.function.value.StringValue
import eu.iamgio.quarkdown.function.value.data.Range
import eu.iamgio.quarkdown.function.value.data.subList
import eu.iamgio.quarkdown.function.value.wrappedAsValue
import java.io.File
import kotlin.io.path.Path

/**
* `Data` stdlib module exporter.
Expand All @@ -21,6 +25,34 @@ val Data: Module =
::csv,
)

/**
* @param path path of the file, relative or absolute (with extension)
* @param requireExistance whether the corresponding file must exist
* @return a [File] instance of the file located in [path].
* If the path is relative, the location is determined by the working directory of the pipeline.
* @throws FunctionRuntimeException if the file does not exist and [requireExistance] is `true`
*/
internal fun file(
context: Context,
path: String,
requireExistance: Boolean = true,
): File {
val workingDirectory = context.attachedPipeline?.options?.workingDirectory

val file =
if (workingDirectory != null && !Path(path).isAbsolute) {
File(workingDirectory, path)
} else {
File(path)
}

if (requireExistance && !file.exists()) {
throw FunctionRuntimeException("File $file does not exist.")
}

return file
}

/**
* @param path path of the file (with extension)
* @param lineRange range of lines to extract from the file.
Expand All @@ -29,10 +61,11 @@ val Data: Module =
*/
@Name("filecontent")
fun fileContent(
@Injected context: Context,
path: String,
lineRange: Range = Range.INFINITE,
): StringValue {
val file = File(path)
val file = file(context, path)

// If the range is infinite on both ends, the whole file is read.
if (lineRange.isInfinite) {
Expand All @@ -49,11 +82,15 @@ fun fileContent(
* @param path path of the CSV file (with extension) to show
* @return a table whose content is loaded from the file located in [path]
*/
fun csv(path: String): NodeValue {
fun csv(
@Injected context: Context,
path: String,
): NodeValue {
val file = file(context, path)
val columns = mutableMapOf<String, MutableList<String>>()

// CSV is read row-by-row, while the Table is built by columns.
csvReader().open(path) {
csvReader().open(file) {
readAllWithHeaderAsSequence()
.flatMap { it.entries }
.forEach { (header, content) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import eu.iamgio.quarkdown.function.expression.eval
import eu.iamgio.quarkdown.function.reflect.Injected
import eu.iamgio.quarkdown.function.value.OutputValue
import eu.iamgio.quarkdown.function.value.ValueFactory
import java.io.File

/**
* `Ecosystem` stdlib module exporter.
Expand All @@ -29,19 +28,20 @@ fun include(
path: String,
): OutputValue<*> {
// Read file content
val raw = File(path).readText()
val file = file(context, path)
val raw = file.readText()

// Evaluate the Quarkdown source.
// This automatically converts the source into a value (e.g. a node, a string, a number, etc.)
// and fills the current context with new declarations (e.g. variables, functions, link definitions, etc.)
val result =
ValueFactory.expression(raw, context)?.eval()
?: throw FunctionRuntimeException("Cannot include sub-file $path: the Quarkdown source could not be evaluated")
?: throw FunctionRuntimeException("Cannot include sub-file $file: the Quarkdown source could not be evaluated")

// The value must be an output value in order to comply with the function rules.
return result as? OutputValue<*>
?: throw FunctionRuntimeException(
"Cannot include sub-file $path: the evaluation of the Quarkdown source is not a suitable output value " +
"Cannot include sub-file $file: the evaluation of the Quarkdown source is not a suitable output value " +
"(${result::class.simpleName} found)",
)
}
15 changes: 10 additions & 5 deletions stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/DataTest.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package eu.iamgio.quarkdown.stdlib

import eu.iamgio.quarkdown.ast.MutableAstAttributes
import eu.iamgio.quarkdown.ast.Table
import eu.iamgio.quarkdown.ast.Text
import eu.iamgio.quarkdown.context.BaseContext
import eu.iamgio.quarkdown.flavor.quarkdown.QuarkdownFlavor
import eu.iamgio.quarkdown.function.value.data.Range
import kotlin.test.Test
import kotlin.test.assertEquals
Expand All @@ -15,35 +18,37 @@ private val LINE_SEPARATOR = System.lineSeparator()
* [Data] module tests.
*/
class DataTest {
private val context = BaseContext(MutableAstAttributes(), QuarkdownFlavor, emptySet())

@Test
fun `file contents`() {
val path = "$DATA_FOLDER/test.txt"

assertEquals(
"Line 1${LINE_SEPARATOR}Line 2${LINE_SEPARATOR}${LINE_SEPARATOR}Line 4${LINE_SEPARATOR}Line 5",
fileContent(path).unwrappedValue,
fileContent(context, path).unwrappedValue,
)

assertEquals(
"Line 2${LINE_SEPARATOR}${LINE_SEPARATOR}Line 4",
fileContent(path, Range(1, 3)).unwrappedValue,
fileContent(context, path, Range(1, 3)).unwrappedValue,
)

assertEquals(
"Line 1${LINE_SEPARATOR}Line 2",
fileContent(path, Range(null, 1)).unwrappedValue,
fileContent(context, path, Range(null, 1)).unwrappedValue,
)

assertEquals(
"Line 4${LINE_SEPARATOR}Line 5",
fileContent(path, Range(3, null)).unwrappedValue,
fileContent(context, path, Range(3, null)).unwrappedValue,
)
}

@Test
fun `csv table`() {
val path = "$DATA_FOLDER/people.csv"
val table = csv(path)
val table = csv(context, path)

assertIs<Table>(table.unwrappedValue)

Expand Down

0 comments on commit 31a0582

Please sign in to comment.