Migration to 1.4
This commit is contained in:
parent
617612bf67
commit
b8d775aa30
@ -1,9 +1,9 @@
|
||||
plugins {
|
||||
val toolsVersion = "0.5.2"
|
||||
id("scientifik.mpp") version toolsVersion apply false
|
||||
id("scientifik.jvm") version toolsVersion apply false
|
||||
id("scientifik.publish") version toolsVersion apply false
|
||||
id("org.jetbrains.dokka") version "0.10.1"
|
||||
val toolsVersion = "0.6.0"
|
||||
id("kscience.mpp") version toolsVersion apply false
|
||||
id("kscience.jvm") version toolsVersion apply false
|
||||
id("kscience.publish") version toolsVersion apply false
|
||||
id("org.jetbrains.dokka") version "1.4.0-rc"
|
||||
id("org.jetbrains.changelog") version "0.4.0"
|
||||
}
|
||||
|
||||
@ -23,6 +23,6 @@ allprojects {
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply(plugin = "scientifik.publish")
|
||||
apply(plugin = "kscience.publish")
|
||||
apply(plugin = "org.jetbrains.dokka")
|
||||
}
|
@ -1,32 +1,29 @@
|
||||
import scientifik.useCoroutines
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("kscience.mpp")
|
||||
}
|
||||
|
||||
description = "Context and provider definitions"
|
||||
|
||||
|
||||
useCoroutines()
|
||||
kscience{
|
||||
useCoroutines()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
api(project(":dataforge-meta"))
|
||||
api("io.github.microutils:kotlin-logging-common:1.7.9")
|
||||
api("io.github.microutils:kotlin-logging:1.9.0")
|
||||
}
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
api(kotlin("reflect"))
|
||||
api("io.github.microutils:kotlin-logging:1.7.9")
|
||||
api("ch.qos.logback:logback-classic:1.2.3")
|
||||
}
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
api("io.github.microutils:kotlin-logging-js:1.7.9")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package hep.dataforge.context
|
||||
import hep.dataforge.meta.DFBuilder
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.names.toName
|
||||
|
||||
/**
|
||||
|
@ -1,29 +1,22 @@
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("kscience.mpp")
|
||||
}
|
||||
|
||||
val coroutinesVersion: String = Scientifik.coroutinesVersion
|
||||
kscience{
|
||||
useCoroutines()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
val commonMain by getting{
|
||||
commonMain{
|
||||
dependencies {
|
||||
api(project(":dataforge-meta"))
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion")
|
||||
}
|
||||
}
|
||||
|
||||
val jvmMain by getting{
|
||||
dependencies {
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
|
||||
jvmMain{
|
||||
dependencies{
|
||||
api(kotlin("reflect"))
|
||||
}
|
||||
}
|
||||
|
||||
val jsMain by getting{
|
||||
dependencies {
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,16 @@
|
||||
import scientifik.DependencySourceSet.TEST
|
||||
import scientifik.useSerialization
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("kscience.mpp")
|
||||
}
|
||||
|
||||
description = "IO module"
|
||||
|
||||
useSerialization(sourceSet = TEST){
|
||||
cbor()
|
||||
kscience {
|
||||
useSerialization(sourceSet = ru.mipt.npm.gradle.DependencySourceSet.TEST) {
|
||||
cbor()
|
||||
}
|
||||
}
|
||||
|
||||
val ioVersion by rootProject.extra("0.2.0-npm-dev-7")
|
||||
val ioVersion by rootProject.extra("0.2.0-npm-dev-10")
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@ -1,13 +1,14 @@
|
||||
import scientifik.useSerialization
|
||||
|
||||
plugins {
|
||||
id("scientifik.jvm")
|
||||
id("kscience.jvm")
|
||||
}
|
||||
|
||||
description = "YAML meta IO"
|
||||
|
||||
useSerialization{
|
||||
yaml()
|
||||
kscience {
|
||||
useSerialization {
|
||||
yaml()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -9,7 +9,6 @@ import hep.dataforge.meta.Meta
|
||||
import kotlinx.io.*
|
||||
import kotlinx.io.text.readUtf8Line
|
||||
import kotlinx.io.text.writeUtf8String
|
||||
import kotlinx.serialization.toUtf8Bytes
|
||||
|
||||
@DFExperimental
|
||||
class FrontMatterEnvelopeFormat(
|
||||
@ -22,7 +21,7 @@ class FrontMatterEnvelopeFormat(
|
||||
var offset = 0u
|
||||
do {
|
||||
line = readUtf8Line() //?: error("Input does not contain front matter separator")
|
||||
offset += line.toUtf8Bytes().size.toUInt()
|
||||
offset += line.toByteArray().size.toUInt()
|
||||
} while (!line.startsWith(SEPARATOR))
|
||||
|
||||
val readMetaFormat =
|
||||
@ -34,7 +33,7 @@ class FrontMatterEnvelopeFormat(
|
||||
do {
|
||||
line = readUtf8Line()
|
||||
writeUtf8String(line + "\r\n")
|
||||
offset += line.toUtf8Bytes().size.toUInt()
|
||||
offset += line.toByteArray().size.toUInt()
|
||||
} while (!line.startsWith(SEPARATOR))
|
||||
}.read {
|
||||
readMetaFormat.run {
|
||||
|
@ -28,7 +28,7 @@ interface Envelope {
|
||||
/**
|
||||
* Build a static envelope using provided builder
|
||||
*/
|
||||
inline operator fun invoke(block: EnvelopeBuilder.() -> Unit) = EnvelopeBuilder().apply(block).build()
|
||||
inline operator fun invoke(block: EnvelopeBuilder.() -> Unit) = EnvelopeBuilder().apply(block).seal()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,42 +3,44 @@ package hep.dataforge.io
|
||||
import hep.dataforge.meta.*
|
||||
import kotlinx.io.*
|
||||
|
||||
class EnvelopeBuilder {
|
||||
public class EnvelopeBuilder : Envelope {
|
||||
private val metaBuilder = MetaBuilder()
|
||||
var data: Binary? = null
|
||||
|
||||
fun meta(block: MetaBuilder.() -> Unit) {
|
||||
override var data: Binary? = null
|
||||
override var meta: Meta
|
||||
get() = metaBuilder
|
||||
set(value) {
|
||||
metaBuilder.update(value)
|
||||
}
|
||||
|
||||
public fun meta(block: MetaBuilder.() -> Unit) {
|
||||
metaBuilder.apply(block)
|
||||
}
|
||||
|
||||
fun meta(meta: Meta) {
|
||||
metaBuilder.update(meta)
|
||||
}
|
||||
|
||||
/**
|
||||
* The general purpose of the envelope
|
||||
*/
|
||||
var type by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY)
|
||||
var dataType by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY)
|
||||
public var type: String? by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY)
|
||||
public var dataType: String? by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY)
|
||||
|
||||
/**
|
||||
* Data unique identifier to bypass identity checks
|
||||
*/
|
||||
var dataID by metaBuilder.string(key = Envelope.ENVELOPE_DATA_ID_KEY)
|
||||
var description by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY)
|
||||
var name by metaBuilder.string(key = Envelope.ENVELOPE_NAME_KEY)
|
||||
public var dataID: String? by metaBuilder.string(key = Envelope.ENVELOPE_DATA_ID_KEY)
|
||||
public var description: String? by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY)
|
||||
public var name: String? by metaBuilder.string(key = Envelope.ENVELOPE_NAME_KEY)
|
||||
|
||||
/**
|
||||
* Construct a data binary from given builder
|
||||
*/
|
||||
@OptIn(ExperimentalIoApi::class)
|
||||
fun data(block: Output.() -> Unit) {
|
||||
public fun data(block: Output.() -> Unit) {
|
||||
val arrayBuilder = ByteArrayOutput()
|
||||
arrayBuilder.block()
|
||||
data = arrayBuilder.toByteArray().asBinary()
|
||||
}
|
||||
|
||||
fun build() = SimpleEnvelope(metaBuilder.seal(), data)
|
||||
public fun seal(): Envelope = SimpleEnvelope(metaBuilder.seal(), data)
|
||||
|
||||
}
|
||||
|
||||
|
@ -14,25 +14,24 @@ import kotlinx.io.Input
|
||||
import kotlinx.io.Output
|
||||
import kotlinx.io.readByteArray
|
||||
import kotlinx.io.text.writeUtf8String
|
||||
import kotlinx.serialization.UnstableDefault
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObjectSerializer
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
|
||||
@OptIn(UnstableDefault::class)
|
||||
class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat {
|
||||
|
||||
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
|
||||
val jsonObject = meta.toJson(descriptor)
|
||||
writeUtf8String(json.stringify(JsonObjectSerializer, jsonObject))
|
||||
writeUtf8String(json.encodeToString(JsonObject.serializer(), jsonObject))
|
||||
}
|
||||
|
||||
override fun toMeta(): Meta = Meta{
|
||||
override fun toMeta(): Meta = Meta {
|
||||
NAME_KEY put name.toString()
|
||||
}
|
||||
|
||||
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta {
|
||||
val str = readByteArray().decodeToString()
|
||||
val jsonElement = json.parseJson(str)
|
||||
val jsonElement = json.parseToJsonElement(str)
|
||||
val item = jsonElement.toMetaItem(descriptor)
|
||||
return item.node ?: Meta.EMPTY
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ fun Output.writeRawString(str: String) {
|
||||
|
||||
fun Input.readRawString(size: Int): String {
|
||||
val array = CharArray(size) { readByte().toChar() }
|
||||
return String(array)
|
||||
return array.concatToString()
|
||||
}
|
||||
|
||||
inline fun buildByteArray(expectedSize: Int = 16, block: Output.() -> Unit): ByteArray =
|
||||
|
@ -3,9 +3,7 @@ package hep.dataforge.io
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
|
||||
import kotlinx.io.asBinary
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.json
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ -57,21 +55,21 @@ class MetaFormatTest {
|
||||
|
||||
@Test
|
||||
fun testJsonToMeta() {
|
||||
val json = jsonArray {
|
||||
val json = buildJsonArray {
|
||||
//top level array
|
||||
+jsonArray {
|
||||
+JsonPrimitive(88)
|
||||
+json {
|
||||
"c" to "aasdad"
|
||||
"d" to true
|
||||
}
|
||||
}
|
||||
+"value"
|
||||
+jsonArray {
|
||||
+JsonPrimitive(1.0)
|
||||
+JsonPrimitive(2.0)
|
||||
+JsonPrimitive(3.0)
|
||||
}
|
||||
add(buildJsonArray {
|
||||
add(JsonPrimitive(88))
|
||||
add(buildJsonObject {
|
||||
put("c", "aasdad")
|
||||
put("d", true)
|
||||
})
|
||||
})
|
||||
add("value")
|
||||
add(buildJsonArray {
|
||||
add(JsonPrimitive(1.0))
|
||||
add(JsonPrimitive(2.0))
|
||||
add(JsonPrimitive(3.0))
|
||||
})
|
||||
}
|
||||
val meta = json.toMetaItem().node!!
|
||||
|
||||
|
@ -19,24 +19,24 @@ class MetaSerializerTest {
|
||||
|
||||
@Test
|
||||
fun testMetaSerialization() {
|
||||
val string = JSON_PRETTY.stringify(MetaSerializer, meta)
|
||||
val restored = JSON_PLAIN.parse(MetaSerializer, string)
|
||||
val string = JSON_PRETTY.encodeToString(MetaSerializer, meta)
|
||||
val restored = JSON_PLAIN.decodeFromString(MetaSerializer, string)
|
||||
assertEquals(meta, restored)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCborSerialization() {
|
||||
val bytes = Cbor.dump(MetaSerializer, meta)
|
||||
val bytes = Cbor.encodeToByteArray(MetaSerializer, meta)
|
||||
println(bytes.contentToString())
|
||||
val restored = Cbor.load(MetaSerializer, bytes)
|
||||
val restored = Cbor.decodeFromByteArray(MetaSerializer, bytes)
|
||||
assertEquals(meta, restored)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNameSerialization() {
|
||||
val name = "a.b.c".toName()
|
||||
val string = JSON_PRETTY.stringify(Name.serializer(), name)
|
||||
val restored = JSON_PLAIN.parse(Name.serializer(), string)
|
||||
val string = JSON_PRETTY.encodeToString(Name.serializer(), name)
|
||||
val restored = JSON_PLAIN.decodeFromString(Name.serializer(), string)
|
||||
assertEquals(name, restored)
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,11 @@ import java.util.concurrent.Executors
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
@ExperimentalTime
|
||||
class EnvelopeClient(
|
||||
@Deprecated("To be replaced by flow-based client")
|
||||
public class EnvelopeClient(
|
||||
override val context: Context,
|
||||
val host: String,
|
||||
val port: Int,
|
||||
public val host: String,
|
||||
public val port: Int,
|
||||
formatFactory: EnvelopeFormatFactory = TaggedEnvelopeFormat,
|
||||
formatMeta: Meta = Meta.EMPTY
|
||||
) : Responder, ContextAware {
|
||||
@ -34,7 +35,7 @@ class EnvelopeClient(
|
||||
// }
|
||||
// }
|
||||
|
||||
suspend fun close() {
|
||||
public suspend fun close() {
|
||||
try {
|
||||
respond(
|
||||
Envelope {
|
||||
@ -47,6 +48,7 @@ class EnvelopeClient(
|
||||
}
|
||||
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
override suspend fun respond(request: Envelope): Envelope = withContext(dispatcher) {
|
||||
//val address = InetSocketAddress(host,port)
|
||||
val socket = Socket(host, port)
|
||||
|
@ -12,7 +12,8 @@ import java.net.ServerSocket
|
||||
import java.net.Socket
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class EnvelopeServer(
|
||||
@Deprecated("To be replaced by flow-based server")
|
||||
public class EnvelopeServer(
|
||||
override val context: Context,
|
||||
val port: Int,
|
||||
val responder: Responder,
|
||||
@ -25,7 +26,7 @@ class EnvelopeServer(
|
||||
|
||||
private val format = formatFactory(formatMeta, context = context)
|
||||
|
||||
fun start() {
|
||||
public fun start() {
|
||||
if (job == null) {
|
||||
logger.info { "Starting envelope server on port $port" }
|
||||
job = scope.launch(Dispatchers.IO) {
|
||||
@ -40,7 +41,7 @@ class EnvelopeServer(
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
public fun stop() {
|
||||
logger.info { "Stopping envelope server on port $port" }
|
||||
job?.cancel()
|
||||
job = null
|
||||
@ -97,7 +98,7 @@ class EnvelopeServer(
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SHUTDOWN_ENVELOPE_TYPE = "@shutdown"
|
||||
public companion object {
|
||||
public const val SHUTDOWN_ENVELOPE_TYPE = "@shutdown"
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import scientifik.useSerialization
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("kscience.mpp")
|
||||
}
|
||||
|
||||
useSerialization()
|
||||
kscience {
|
||||
useSerialization()
|
||||
}
|
||||
|
||||
description = "Meta definition and basic operations on meta"
|
@ -4,16 +4,26 @@ import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.plus
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.mapValues
|
||||
import kotlin.collections.removeAll
|
||||
import kotlin.collections.set
|
||||
|
||||
//TODO add validator to configuration
|
||||
|
||||
data class MetaListener(
|
||||
public data class MetaListener(
|
||||
val owner: Any? = null,
|
||||
val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit
|
||||
)
|
||||
|
||||
interface ObservableMeta : Meta {
|
||||
public interface ObservableMeta : Meta {
|
||||
fun onChange(owner: Any?, action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit)
|
||||
fun removeListener(owner: Any?)
|
||||
}
|
||||
|
@ -61,9 +61,9 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
|
||||
elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor, null)
|
||||
}
|
||||
else -> {
|
||||
val array = jsonArray {
|
||||
val array = buildJsonArray {
|
||||
items.forEach { (index, item) ->
|
||||
+item.toJsonElement(itemDescriptor, index)
|
||||
add(item.toJsonElement(itemDescriptor, index))
|
||||
}
|
||||
}
|
||||
elementMap[jsonKey] = array
|
||||
@ -89,16 +89,11 @@ fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): JsonMeta = JsonMeta(t
|
||||
fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
|
||||
return when (this) {
|
||||
JsonNull -> Null
|
||||
is JsonLiteral -> {
|
||||
when (body) {
|
||||
true -> True
|
||||
false -> False
|
||||
is Number -> NumberValue(body as Number)
|
||||
else -> if (isString) {
|
||||
StringValue(content)
|
||||
} else {
|
||||
content.parseValue()
|
||||
}
|
||||
else -> {
|
||||
if (isString) {
|
||||
StringValue(content)
|
||||
} else {
|
||||
content.parseValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,7 +121,7 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
|
||||
MetaItem.ValueItem(value)
|
||||
} else {
|
||||
//We can't return multiple items therefore we create top level node
|
||||
json { JSON_ARRAY_KEY to this@toMetaItem }.toMetaItem(descriptor)
|
||||
buildJsonObject { put(JSON_ARRAY_KEY, this@toMetaItem) }.toMetaItem(descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,7 +160,7 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
|
||||
} else value.forEachIndexed { index, jsonElement ->
|
||||
val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
|
||||
val indexValue: String = (jsonElement as? JsonObject)
|
||||
?.get(indexKey)?.contentOrNull
|
||||
?.get(indexKey)?.jsonPrimitive?.contentOrNull
|
||||
?: index.toString() //In case index is non-string, the backward transformation will be broken.
|
||||
|
||||
val token = key.withIndex(indexValue)
|
||||
|
@ -5,7 +5,12 @@ import hep.dataforge.meta.MetaItem.NodeItem
|
||||
import hep.dataforge.meta.MetaItem.ValueItem
|
||||
import hep.dataforge.names.*
|
||||
import hep.dataforge.values.*
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
|
||||
/**
|
||||
@ -218,7 +223,7 @@ abstract class MetaBase : Meta {
|
||||
|
||||
override fun hashCode(): Int = items.hashCode()
|
||||
|
||||
override fun toString(): String = JSON_PRETTY.stringify(MetaSerializer, this)
|
||||
override fun toString(): String = JSON_PRETTY.encodeToString(MetaSerializer, this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,11 +1,15 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.NameToken
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.builtins.MapSerializer
|
||||
import kotlinx.serialization.json.JsonInput
|
||||
import kotlinx.serialization.json.JsonObjectSerializer
|
||||
import kotlinx.serialization.json.JsonOutput
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.json.JsonDecoder
|
||||
import kotlinx.serialization.json.JsonEncoder
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
|
||||
/**
|
||||
@ -21,8 +25,8 @@ object MetaSerializer : KSerializer<Meta> {
|
||||
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): Meta {
|
||||
return if (decoder is JsonInput) {
|
||||
JsonObjectSerializer.deserialize(decoder).toMeta()
|
||||
return if (decoder is JsonDecoder) {
|
||||
JsonObject.serializer().deserialize(decoder).toMeta()
|
||||
} else {
|
||||
object : MetaBase() {
|
||||
override val items: Map<NameToken, MetaItem<*>> = mapSerializer.deserialize(decoder)
|
||||
@ -31,8 +35,8 @@ object MetaSerializer : KSerializer<Meta> {
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Meta) {
|
||||
if (encoder is JsonOutput) {
|
||||
JsonObjectSerializer.serialize(encoder, value.toJson())
|
||||
if (encoder is JsonEncoder) {
|
||||
JsonObject.serializer().serialize(encoder, value.toJson())
|
||||
} else {
|
||||
mapSerializer.serialize(encoder, value.items)
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ fun <M : MutableMeta<M>> M.append(name: Name, value: Any?) {
|
||||
if (newIndex != null) {
|
||||
set(name, value)
|
||||
} else {
|
||||
val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1
|
||||
val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.maxOrNull() ?: -1) + 1
|
||||
set(name.withIndex(index.toString()), value)
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +1,50 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.InternalSerializationApi
|
||||
import kotlinx.serialization.builtins.DoubleArraySerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.CompositeDecoder
|
||||
import kotlinx.serialization.encoding.CompositeEncoder
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
|
||||
fun SerialDescriptorBuilder.boolean(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
fun ClassSerialDescriptorBuilder.boolean(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
element(name, Boolean.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
|
||||
|
||||
fun SerialDescriptorBuilder.string(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
fun ClassSerialDescriptorBuilder.string(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
element(name, String.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
|
||||
|
||||
fun SerialDescriptorBuilder.int(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
fun ClassSerialDescriptorBuilder.int(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
element(name, Int.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
|
||||
|
||||
fun SerialDescriptorBuilder.double(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
fun ClassSerialDescriptorBuilder.double(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
element(name, Double.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
|
||||
|
||||
fun SerialDescriptorBuilder.float(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
fun ClassSerialDescriptorBuilder.float(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
element(name, Float.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
|
||||
|
||||
fun SerialDescriptorBuilder.long(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
fun ClassSerialDescriptorBuilder.long(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
element(name, Long.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
|
||||
|
||||
fun SerialDescriptorBuilder.doubleArray(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
|
||||
fun ClassSerialDescriptorBuilder.doubleArray(
|
||||
name: String,
|
||||
isOptional: Boolean = false,
|
||||
vararg annotations: Annotation,
|
||||
) =
|
||||
element(name, DoubleArraySerializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
|
||||
|
||||
@OptIn(InternalSerializationApi::class)
|
||||
inline fun <reified E : Enum<E>> SerialDescriptorBuilder.enum(
|
||||
inline fun <reified E : Enum<E>> ClassSerialDescriptorBuilder.enum(
|
||||
name: String,
|
||||
isOptional: Boolean = false,
|
||||
vararg annotations: Annotation
|
||||
vararg annotations: Annotation,
|
||||
) {
|
||||
val enumDescriptor = SerialDescriptor(serialName, UnionKind.ENUM_KIND) {
|
||||
val enumDescriptor = buildSerialDescriptor(serialName, SerialKind.ENUM) {
|
||||
enumValues<E>().forEach {
|
||||
val fqn = "$serialName.${it.name}"
|
||||
val enumMemberDescriptor = SerialDescriptor(fqn, StructureKind.OBJECT)
|
||||
val enumMemberDescriptor = buildSerialDescriptor(fqn, StructureKind.OBJECT)
|
||||
element(it.name, enumMemberDescriptor)
|
||||
}
|
||||
}
|
||||
@ -46,10 +54,9 @@ inline fun <reified E : Enum<E>> SerialDescriptorBuilder.enum(
|
||||
@DFExperimental
|
||||
inline fun <R> Decoder.decodeStructure(
|
||||
desc: SerialDescriptor,
|
||||
vararg typeParams: KSerializer<*> = emptyArray(),
|
||||
crossinline block: CompositeDecoder.() -> R
|
||||
crossinline block: CompositeDecoder.() -> R,
|
||||
): R {
|
||||
val decoder = beginStructure(desc, *typeParams)
|
||||
val decoder = beginStructure(desc)
|
||||
val res = decoder.block()
|
||||
decoder.endStructure(desc)
|
||||
return res
|
||||
@ -58,15 +65,12 @@ inline fun <R> Decoder.decodeStructure(
|
||||
@DFExperimental
|
||||
inline fun Encoder.encodeStructure(
|
||||
desc: SerialDescriptor,
|
||||
vararg typeParams: KSerializer<*> = emptyArray(),
|
||||
block: CompositeEncoder.() -> Unit
|
||||
block: CompositeEncoder.() -> Unit,
|
||||
) {
|
||||
val encoder = beginStructure(desc, *typeParams)
|
||||
val encoder = beginStructure(desc)
|
||||
encoder.block()
|
||||
encoder.endStructure(desc)
|
||||
}
|
||||
|
||||
@OptIn(UnstableDefault::class)
|
||||
val JSON_PRETTY = Json(JsonConfiguration(prettyPrint = true, useArrayPolymorphism = true))
|
||||
@OptIn(UnstableDefault::class)
|
||||
val JSON_PLAIN = Json(JsonConfiguration(prettyPrint = true, useArrayPolymorphism = true))
|
||||
val JSON_PRETTY = Json { prettyPrint = true; useArrayPolymorphism = true }
|
||||
val JSON_PLAIN = Json { prettyPrint = false; useArrayPolymorphism = true }
|
@ -1,6 +1,13 @@
|
||||
package hep.dataforge.names
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
|
||||
/**
|
||||
@ -60,7 +67,7 @@ class Name(val tokens: List<NameToken>) {
|
||||
val EMPTY = Name(emptyList())
|
||||
|
||||
override val descriptor: SerialDescriptor =
|
||||
PrimitiveDescriptor("hep.dataforge.names.Name", PrimitiveKind.STRING)
|
||||
PrimitiveSerialDescriptor("hep.dataforge.names.Name", PrimitiveKind.STRING)
|
||||
|
||||
override fun deserialize(decoder: Decoder): Name {
|
||||
return decoder.decodeString().toName()
|
||||
@ -101,7 +108,7 @@ data class NameToken(val body: String, val index: String? = null) {
|
||||
@Serializer(NameToken::class)
|
||||
companion object : KSerializer<NameToken> {
|
||||
override val descriptor: SerialDescriptor =
|
||||
PrimitiveDescriptor("hep.dataforge.names.NameToken", PrimitiveKind.STRING)
|
||||
PrimitiveSerialDescriptor("hep.dataforge.names.NameToken", PrimitiveKind.STRING)
|
||||
|
||||
override fun deserialize(decoder: Decoder): NameToken {
|
||||
return decoder.decodeString().toName().first()!!
|
||||
|
@ -3,22 +3,27 @@ package hep.dataforge.values
|
||||
import hep.dataforge.meta.boolean
|
||||
import hep.dataforge.meta.enum
|
||||
import hep.dataforge.meta.string
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.list
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@Serializer(Value::class)
|
||||
object ValueSerializer : KSerializer<Value> {
|
||||
private val listSerializer by lazy { ValueSerializer.list }
|
||||
private val listSerializer by lazy { ListSerializer(ValueSerializer) }
|
||||
|
||||
override val descriptor: SerialDescriptor =
|
||||
SerialDescriptor("hep.dataforge.values.Value") {
|
||||
buildClassSerialDescriptor("hep.dataforge.values.Value") {
|
||||
boolean("isList")
|
||||
enum<ValueType>("valueType")
|
||||
string("value")
|
||||
}
|
||||
|
||||
private fun Decoder.decodeValue(): Value {
|
||||
return when (decode(ValueType.serializer())) {
|
||||
return when (decodeSerializableValue(ValueType.serializer())) {
|
||||
ValueType.NULL -> Null
|
||||
ValueType.NUMBER -> decodeDouble().asValue() //TODO differentiate?
|
||||
ValueType.BOOLEAN -> decodeBoolean().asValue()
|
||||
@ -37,7 +42,7 @@ object ValueSerializer : KSerializer<Value> {
|
||||
}
|
||||
|
||||
private fun Encoder.encodeValue(value: Value) {
|
||||
encode(ValueType.serializer(), value.type)
|
||||
encodeSerializableValue(ValueType.serializer(), value.type)
|
||||
when (value.type) {
|
||||
ValueType.NULL -> {
|
||||
// do nothing
|
||||
|
@ -1,35 +1,33 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.json
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class JsonMetaTest {
|
||||
val json = json {
|
||||
"firstValue" to "a"
|
||||
"secondValue" to "b"
|
||||
"array" to jsonArray {
|
||||
+"1"
|
||||
+"2"
|
||||
+"3"
|
||||
}
|
||||
"nodeArray" to jsonArray {
|
||||
+json {
|
||||
"index" to "1"
|
||||
"value" to 2
|
||||
}
|
||||
+json {
|
||||
"index" to "2"
|
||||
"value" to 3
|
||||
}
|
||||
+json {
|
||||
"index" to "3"
|
||||
"value" to 4
|
||||
}
|
||||
}
|
||||
val json = buildJsonObject {
|
||||
put("firstValue", "a")
|
||||
put("secondValue", "b")
|
||||
put("array", buildJsonArray {
|
||||
add("1")
|
||||
add("2")
|
||||
add("3")
|
||||
})
|
||||
put("nodeArray", buildJsonArray {
|
||||
add(buildJsonObject {
|
||||
put("index", "1")
|
||||
put("value", 2)
|
||||
})
|
||||
add(buildJsonObject {
|
||||
put("index", "2")
|
||||
put("value", 3)
|
||||
})
|
||||
add(buildJsonObject {
|
||||
put("index", "3")
|
||||
put("value", 4)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
val descriptor = NodeDescriptor{
|
||||
@ -45,7 +43,8 @@ class JsonMetaTest {
|
||||
//println(meta)
|
||||
val reconstructed = meta.toJson(descriptor)
|
||||
println(reconstructed)
|
||||
assertEquals(2, reconstructed["nodeArray"]?.jsonArray?.get(1)?.jsonObject?.get("index")?.int)
|
||||
assertEquals(2,
|
||||
reconstructed["nodeArray"]?.jsonArray?.get(1)?.jsonObject?.get("index")?.jsonPrimitive?.int)
|
||||
assertEquals(json,reconstructed)
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ fun Meta.toDynamic(): dynamic {
|
||||
return res
|
||||
}
|
||||
|
||||
class DynamicMeta(val obj: dynamic) : MetaBase() {
|
||||
public class DynamicMeta(public val obj: dynamic) : MetaBase() {
|
||||
private fun keys() = js("Object.keys(this.obj)") as Array<String>
|
||||
|
||||
private fun isArray(@Suppress("UNUSED_PARAMETER") obj: dynamic): Boolean =
|
||||
@ -47,7 +47,7 @@ class DynamicMeta(val obj: dynamic) : MetaBase() {
|
||||
private fun isPrimitive(obj: dynamic): Boolean =
|
||||
(jsTypeOf(obj) != "object")
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Suppress("UNCHECKED_CAST", "USELESS_CAST")
|
||||
private fun asItem(obj: dynamic): MetaItem<DynamicMeta>? {
|
||||
return when {
|
||||
obj == null -> MetaItem.ValueItem(Null)
|
||||
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("kscience.mpp")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
@ -1,25 +1,15 @@
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("kscience.mpp")
|
||||
}
|
||||
|
||||
val htmlVersion by rootProject.extra("0.6.12")
|
||||
val htmlVersion by rootProject.extra("0.7.2")
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
api(project(":dataforge-output"))
|
||||
api("org.jetbrains.kotlinx:kotlinx-html-common:$htmlVersion")
|
||||
}
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
api("org.jetbrains.kotlinx:kotlinx-html-js:$htmlVersion")
|
||||
}
|
||||
}
|
||||
val jvmMain by getting{
|
||||
dependencies {
|
||||
api("org.jetbrains.kotlinx:kotlinx-html-jvm:$htmlVersion")
|
||||
api("org.jetbrains.kotlinx:kotlinx-html:$htmlVersion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("kscience.mpp")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
@ -13,7 +12,7 @@ kotlin {
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("scripting-jvm-host-embeddable"))
|
||||
implementation(kotlin("scripting-jvm-host"))
|
||||
implementation(kotlin("scripting-jvm"))
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("kscience.mpp")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
@ -1,10 +1,8 @@
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("kscience.mpp")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
js()
|
||||
sourceSets {
|
||||
val commonMain by getting{
|
||||
dependencies {
|
||||
|
@ -1,31 +0,0 @@
|
||||
apply plugin: "com.jfrog.artifactory"
|
||||
|
||||
artifactory {
|
||||
def artifactory_user = project.hasProperty('artifactoryUser') ? project.property('artifactoryUser') : ""
|
||||
def artifactory_password = project.hasProperty('artifactoryPassword') ? project.property('artifactoryPassword') : ""
|
||||
def artifactory_contextUrl = 'http://npm.mipt.ru:8081/artifactory'
|
||||
|
||||
contextUrl = artifactory_contextUrl //The base Artifactory URL if not overridden by the publisher/resolver
|
||||
publish {
|
||||
repository {
|
||||
repoKey = 'gradle-dev-local'
|
||||
username = artifactory_user
|
||||
password = artifactory_password
|
||||
}
|
||||
|
||||
defaults {
|
||||
publications('jvm', 'js', 'kotlinMultiplatform', 'metadata')
|
||||
publishBuildInfo = false
|
||||
publishArtifacts = true
|
||||
publishPom = true
|
||||
publishIvy = false
|
||||
}
|
||||
}
|
||||
resolve {
|
||||
repository {
|
||||
repoKey = 'gradle-dev'
|
||||
username = artifactory_user
|
||||
password = artifactory_password
|
||||
}
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
apply plugin: 'com.jfrog.bintray'
|
||||
|
||||
def vcs = "https://github.com/mipt-npm/kmath"
|
||||
|
||||
def pomConfig = {
|
||||
licenses {
|
||||
license {
|
||||
name "The Apache Software License, Version 2.0"
|
||||
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
distribution "repo"
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id "MIPT-NPM"
|
||||
name "MIPT nuclear physics methods laboratory"
|
||||
organization "MIPT"
|
||||
organizationUrl "http://npm.mipt.ru"
|
||||
}
|
||||
}
|
||||
scm {
|
||||
url vcs
|
||||
}
|
||||
}
|
||||
|
||||
project.ext.configureMavenCentralMetadata = { pom ->
|
||||
def root = asNode()
|
||||
root.appendNode('name', project.name)
|
||||
root.appendNode('description', project.description)
|
||||
root.appendNode('url', vcs)
|
||||
root.children().last() + pomConfig
|
||||
}
|
||||
|
||||
project.ext.configurePom = pomConfig
|
||||
|
||||
|
||||
// Configure publishing
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
url = "https://bintray.com/mipt-npm/scientifik"
|
||||
}
|
||||
}
|
||||
|
||||
// Process each publication we have in this project
|
||||
publications.all { publication ->
|
||||
// apply changes to pom.xml files, see pom.gradle
|
||||
pom.withXml(configureMavenCentralMetadata)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bintray {
|
||||
user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER')
|
||||
key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY')
|
||||
publish = true
|
||||
override = true // for multi-platform Kotlin/Native publishing
|
||||
|
||||
pkg {
|
||||
userOrg = "mipt-npm"
|
||||
repo = "scientifik"
|
||||
name = "scientifik.kmath"
|
||||
issueTrackerUrl = "https://github.com/mipt-npm/kmath/issues"
|
||||
licenses = ['Apache-2.0']
|
||||
vcsUrl = vcs
|
||||
version {
|
||||
name = project.version
|
||||
vcsTag = project.version
|
||||
released = new Date()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bintrayUpload.dependsOn publishToMavenLocal
|
||||
|
||||
// This is for easier debugging of bintray uploading problems
|
||||
bintrayUpload.doFirst {
|
||||
publications = project.publishing.publications.findAll {
|
||||
!it.name.contains('-test') && it.name != 'kotlinMultiplatform'
|
||||
}.collect {
|
||||
println("Uploading artifact '$it.groupId:$it.artifactId:$it.version' from publication '$it.name'")
|
||||
it.name//https://github.com/bintray/gradle-bintray-plugin/issues/256
|
||||
}
|
||||
}
|
@ -6,21 +6,21 @@ pluginManagement {
|
||||
maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
maven("https://dl.bintray.com/kotlin/kotlinx")
|
||||
maven("https://dl.bintray.com/mipt-npm/scientifik")
|
||||
maven("https://dl.bintray.com/mipt-npm/kscience")
|
||||
maven("https://dl.bintray.com/mipt-npm/dev")
|
||||
}
|
||||
|
||||
val toolsVersion = "0.6.0"
|
||||
|
||||
resolutionStrategy {
|
||||
eachPlugin {
|
||||
when (requested.id.id) {
|
||||
"kotlinx-atomicfu" -> useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${requested.version}")
|
||||
"scientifik.mpp", "scientifik.jvm", "scientifik.publish" -> useModule("scientifik:gradle-tools:${requested.version}")
|
||||
"kscience.mpp", "kscience.jvm", "kscience.js", "kscience.publish" -> useModule("ru.mipt.npm:gradle-tools:${toolsVersion}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enableFeaturePreview("GRADLE_METADATA")
|
||||
|
||||
//rootProject.name = "dataforge-core"
|
||||
include(
|
||||
":dataforge-meta",
|
||||
":dataforge-io",
|
||||
@ -32,6 +32,4 @@ include(
|
||||
":dataforge-tables",
|
||||
":dataforge-workspace",
|
||||
":dataforge-scripting"
|
||||
)
|
||||
|
||||
//includeBuild("../kotlinx-io")
|
||||
)
|
Loading…
Reference in New Issue
Block a user