diff --git a/snark-gradle-plugin/src/main/kotlin/SnarkGradlePlugin.kt b/snark-gradle-plugin/src/main/kotlin/SnarkGradlePlugin.kt index 2271e3f..33858a1 100644 --- a/snark-gradle-plugin/src/main/kotlin/SnarkGradlePlugin.kt +++ b/snark-gradle-plugin/src/main/kotlin/SnarkGradlePlugin.kt @@ -2,6 +2,7 @@ package space.kscience.snark.plugin import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.file.FileTree import org.gradle.kotlin.dsl.withType import java.io.File import java.time.LocalDateTime diff --git a/snark-html/src/main/kotlin/space/kscience/snark/html/SnarkHtmlPlugin.kt b/snark-html/src/main/kotlin/space/kscience/snark/html/SnarkHtmlPlugin.kt index 2da57c1..f7cf6bf 100644 --- a/snark-html/src/main/kotlin/space/kscience/snark/html/SnarkHtmlPlugin.kt +++ b/snark-html/src/main/kotlin/space/kscience/snark/html/SnarkHtmlPlugin.kt @@ -3,6 +3,7 @@ package space.kscience.snark.html import io.ktor.utils.io.core.readBytes import space.kscience.dataforge.context.* import space.kscience.dataforge.data.DataTree +import space.kscience.dataforge.data.node import space.kscience.dataforge.io.IOPlugin import space.kscience.dataforge.io.IOReader import space.kscience.dataforge.io.JsonMetaFormat @@ -20,6 +21,7 @@ import space.kscience.dataforge.workspace.readDataDirectory import space.kscience.snark.SnarkParser import java.nio.file.Path import kotlin.io.path.extension +import kotlin.io.path.toPath /** * A plugin used for rendering a [DataTree] as HTML @@ -106,13 +108,17 @@ public fun SnarkHtmlPlugin.readDirectory(path: Path): DataTree = io.readDat parser.asReader(context, meta) } -public fun SnarkHtmlPlugin.readResourceDirectory( - resource: String = "", - classLoader: ClassLoader = SnarkHtmlPlugin::class.java.classLoader, -): DataTree = readDirectory( - Path.of( - classLoader.getResource(resource)?.toURI() ?: error( - "Resource with name $resource is not resolved" - ) - ) -) \ No newline at end of file +public fun SnarkHtmlPlugin.readResources( + vararg resources: String, + classLoader: ClassLoader = Thread.currentThread().contextClassLoader, +): DataTree { +// require(resource.isNotBlank()) {"Can't mount root resource tree as data root"} + return DataTree { + resources.forEach { resource -> + val path = classLoader.getResource(resource)?.toURI()?.toPath() ?: error( + "Resource with name $resource is not resolved" + ) + node(resource, readDirectory(path)) + } + } +} \ No newline at end of file diff --git a/snark-html/src/main/kotlin/space/kscience/snark/html/StaticSiteBuilder.kt b/snark-html/src/main/kotlin/space/kscience/snark/html/StaticSiteBuilder.kt index b1dbdbe..63d0224 100644 --- a/snark-html/src/main/kotlin/space/kscience/snark/html/StaticSiteBuilder.kt +++ b/snark-html/src/main/kotlin/space/kscience/snark/html/StaticSiteBuilder.kt @@ -1,20 +1,28 @@ package space.kscience.snark.html +import io.ktor.utils.io.streams.asInput +import io.ktor.utils.io.streams.asOutput +import kotlinx.coroutines.runBlocking import kotlinx.html.HTML import kotlinx.html.html import kotlinx.html.stream.createHTML import space.kscience.dataforge.data.DataTree -import space.kscience.dataforge.meta.Laminate -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.toMutableMeta +import space.kscience.dataforge.data.DataTreeItem +import space.kscience.dataforge.data.await +import space.kscience.dataforge.data.getItem +import space.kscience.dataforge.io.Binary +import space.kscience.dataforge.io.toByteArray +import space.kscience.dataforge.io.writeBinary +import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.plus -import java.nio.file.Files +import space.kscience.dataforge.workspace.FileData import java.nio.file.Path import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.io.path.* +import kotlin.reflect.typeOf /** @@ -29,19 +37,55 @@ internal class StaticSiteBuilder( private val outputPath: Path, ) : SiteBuilder { - override fun static(dataName: Name, routeName: Name) { - TODO("Not yet implemented") - } - private fun Path.copyRecursively(target: Path) { - Files.walk(this).forEach { source: Path -> - val destination: Path = target.resolve(source.relativeTo(this)) - if (!destination.isDirectory()) { - //avoid re-creating directories - source.copyTo(destination, true) +// private fun Path.copyRecursively(target: Path) { +// Files.walk(this).forEach { source: Path -> +// val destination: Path = target.resolve(source.relativeTo(this)) +// if (!destination.isDirectory()) { +// //avoid re-creating directories +// source.copyTo(destination, true) +// } +// } +// } + + @OptIn(ExperimentalPathApi::class) + private suspend fun files(item: DataTreeItem, routeName: Name) { + //try using direct file rendering + item.meta[FileData.FILE_PATH_KEY]?.string?.let { + val file = Path.of(it) + val targetPath = outputPath.resolve(routeName.toWebPath()) + targetPath.parent.createDirectories() + file.copyToRecursively(targetPath, followLinks = false) + //success, don't do anything else + return@files + } + + when (item) { + is DataTreeItem.Leaf -> { + val datum = item.data + if (datum.type != typeOf()) error("Can't directly serve file of type ${item.data.type}") + val targetPath = outputPath.resolve(routeName.toWebPath()) + val binary = datum.await() as Binary + targetPath.outputStream().asOutput().use{ + it.writeBinary(binary) + } + } + + is DataTreeItem.Node -> { + item.tree.items.forEach { (token, childItem) -> + files(childItem, routeName + token) + } } } } + + override fun static(dataName: Name, routeName: Name) { + val item: DataTreeItem = data.getItem(dataName) ?: error("Data with name $dataName is not resolved") + runBlocking { + files(item, routeName) + } + } + // // override fun file(file: Path, webPath: String) { // val targetPath = outputPath.resolve(webPath)