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 {
|
||||
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.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.DF_TYPE
|
||||
|
||||
@DfId(DF_TYPE)
|
||||
public interface SnarkReader<out T>: IOReader<T> {
|
||||
@DfType(DF_TYPE)
|
||||
public interface SnarkReader<out T> : IOReader<T> {
|
||||
public val types: Set<String>
|
||||
public val priority: Int get() = DEFAULT_PRIORITY
|
||||
public fun readFrom(source: String): T
|
||||
@ -39,5 +39,5 @@ private class SnarkReaderWrapper<out T>(
|
||||
public fun <T : Any> SnarkReader(
|
||||
reader: IOReader<T>,
|
||||
vararg types: String,
|
||||
priority: Int = DEFAULT_PRIORITY
|
||||
priority: Int = DEFAULT_PRIORITY,
|
||||
): SnarkReader<T> = SnarkReaderWrapper(reader, types.toSet(), priority)
|
@ -1,12 +1,12 @@
|
||||
package space.kscience.snark
|
||||
|
||||
import space.kscience.dataforge.misc.DfId
|
||||
import space.kscience.dataforge.misc.DfType
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
|
||||
import kotlinx.html.HTML
|
||||
import kotlinx.html.stream.createHTML
|
||||
import kotlinx.html.visitTagAndFinalize
|
||||
import space.kscience.dataforge.data.DataSet
|
||||
import space.kscience.dataforge.data.DataSetBuilder
|
||||
import space.kscience.dataforge.data.node
|
||||
import space.kscience.dataforge.data.static
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.names.Name
|
||||
|
||||
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>()) {
|
||||
// val languageMeta: Meta = Language.forName(name)
|
||||
//
|
||||
|
@ -11,7 +11,7 @@ import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.parseAsName
|
||||
|
||||
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(
|
||||
|
@ -75,9 +75,9 @@ public val SiteContext.languagePrefix: Name
|
||||
get() = languages[language]?.let { it[Language::prefix.name].string ?: language }?.parseAsName() ?: Name.EMPTY
|
||||
|
||||
@SnarkBuilder
|
||||
public suspend fun SiteContext.multiLanguageSite(data: DataSet<Any>, languageMap: Map<String, Meta>, site: HtmlSite) {
|
||||
languageMap.forEach { (languageKey, languageMeta) ->
|
||||
val prefix = languageMeta[Language::prefix.name].string ?: languageKey
|
||||
public suspend fun SiteContext.multiLanguageSite(data: DataSet<Any>, languageMap: Map<String, Language>, site: HtmlSite) {
|
||||
languageMap.forEach { (languageKey, language) ->
|
||||
val prefix = language.prefix ?: languageKey
|
||||
val languageSiteMeta = Meta {
|
||||
SITE_LANGUAGE_KEY put languageKey
|
||||
SITE_LANGUAGES_KEY put Meta {
|
||||
|
@ -25,6 +25,8 @@ public fun Name.toWebPath(): String = tokens.joinToString(separator = "/") {
|
||||
@SnarkBuilder
|
||||
public interface PageContext : SnarkContext {
|
||||
|
||||
public val site: SiteContext
|
||||
|
||||
/**
|
||||
* 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.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
|
||||
}
|
||||
public val PageContext.name: Name? get() = pageMeta["name"].string?.parseAsName()
|
@ -6,12 +6,11 @@ import space.kscience.dataforge.io.Binary
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.workspace.Workspace
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.snark.SnarkBuilder
|
||||
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
|
||||
public suspend fun SiteContext.page(
|
||||
route: Name,
|
||||
|
@ -1,6 +1,5 @@
|
||||
package space.kscience.snark.html
|
||||
|
||||
import io.ktor.http.ContentType
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.unsafe
|
||||
import kotlinx.io.Source
|
||||
@ -12,25 +11,25 @@ import space.kscience.snark.SnarkReader
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
public object HtmlReader : SnarkReader<HtmlFragment> {
|
||||
public object HtmlReader : SnarkReader<DataFragment> {
|
||||
override val types: Set<String> = setOf("html")
|
||||
|
||||
override fun readFrom(source: String): HtmlFragment = HtmlFragment {
|
||||
override fun readFrom(source: String): DataFragment = DataFragment { _, _ ->
|
||||
div {
|
||||
unsafe { +source }
|
||||
}
|
||||
}
|
||||
|
||||
override fun readFrom(source: Source): HtmlFragment = readFrom(source.readString())
|
||||
override val type: KType = typeOf<HtmlFragment>()
|
||||
override fun readFrom(source: Source): DataFragment = readFrom(source.readString())
|
||||
override val type: KType = typeOf<DataFragment>()
|
||||
}
|
||||
|
||||
public object MarkdownReader : SnarkReader<HtmlFragment> {
|
||||
override val type: KType = typeOf<HtmlFragment>()
|
||||
public object MarkdownReader : SnarkReader<DataFragment> {
|
||||
override val type: KType = typeOf<DataFragment>()
|
||||
|
||||
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 htmlString = HtmlGenerator(source, parsedTree, markdownFlavor).generateHtml()
|
||||
|
||||
@ -44,9 +43,9 @@ public object MarkdownReader : SnarkReader<HtmlFragment> {
|
||||
private val markdownFlavor = CommonMarkFlavourDescriptor()
|
||||
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.plus
|
||||
import space.kscience.dataforge.workspace.FileData
|
||||
import space.kscience.dataforge.workspace.Workspace
|
||||
import space.kscience.snark.html.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.contracts.InvocationKind
|
||||
@ -32,7 +31,6 @@ internal class StaticSiteContext(
|
||||
) : SiteContext {
|
||||
|
||||
|
||||
|
||||
// @OptIn(ExperimentalPathApi::class)
|
||||
// private suspend fun files(item: DataTreeItem<Any>, routeName: Name) {
|
||||
// //try using direct file rendering
|
||||
@ -91,30 +89,23 @@ internal class StaticSiteContext(
|
||||
"${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 =
|
||||
this@StaticSiteContext.resolveRef(this@StaticSiteContext.baseUrl, ref)
|
||||
site.resolveRef(site.baseUrl, ref)
|
||||
|
||||
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) {
|
||||
|
||||
val htmlBuilder = createHTML()
|
||||
|
||||
val modifiedPageMeta = pageMeta.toMutableMeta().apply {
|
||||
"name" put route.toString()
|
||||
}
|
||||
|
||||
htmlBuilder.html {
|
||||
with(htmlPage) {
|
||||
renderPage(StaticPageContext(Laminate(modifiedPageMeta, siteMeta)), data)
|
||||
}
|
||||
}
|
||||
|
||||
val newPath = if (route.isEmpty()) {
|
||||
outputPath.resolve("index.html")
|
||||
} else {
|
||||
@ -122,17 +113,20 @@ internal class StaticSiteContext(
|
||||
}
|
||||
|
||||
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) {
|
||||
val subSiteContext = StaticSiteContext(
|
||||
siteMeta = Laminate(siteMeta, this.siteMeta),
|
||||
baseUrl = if (baseUrl == "") "" else resolveRef(baseUrl, route.toWebPath()),
|
||||
route = Name.EMPTY,
|
||||
outputPath = outputPath.resolve(route.toWebPath())
|
||||
)
|
||||
htmlSite.renderSite(subSiteContext, data)
|
||||
with(htmlSite) {
|
||||
StaticSiteContext(
|
||||
siteMeta = Laminate(siteMeta, this@StaticSiteContext.siteMeta),
|
||||
baseUrl = if (baseUrl == "") "" else resolveRef(baseUrl, route.toWebPath()),
|
||||
route = Name.EMPTY,
|
||||
outputPath = outputPath.resolve(route.toWebPath())
|
||||
).renderSite(data)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,28 +1,31 @@
|
||||
package space.kscience.snark.ktor
|
||||
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.URLBuilder
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.http.fromFileExtension
|
||||
import io.ktor.http.*
|
||||
import io.ktor.http.content.TextContent
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.html.respondHtml
|
||||
import io.ktor.server.http.content.staticFiles
|
||||
import io.ktor.server.plugins.origin
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondBytes
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.css.CssBuilder
|
||||
import kotlinx.html.CommonAttributeGroupFacade
|
||||
import kotlinx.html.head
|
||||
import kotlinx.html.style
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.createRouteFromPath
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.routing
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.ContextAware
|
||||
import space.kscience.dataforge.context.error
|
||||
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.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.cutLast
|
||||
import space.kscience.dataforge.names.endsWith
|
||||
@ -34,11 +37,11 @@ import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
public fun CommonAttributeGroupFacade.css(block: CssBuilder.() -> Unit) {
|
||||
style = CssBuilder().block().toString()
|
||||
}
|
||||
//public fun CommonAttributeGroupFacade.css(block: CssBuilder.() -> Unit) {
|
||||
// style = CssBuilder().block().toString()
|
||||
//}
|
||||
|
||||
public class KtorSiteBuilder(
|
||||
public class KtorSiteContext(
|
||||
override val context: Context,
|
||||
override val siteMeta: Meta,
|
||||
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,
|
||||
override val pageMeta: Meta,
|
||||
) : 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(
|
||||
pageName: Name,
|
||||
relative: Boolean,
|
||||
): 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)) {
|
||||
resolveRef(fullPageName.cutLast().toWebPath())
|
||||
} else {
|
||||
@ -121,27 +125,25 @@ public class KtorSiteBuilder(
|
||||
"name" put route.toString()
|
||||
"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 {
|
||||
head {}
|
||||
with(htmlPage) {
|
||||
renderPage(pageBuilder, data)
|
||||
}
|
||||
}
|
||||
call.respond(TextContent(html, ContentType.Text.Html.withCharset(Charsets.UTF_8), HttpStatusCode.OK))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun site(route: Name, data: DataSet<Any>, siteMeta: Meta, htmlSite: HtmlSite) {
|
||||
val context = KtorSiteBuilder(
|
||||
context,
|
||||
siteMeta = Laminate(siteMeta, this.siteMeta),
|
||||
baseUrl = resolveRef(baseUrl, route.toWebPath()),
|
||||
route = Name.EMPTY,
|
||||
ktorRoute = ktorRoute.createRouteFromPath(route.toWebPath())
|
||||
)
|
||||
|
||||
htmlSite.renderSite(context, data)
|
||||
with(htmlSite) {
|
||||
KtorSiteContext(
|
||||
context,
|
||||
siteMeta = Laminate(siteMeta, this@KtorSiteContext.siteMeta),
|
||||
baseUrl = resolveRef(baseUrl, route.toWebPath()),
|
||||
route = Name.EMPTY,
|
||||
ktorRoute = ktorRoute.createRouteFromPath(route.toWebPath())
|
||||
).renderSite(data)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -151,12 +153,12 @@ private fun Route.site(
|
||||
data: DataTree<*>,
|
||||
baseUrl: String = "",
|
||||
siteMeta: Meta = data.meta,
|
||||
block: KtorSiteBuilder.() -> Unit,
|
||||
block: KtorSiteContext.() -> Unit,
|
||||
) {
|
||||
contract {
|
||||
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(
|
Loading…
Reference in New Issue
Block a user