No compile-time errors
This commit is contained in:
parent
738f41265f
commit
c0f869f6e3
@ -14,7 +14,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion by extra("0.7.0")
|
val dataforgeVersion by extra("0.7.1")
|
||||||
|
|
||||||
ksciencePublish {
|
ksciencePublish {
|
||||||
pom("https://github.com/SciProgCentre/snark") {
|
pom("https://github.com/SciProgCentre/snark") {
|
||||||
|
@ -2,12 +2,12 @@ package space.kscience.snark
|
|||||||
|
|
||||||
import space.kscience.dataforge.io.IOReader
|
import space.kscience.dataforge.io.IOReader
|
||||||
import space.kscience.dataforge.io.asBinary
|
import space.kscience.dataforge.io.asBinary
|
||||||
import space.kscience.dataforge.misc.DfId
|
import space.kscience.dataforge.misc.DfType
|
||||||
import space.kscience.snark.SnarkReader.Companion.DEFAULT_PRIORITY
|
import space.kscience.snark.SnarkReader.Companion.DEFAULT_PRIORITY
|
||||||
import space.kscience.snark.SnarkReader.Companion.DF_TYPE
|
import space.kscience.snark.SnarkReader.Companion.DF_TYPE
|
||||||
|
|
||||||
@DfId(DF_TYPE)
|
@DfType(DF_TYPE)
|
||||||
public interface SnarkReader<out T>: IOReader<T> {
|
public interface SnarkReader<out T> : IOReader<T> {
|
||||||
public val types: Set<String>
|
public val types: Set<String>
|
||||||
public val priority: Int get() = DEFAULT_PRIORITY
|
public val priority: Int get() = DEFAULT_PRIORITY
|
||||||
public fun readFrom(source: String): T
|
public fun readFrom(source: String): T
|
||||||
@ -39,5 +39,5 @@ private class SnarkReaderWrapper<out T>(
|
|||||||
public fun <T : Any> SnarkReader(
|
public fun <T : Any> SnarkReader(
|
||||||
reader: IOReader<T>,
|
reader: IOReader<T>,
|
||||||
vararg types: String,
|
vararg types: String,
|
||||||
priority: Int = DEFAULT_PRIORITY
|
priority: Int = DEFAULT_PRIORITY,
|
||||||
): SnarkReader<T> = SnarkReaderWrapper(reader, types.toSet(), priority)
|
): SnarkReader<T> = SnarkReaderWrapper(reader, types.toSet(), priority)
|
@ -1,12 +1,12 @@
|
|||||||
package space.kscience.snark
|
package space.kscience.snark
|
||||||
|
|
||||||
import space.kscience.dataforge.misc.DfId
|
import space.kscience.dataforge.misc.DfType
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object that conducts page-based text transformation. Like using link replacement or templating.
|
* An object that conducts page-based text transformation. Like using link replacement or templating.
|
||||||
*/
|
*/
|
||||||
@DfId(TextProcessor.DF_TYPE)
|
@DfType(TextProcessor.DF_TYPE)
|
||||||
public fun interface TextProcessor {
|
public fun interface TextProcessor {
|
||||||
|
|
||||||
public fun process(text: CharSequence): String
|
public fun process(text: CharSequence): String
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package space.kscience.snark
|
||||||
|
|
||||||
|
import space.kscience.dataforge.data.DataSet
|
||||||
|
import space.kscience.dataforge.data.filterByType
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
import space.kscience.dataforge.names.startsWith
|
||||||
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
|
public inline fun <reified R : Any> DataSet<*>.branch(
|
||||||
|
branchName: Name,
|
||||||
|
): DataSet<R> = filterByType(typeOf<R>()) { name, _ -> name.startsWith(branchName) }
|
||||||
|
|
||||||
|
public inline fun <reified R : Any> DataSet<*>.branch(
|
||||||
|
branchName: String,
|
||||||
|
): DataSet<R> = filterByType(typeOf<R>()) { name, _ -> name.startsWith(branchName.parseAsName()) }
|
@ -0,0 +1,81 @@
|
|||||||
|
package space.kscience.snark.html
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.html.FlowContent
|
||||||
|
import space.kscience.dataforge.data.*
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.int
|
||||||
|
import space.kscience.dataforge.meta.string
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
import space.kscience.dataforge.names.plus
|
||||||
|
import space.kscience.dataforge.names.startsWith
|
||||||
|
import space.kscience.snark.SnarkContext
|
||||||
|
|
||||||
|
public fun interface DataFragment {
|
||||||
|
public suspend fun FlowContent.renderFragment(page: PageContext, data: DataSet<*>)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
context(PageContext)
|
||||||
|
public fun FlowContent.htmlData(data: DataSet<*>, fragment: Data<DataFragment>): Unit = runBlocking(Dispatchers.IO) {
|
||||||
|
with(fragment.await()) { renderFragment(page, data) }
|
||||||
|
}
|
||||||
|
|
||||||
|
context(SnarkContext)
|
||||||
|
public val Data<*>.id: String
|
||||||
|
get() = meta["id"]?.string ?: "block[${hashCode()}]"
|
||||||
|
|
||||||
|
context(SnarkContext)
|
||||||
|
public val Data<*>.language: String?
|
||||||
|
get() = meta["language"].string?.lowercase()
|
||||||
|
|
||||||
|
context(SnarkContext)
|
||||||
|
public val Data<*>.order: Int?
|
||||||
|
get() = meta["order"]?.int
|
||||||
|
|
||||||
|
context(SnarkContext)
|
||||||
|
public val Data<*>.published: Boolean
|
||||||
|
get() = meta["published"].string != "false"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a Html builder by its full name
|
||||||
|
*/
|
||||||
|
context(SnarkContext)
|
||||||
|
public fun DataSet<*>.resolveHtmlOrNull(name: Name): Data<DataFragment>? {
|
||||||
|
val resolved = (getByType<DataFragment>(name) ?: getByType<DataFragment>(name + SiteContext.INDEX_PAGE_TOKEN))
|
||||||
|
|
||||||
|
return resolved?.takeIf {
|
||||||
|
it.published //TODO add language confirmation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context(SnarkContext)
|
||||||
|
public fun DataSet<*>.resolveHtmlOrNull(name: String): Data<DataFragment>? = resolveHtmlOrNull(name.parseAsName())
|
||||||
|
|
||||||
|
context(SnarkContext)
|
||||||
|
public fun DataSet<*>.resolveHtml(name: String): Data<DataFragment> = resolveHtmlOrNull(name)
|
||||||
|
?: error("Html fragment with name $name is not resolved")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all Html blocks using given name/meta filter
|
||||||
|
*/
|
||||||
|
context(SnarkContext)
|
||||||
|
public fun DataSet<*>.resolveAllHtml(
|
||||||
|
predicate: (name: Name, meta: Meta) -> Boolean,
|
||||||
|
): Map<Name, Data<DataFragment>> = filterByType<DataFragment> { name, meta ->
|
||||||
|
predicate(name, meta)
|
||||||
|
&& meta["published"].string != "false"
|
||||||
|
//TODO add language confirmation
|
||||||
|
}.asSequence().associate { it.name to it.data }
|
||||||
|
|
||||||
|
context(SnarkContext)
|
||||||
|
public fun DataSet<*>.findHtmlByContentType(
|
||||||
|
contentType: String,
|
||||||
|
baseName: Name = Name.EMPTY,
|
||||||
|
): Map<Name, Data<DataFragment>> = resolveAllHtml { name, meta ->
|
||||||
|
name.startsWith(baseName) && meta["content_type"].string == contentType
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
package space.kscience.snark.html
|
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.html.FlowContent
|
|
||||||
import kotlinx.html.TagConsumer
|
|
||||||
import space.kscience.dataforge.data.Data
|
|
||||||
import space.kscience.dataforge.data.await
|
|
||||||
import space.kscience.dataforge.meta.get
|
|
||||||
import space.kscience.dataforge.meta.int
|
|
||||||
import space.kscience.dataforge.meta.string
|
|
||||||
import space.kscience.snark.SnarkContext
|
|
||||||
|
|
||||||
|
|
||||||
//TODO replace by VisionForge type
|
|
||||||
|
|
||||||
public fun interface HtmlFragment {
|
|
||||||
public fun TagConsumer<*>.renderFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
public typealias HtmlData = Data<HtmlFragment>
|
|
||||||
|
|
||||||
|
|
||||||
public fun FlowContent.htmlData(page: PageContext, data: HtmlData): Unit = runBlocking(Dispatchers.IO) {
|
|
||||||
withSnarkPage(page) {
|
|
||||||
with(data.await()) { consumer.renderFragment() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context(SnarkContext)
|
|
||||||
public val Data<*>.id: String
|
|
||||||
get() = meta["id"]?.string ?: "block[${hashCode()}]"
|
|
||||||
|
|
||||||
context(SnarkContext)
|
|
||||||
public val Data<*>.language: String?
|
|
||||||
get() = meta["language"].string?.lowercase()
|
|
||||||
|
|
||||||
context(SnarkContext)
|
|
||||||
public val Data<*>.order: Int?
|
|
||||||
get() = meta["order"]?.int
|
|
||||||
|
|
||||||
context(SnarkContext)
|
|
||||||
public val Data<*>.published: Boolean
|
|
||||||
get() = meta["published"].string != "false"
|
|
@ -1,15 +1,29 @@
|
|||||||
package space.kscience.snark.html
|
package space.kscience.snark.html
|
||||||
|
|
||||||
import kotlinx.html.HTML
|
import kotlinx.html.HTML
|
||||||
|
import kotlinx.html.stream.createHTML
|
||||||
|
import kotlinx.html.visitTagAndFinalize
|
||||||
import space.kscience.dataforge.data.DataSet
|
import space.kscience.dataforge.data.DataSet
|
||||||
import space.kscience.dataforge.data.DataSetBuilder
|
import space.kscience.dataforge.data.DataSetBuilder
|
||||||
import space.kscience.dataforge.data.node
|
|
||||||
import space.kscience.dataforge.data.static
|
import space.kscience.dataforge.data.static
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
|
||||||
public fun interface HtmlPage {
|
public fun interface HtmlPage {
|
||||||
public fun HTML.renderPage(pageContext: PageContext, pageData: DataSet<*>)
|
public suspend fun HTML.renderPage(page: PageContext, data: DataSet<*>)
|
||||||
|
|
||||||
|
public companion object{
|
||||||
|
public suspend fun createHtmlString(pageContext: PageContext, page: HtmlPage, data: DataSet<*>): String{
|
||||||
|
return createHTML().run {
|
||||||
|
HTML(kotlinx.html.emptyMap, this, null).visitTagAndFinalize(this) {
|
||||||
|
with(page) {
|
||||||
|
renderPage(pageContext, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -22,6 +36,7 @@ public fun DataSetBuilder<Any>.page(name: Name, pageMeta: Meta = Meta.EMPTY, blo
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// if (data.type == typeOf<HtmlData>()) {
|
// if (data.type == typeOf<HtmlData>()) {
|
||||||
// val languageMeta: Meta = Language.forName(name)
|
// val languageMeta: Meta = Language.forName(name)
|
||||||
//
|
//
|
||||||
|
@ -11,7 +11,7 @@ import space.kscience.dataforge.names.asName
|
|||||||
import space.kscience.dataforge.names.parseAsName
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
|
||||||
public fun interface HtmlSite {
|
public fun interface HtmlSite {
|
||||||
public fun renderSite(siteContext: SiteContext, siteData: DataSet<Any>)
|
public suspend fun SiteContext.renderSite(data: DataSet<Any>)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun DataSetBuilder<Any>.site(
|
public fun DataSetBuilder<Any>.site(
|
||||||
|
@ -75,9 +75,9 @@ public val SiteContext.languagePrefix: Name
|
|||||||
get() = languages[language]?.let { it[Language::prefix.name].string ?: language }?.parseAsName() ?: Name.EMPTY
|
get() = languages[language]?.let { it[Language::prefix.name].string ?: language }?.parseAsName() ?: Name.EMPTY
|
||||||
|
|
||||||
@SnarkBuilder
|
@SnarkBuilder
|
||||||
public suspend fun SiteContext.multiLanguageSite(data: DataSet<Any>, languageMap: Map<String, Meta>, site: HtmlSite) {
|
public suspend fun SiteContext.multiLanguageSite(data: DataSet<Any>, languageMap: Map<String, Language>, site: HtmlSite) {
|
||||||
languageMap.forEach { (languageKey, languageMeta) ->
|
languageMap.forEach { (languageKey, language) ->
|
||||||
val prefix = languageMeta[Language::prefix.name].string ?: languageKey
|
val prefix = language.prefix ?: languageKey
|
||||||
val languageSiteMeta = Meta {
|
val languageSiteMeta = Meta {
|
||||||
SITE_LANGUAGE_KEY put languageKey
|
SITE_LANGUAGE_KEY put languageKey
|
||||||
SITE_LANGUAGES_KEY put Meta {
|
SITE_LANGUAGES_KEY put Meta {
|
||||||
|
@ -25,6 +25,8 @@ public fun Name.toWebPath(): String = tokens.joinToString(separator = "/") {
|
|||||||
@SnarkBuilder
|
@SnarkBuilder
|
||||||
public interface PageContext : SnarkContext {
|
public interface PageContext : SnarkContext {
|
||||||
|
|
||||||
|
public val site: SiteContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A metadata for a page. It should include site metadata
|
* A metadata for a page. It should include site metadata
|
||||||
*/
|
*/
|
||||||
@ -52,43 +54,4 @@ public fun PageContext.resolvePageRef(pageName: String): String = resolvePageRef
|
|||||||
|
|
||||||
public val PageContext.homeRef: String get() = resolvePageRef(SiteContext.INDEX_PAGE_TOKEN.asName())
|
public val PageContext.homeRef: String get() = resolvePageRef(SiteContext.INDEX_PAGE_TOKEN.asName())
|
||||||
|
|
||||||
public val PageContext.name: Name? get() = pageMeta["name"].string?.parseAsName()
|
public val PageContext.name: Name? get() = pageMeta["name"].string?.parseAsName()
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a Html builder by its full name
|
|
||||||
*/
|
|
||||||
context(SnarkContext)
|
|
||||||
public fun DataTree<*>.resolveHtmlOrNull(name: Name): HtmlData? {
|
|
||||||
val resolved = (getByType<HtmlFragment>(name) ?: getByType<HtmlFragment>(name + SiteContext.INDEX_PAGE_TOKEN))
|
|
||||||
|
|
||||||
return resolved?.takeIf {
|
|
||||||
it.published //TODO add language confirmation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context(SnarkContext)
|
|
||||||
public fun DataTree<*>.resolveHtmlOrNull(name: String): HtmlData? = resolveHtmlOrNull(name.parseAsName())
|
|
||||||
|
|
||||||
context(SnarkContext)
|
|
||||||
public fun DataTree<*>.resolveHtml(name: String): HtmlData = resolveHtmlOrNull(name)
|
|
||||||
?: error("Html fragment with name $name is not resolved")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find all Html blocks using given name/meta filter
|
|
||||||
*/
|
|
||||||
context(SnarkContext)
|
|
||||||
public fun DataTree<*>.resolveAllHtml(predicate: (name: Name, meta: Meta) -> Boolean): Map<Name, HtmlData> =
|
|
||||||
filterByType<HtmlFragment> { name, meta ->
|
|
||||||
predicate(name, meta)
|
|
||||||
&& meta["published"].string != "false"
|
|
||||||
//TODO add language confirmation
|
|
||||||
}.asSequence().associate { it.name to it.data }
|
|
||||||
|
|
||||||
|
|
||||||
context(SnarkContext)
|
|
||||||
public fun DataTree<*>.findByContentType(
|
|
||||||
contentType: String,
|
|
||||||
baseName: Name = Name.EMPTY,
|
|
||||||
): Map<Name, Data<HtmlFragment>> = resolveAllHtml { name, meta ->
|
|
||||||
name.startsWith(baseName) && meta["content_type"].string == contentType
|
|
||||||
}
|
|
@ -6,12 +6,11 @@ import space.kscience.dataforge.io.Binary
|
|||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.dataforge.meta.string
|
import space.kscience.dataforge.meta.string
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.*
|
||||||
import space.kscience.dataforge.names.NameToken
|
|
||||||
import space.kscience.dataforge.names.asName
|
|
||||||
import space.kscience.dataforge.workspace.Workspace
|
|
||||||
import space.kscience.snark.SnarkBuilder
|
import space.kscience.snark.SnarkBuilder
|
||||||
import space.kscience.snark.SnarkContext
|
import space.kscience.snark.SnarkContext
|
||||||
|
import kotlin.reflect.full.isSubtypeOf
|
||||||
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,6 +73,24 @@ public interface SiteContext : SnarkContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public suspend fun SiteContext.static(dataSet: DataSet<Binary>, prefix: Name = Name.EMPTY) {
|
||||||
|
dataSet.forEach { (name, data) ->
|
||||||
|
static(prefix + name, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public suspend fun SiteContext.static(dataSet: DataSet<*>, branch: String, prefix: String = branch) {
|
||||||
|
val branchName = branch.parseAsName()
|
||||||
|
val prefixName = prefix.parseAsName()
|
||||||
|
val binaryType = typeOf<Binary>()
|
||||||
|
dataSet.forEach { (name, data) ->
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
if (name.startsWith(branchName) && data.type.isSubtypeOf(binaryType)) {
|
||||||
|
static(prefixName + name, data as Data<Binary>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SnarkBuilder
|
@SnarkBuilder
|
||||||
public suspend fun SiteContext.page(
|
public suspend fun SiteContext.page(
|
||||||
route: Name,
|
route: Name,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package space.kscience.snark.html
|
package space.kscience.snark.html
|
||||||
|
|
||||||
import io.ktor.http.ContentType
|
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import kotlinx.html.unsafe
|
import kotlinx.html.unsafe
|
||||||
import kotlinx.io.Source
|
import kotlinx.io.Source
|
||||||
@ -12,25 +11,25 @@ import space.kscience.snark.SnarkReader
|
|||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
public object HtmlReader : SnarkReader<HtmlFragment> {
|
public object HtmlReader : SnarkReader<DataFragment> {
|
||||||
override val types: Set<String> = setOf("html")
|
override val types: Set<String> = setOf("html")
|
||||||
|
|
||||||
override fun readFrom(source: String): HtmlFragment = HtmlFragment {
|
override fun readFrom(source: String): DataFragment = DataFragment { _, _ ->
|
||||||
div {
|
div {
|
||||||
unsafe { +source }
|
unsafe { +source }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun readFrom(source: Source): HtmlFragment = readFrom(source.readString())
|
override fun readFrom(source: Source): DataFragment = readFrom(source.readString())
|
||||||
override val type: KType = typeOf<HtmlFragment>()
|
override val type: KType = typeOf<DataFragment>()
|
||||||
}
|
}
|
||||||
|
|
||||||
public object MarkdownReader : SnarkReader<HtmlFragment> {
|
public object MarkdownReader : SnarkReader<DataFragment> {
|
||||||
override val type: KType = typeOf<HtmlFragment>()
|
override val type: KType = typeOf<DataFragment>()
|
||||||
|
|
||||||
override val types: Set<String> = setOf("text/markdown", "md", "markdown")
|
override val types: Set<String> = setOf("text/markdown", "md", "markdown")
|
||||||
|
|
||||||
override fun readFrom(source: String): HtmlFragment = HtmlFragment {
|
override fun readFrom(source: String): DataFragment = DataFragment { _, _ ->
|
||||||
val parsedTree = markdownParser.buildMarkdownTreeFromString(source)
|
val parsedTree = markdownParser.buildMarkdownTreeFromString(source)
|
||||||
val htmlString = HtmlGenerator(source, parsedTree, markdownFlavor).generateHtml()
|
val htmlString = HtmlGenerator(source, parsedTree, markdownFlavor).generateHtml()
|
||||||
|
|
||||||
@ -44,9 +43,9 @@ public object MarkdownReader : SnarkReader<HtmlFragment> {
|
|||||||
private val markdownFlavor = CommonMarkFlavourDescriptor()
|
private val markdownFlavor = CommonMarkFlavourDescriptor()
|
||||||
private val markdownParser = MarkdownParser(markdownFlavor)
|
private val markdownParser = MarkdownParser(markdownFlavor)
|
||||||
|
|
||||||
override fun readFrom(source: Source): HtmlFragment = readFrom(source.readString())
|
override fun readFrom(source: Source): DataFragment = readFrom(source.readString())
|
||||||
|
|
||||||
public val snarkReader: SnarkReader<HtmlFragment> = SnarkReader(this, "text/markdown")
|
public val snarkReader: SnarkReader<DataFragment> = SnarkReader(this, "text/markdown")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import space.kscience.dataforge.names.Name
|
|||||||
import space.kscience.dataforge.names.isEmpty
|
import space.kscience.dataforge.names.isEmpty
|
||||||
import space.kscience.dataforge.names.plus
|
import space.kscience.dataforge.names.plus
|
||||||
import space.kscience.dataforge.workspace.FileData
|
import space.kscience.dataforge.workspace.FileData
|
||||||
import space.kscience.dataforge.workspace.Workspace
|
|
||||||
import space.kscience.snark.html.*
|
import space.kscience.snark.html.*
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
@ -32,7 +31,6 @@ internal class StaticSiteContext(
|
|||||||
) : SiteContext {
|
) : SiteContext {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// @OptIn(ExperimentalPathApi::class)
|
// @OptIn(ExperimentalPathApi::class)
|
||||||
// private suspend fun files(item: DataTreeItem<Any>, routeName: Name) {
|
// private suspend fun files(item: DataTreeItem<Any>, routeName: Name) {
|
||||||
// //try using direct file rendering
|
// //try using direct file rendering
|
||||||
@ -91,30 +89,23 @@ internal class StaticSiteContext(
|
|||||||
"${baseUrl.removeSuffix("/")}/$ref"
|
"${baseUrl.removeSuffix("/")}/$ref"
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class StaticPageContext(override val pageMeta: Meta) : PageContext {
|
class StaticPageContext(override val site: StaticSiteContext, override val pageMeta: Meta) : PageContext {
|
||||||
|
|
||||||
override fun resolveRef(ref: String): String =
|
override fun resolveRef(ref: String): String =
|
||||||
this@StaticSiteContext.resolveRef(this@StaticSiteContext.baseUrl, ref)
|
site.resolveRef(site.baseUrl, ref)
|
||||||
|
|
||||||
override fun resolvePageRef(pageName: Name, relative: Boolean): String = resolveRef(
|
override fun resolvePageRef(pageName: Name, relative: Boolean): String = resolveRef(
|
||||||
(if (relative) this@StaticSiteContext.route + pageName else pageName).toWebPath() + ".html"
|
(if (relative) site.route + pageName else pageName).toWebPath() + ".html"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun page(route: Name, data: DataSet<Any>, pageMeta: Meta, htmlPage: HtmlPage) {
|
override suspend fun page(route: Name, data: DataSet<Any>, pageMeta: Meta, htmlPage: HtmlPage) {
|
||||||
|
|
||||||
val htmlBuilder = createHTML()
|
|
||||||
|
|
||||||
val modifiedPageMeta = pageMeta.toMutableMeta().apply {
|
val modifiedPageMeta = pageMeta.toMutableMeta().apply {
|
||||||
"name" put route.toString()
|
"name" put route.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlBuilder.html {
|
|
||||||
with(htmlPage) {
|
|
||||||
renderPage(StaticPageContext(Laminate(modifiedPageMeta, siteMeta)), data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val newPath = if (route.isEmpty()) {
|
val newPath = if (route.isEmpty()) {
|
||||||
outputPath.resolve("index.html")
|
outputPath.resolve("index.html")
|
||||||
} else {
|
} else {
|
||||||
@ -122,17 +113,20 @@ internal class StaticSiteContext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
newPath.parent.createDirectories()
|
newPath.parent.createDirectories()
|
||||||
newPath.writeText(htmlBuilder.finalize())
|
|
||||||
|
val pageContext = StaticPageContext(this, Laminate(modifiedPageMeta, siteMeta))
|
||||||
|
newPath.writeText(HtmlPage.createHtmlString(pageContext,htmlPage, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun site(route: Name, data: DataSet<Any>, siteMeta: Meta, htmlSite: HtmlSite) {
|
override suspend fun site(route: Name, data: DataSet<Any>, siteMeta: Meta, htmlSite: HtmlSite) {
|
||||||
val subSiteContext = StaticSiteContext(
|
with(htmlSite) {
|
||||||
siteMeta = Laminate(siteMeta, this.siteMeta),
|
StaticSiteContext(
|
||||||
baseUrl = if (baseUrl == "") "" else resolveRef(baseUrl, route.toWebPath()),
|
siteMeta = Laminate(siteMeta, this@StaticSiteContext.siteMeta),
|
||||||
route = Name.EMPTY,
|
baseUrl = if (baseUrl == "") "" else resolveRef(baseUrl, route.toWebPath()),
|
||||||
outputPath = outputPath.resolve(route.toWebPath())
|
route = Name.EMPTY,
|
||||||
)
|
outputPath = outputPath.resolve(route.toWebPath())
|
||||||
htmlSite.renderSite(subSiteContext, data)
|
).renderSite(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,31 @@
|
|||||||
package space.kscience.snark.ktor
|
package space.kscience.snark.ktor
|
||||||
|
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.*
|
||||||
import io.ktor.http.URLBuilder
|
import io.ktor.http.content.TextContent
|
||||||
import io.ktor.http.URLProtocol
|
|
||||||
import io.ktor.http.fromFileExtension
|
|
||||||
import io.ktor.server.application.Application
|
import io.ktor.server.application.Application
|
||||||
import io.ktor.server.application.call
|
import io.ktor.server.application.call
|
||||||
import io.ktor.server.html.respondHtml
|
|
||||||
import io.ktor.server.http.content.staticFiles
|
import io.ktor.server.http.content.staticFiles
|
||||||
import io.ktor.server.plugins.origin
|
import io.ktor.server.plugins.origin
|
||||||
|
import io.ktor.server.response.respond
|
||||||
import io.ktor.server.response.respondBytes
|
import io.ktor.server.response.respondBytes
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.Route
|
||||||
import kotlinx.css.CssBuilder
|
import io.ktor.server.routing.createRouteFromPath
|
||||||
import kotlinx.html.CommonAttributeGroupFacade
|
import io.ktor.server.routing.get
|
||||||
import kotlinx.html.head
|
import io.ktor.server.routing.routing
|
||||||
import kotlinx.html.style
|
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.ContextAware
|
import space.kscience.dataforge.context.ContextAware
|
||||||
import space.kscience.dataforge.context.error
|
import space.kscience.dataforge.context.error
|
||||||
import space.kscience.dataforge.context.logger
|
import space.kscience.dataforge.context.logger
|
||||||
import space.kscience.dataforge.data.*
|
import space.kscience.dataforge.data.Data
|
||||||
|
import space.kscience.dataforge.data.DataSet
|
||||||
|
import space.kscience.dataforge.data.DataTree
|
||||||
|
import space.kscience.dataforge.data.await
|
||||||
import space.kscience.dataforge.io.Binary
|
import space.kscience.dataforge.io.Binary
|
||||||
import space.kscience.dataforge.io.toByteArray
|
import space.kscience.dataforge.io.toByteArray
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.Laminate
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.string
|
||||||
|
import space.kscience.dataforge.meta.toMutableMeta
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.cutLast
|
import space.kscience.dataforge.names.cutLast
|
||||||
import space.kscience.dataforge.names.endsWith
|
import space.kscience.dataforge.names.endsWith
|
||||||
@ -34,11 +37,11 @@ import kotlin.contracts.InvocationKind
|
|||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
public fun CommonAttributeGroupFacade.css(block: CssBuilder.() -> Unit) {
|
//public fun CommonAttributeGroupFacade.css(block: CssBuilder.() -> Unit) {
|
||||||
style = CssBuilder().block().toString()
|
// style = CssBuilder().block().toString()
|
||||||
}
|
//}
|
||||||
|
|
||||||
public class KtorSiteBuilder(
|
public class KtorSiteContext(
|
||||||
override val context: Context,
|
override val context: Context,
|
||||||
override val siteMeta: Meta,
|
override val siteMeta: Meta,
|
||||||
private val baseUrl: String,
|
private val baseUrl: String,
|
||||||
@ -87,18 +90,19 @@ public class KtorSiteBuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private inner class KtorPageContext(
|
private class KtorPageContext(
|
||||||
|
override val site: KtorSiteContext,
|
||||||
val pageBaseUrl: String,
|
val pageBaseUrl: String,
|
||||||
override val pageMeta: Meta,
|
override val pageMeta: Meta,
|
||||||
) : PageContext {
|
) : PageContext {
|
||||||
|
|
||||||
override fun resolveRef(ref: String): String = this@KtorSiteBuilder.resolveRef(pageBaseUrl, ref)
|
override fun resolveRef(ref: String): String = site.resolveRef(pageBaseUrl, ref)
|
||||||
|
|
||||||
override fun resolvePageRef(
|
override fun resolvePageRef(
|
||||||
pageName: Name,
|
pageName: Name,
|
||||||
relative: Boolean,
|
relative: Boolean,
|
||||||
): String {
|
): String {
|
||||||
val fullPageName = if (relative) this@KtorSiteBuilder.route + pageName else pageName
|
val fullPageName = if (relative) site.route + pageName else pageName
|
||||||
return if (fullPageName.endsWith(SiteContext.INDEX_PAGE_TOKEN)) {
|
return if (fullPageName.endsWith(SiteContext.INDEX_PAGE_TOKEN)) {
|
||||||
resolveRef(fullPageName.cutLast().toWebPath())
|
resolveRef(fullPageName.cutLast().toWebPath())
|
||||||
} else {
|
} else {
|
||||||
@ -121,27 +125,25 @@ public class KtorSiteBuilder(
|
|||||||
"name" put route.toString()
|
"name" put route.toString()
|
||||||
"url" put url.buildString()
|
"url" put url.buildString()
|
||||||
}
|
}
|
||||||
val pageBuilder = KtorPageContext(url.buildString(), Laminate(modifiedPageMeta, siteMeta))
|
val pageContext =
|
||||||
|
KtorPageContext(this@KtorSiteContext, url.buildString(), Laminate(modifiedPageMeta, siteMeta))
|
||||||
|
//render page in suspend environment
|
||||||
|
val html = HtmlPage.createHtmlString(pageContext, htmlPage, data)
|
||||||
|
|
||||||
call.respondHtml {
|
call.respond(TextContent(html, ContentType.Text.Html.withCharset(Charsets.UTF_8), HttpStatusCode.OK))
|
||||||
head {}
|
|
||||||
with(htmlPage) {
|
|
||||||
renderPage(pageBuilder, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun site(route: Name, data: DataSet<Any>, siteMeta: Meta, htmlSite: HtmlSite) {
|
override suspend fun site(route: Name, data: DataSet<Any>, siteMeta: Meta, htmlSite: HtmlSite) {
|
||||||
val context = KtorSiteBuilder(
|
with(htmlSite) {
|
||||||
context,
|
KtorSiteContext(
|
||||||
siteMeta = Laminate(siteMeta, this.siteMeta),
|
context,
|
||||||
baseUrl = resolveRef(baseUrl, route.toWebPath()),
|
siteMeta = Laminate(siteMeta, this@KtorSiteContext.siteMeta),
|
||||||
route = Name.EMPTY,
|
baseUrl = resolveRef(baseUrl, route.toWebPath()),
|
||||||
ktorRoute = ktorRoute.createRouteFromPath(route.toWebPath())
|
route = Name.EMPTY,
|
||||||
)
|
ktorRoute = ktorRoute.createRouteFromPath(route.toWebPath())
|
||||||
|
).renderSite(data)
|
||||||
htmlSite.renderSite(context, data)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -151,12 +153,12 @@ private fun Route.site(
|
|||||||
data: DataTree<*>,
|
data: DataTree<*>,
|
||||||
baseUrl: String = "",
|
baseUrl: String = "",
|
||||||
siteMeta: Meta = data.meta,
|
siteMeta: Meta = data.meta,
|
||||||
block: KtorSiteBuilder.() -> Unit,
|
block: KtorSiteContext.() -> Unit,
|
||||||
) {
|
) {
|
||||||
contract {
|
contract {
|
||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
}
|
}
|
||||||
block(KtorSiteBuilder(context, siteMeta, baseUrl, route = Name.EMPTY, this@Route))
|
block(KtorSiteContext(context, siteMeta, baseUrl, route = Name.EMPTY, this@Route))
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun Application.site(
|
public fun Application.site(
|
Loading…
Reference in New Issue
Block a user