Skip to content

Commit

Permalink
Serialization work
Browse files Browse the repository at this point in the history
  • Loading branch information
gdude2002 committed Jun 29, 2024
1 parent 21f2d21 commit 3abf50d
Show file tree
Hide file tree
Showing 9 changed files with 371 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package com.kotlindiscord.kord.extensions.annotations

@RequiresOptIn(
level = RequiresOptIn.Level.ERROR,
message = "This API is internal and should not be used. It could be removed or changed without notice."
)
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.TYPEALIAS,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY,
AnnotationTarget.FIELD,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.PROPERTY_SETTER
)
public annotation class InternalAPI
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

package com.kotlindiscord.kord.extensions.utils.collections

import com.kotlindiscord.kord.extensions.utils.collections.serializers.FixedLengthQueueSerializer
import kotlinx.serialization.Serializable
import java.util.*

/**
Expand All @@ -19,6 +21,7 @@ import java.util.*
*
* @param maxSize Maximum number of elements in this queue.
*/
@Serializable(with = FixedLengthQueueSerializer::class)
public class FixedLengthQueue<E : Any?>(public val maxSize: Int) : Queue<E> {
init {
if (maxSize <= 0) {
Expand Down Expand Up @@ -75,7 +78,7 @@ public class FixedLengthQueue<E : Any?>(public val maxSize: Int) : Queue<E> {
data.lastOrNull()

/**
* Returns a list of all elements in this queue, in pushed order.
* Returns a list of all elements in this queue, in pushed order (newest to oldest).
*/
public fun getAll(): List<E> =
data.reversed()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

@file:Suppress("MagicNumber")

package com.kotlindiscord.kord.extensions.utils.collections.serializers

import com.kotlindiscord.kord.extensions.utils.collections.FixedLengthQueue
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encodeToString
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.Json

/**
* Serializer for fixed-length queues.
*
* Serialises to an object containing "maxSize" and "contents" keys.
*/
public class FixedLengthQueueSerializer<T : Any?>(
private val dataSerializer: KSerializer<T>,
) : KSerializer<FixedLengthQueue<T>> {
private val listSerializer = ListSerializer(dataSerializer)

override val descriptor: SerialDescriptor =
buildClassSerialDescriptor("FixedLengthQueue", dataSerializer.descriptor) {
element<Int>("maxSize")
element("contents", listSerializer.descriptor)
}

override fun deserialize(decoder: Decoder): FixedLengthQueue<T> {
var maxSize = 0
var reversedElements: List<T> = emptyList()

decoder.decodeStructure(descriptor) {
while (true) {
when (val index = decodeElementIndex(descriptor)) {
0 -> maxSize = decodeIntElement(descriptor, 0)
1 -> reversedElements = decodeSerializableElement(descriptor, 1, listSerializer)

CompositeDecoder.DECODE_DONE -> break

else -> error("Unexpected descriptor index: $index")
}
}

require(maxSize > 0)
}

val queue = FixedLengthQueue<T>(maxSize)

queue.addAll(reversedElements.reversed())

return queue
}

override fun serialize(encoder: Encoder, value: FixedLengthQueue<T>) {
val allElements = value.getAll()

encoder.encodeStructure(descriptor) {
encodeIntElement(descriptor, 0, value.maxSize)
encodeSerializableElement(descriptor, 1, listSerializer, allElements)
}
}
}

public fun main() {
val queue = FixedLengthQueue<String>(4)

queue.addAll(listOf("1", "2", "3", "4"))

val string = Json.encodeToString(queue)
val output = Json.decodeFromString<FixedLengthQueue<String>>(string)

println("== INPUT ==")
println("Size: ${queue.maxSize}")
println("Elements: ${queue.getAll()}")
println()

println("== SERIALIZED ==")
println(string)
println()

println("== OUTPUT ==")
println("Size: ${output.maxSize}")
println("Elements: ${output.getAll()}")
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@

package dev.kordex.extra.web.values

import dev.kordex.extra.web.values.serializers.TimedContainerSerializer
import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable

@Serializable
@Serializable(with = TimedContainerSerializer::class)
public data class TimedContainer<V : Any?> (
val value: V,
val time: Instant,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

@file:Suppress("MagicNumber")

package dev.kordex.extra.web.values.serializers

import dev.kord.common.entity.optional.Optional
import dev.kordex.extra.web.types.Identifier
import dev.kordex.extra.web.values.types.SimpleValue
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.*

public class SimpleValueSerializer<T : Any>(
private val dataSerializer: KSerializer<T>,
) : KSerializer<SimpleValue<T>> {
private val optionalSerializer = Optional.serializer(dataSerializer.nullable)

override val descriptor: SerialDescriptor =
buildClassSerialDescriptor("SimpleValue", dataSerializer.descriptor) {
element<Identifier>("identifier")
element<Boolean>("writable")
element("value", optionalSerializer.descriptor)
}

override fun deserialize(decoder: Decoder): SimpleValue<T> {
var identifier = Identifier("", "")
var writeable = true
var value: Optional<T?> = Optional.Missing()

decoder.decodeStructure(descriptor) {
while (true) {
when (val index = decodeElementIndex(descriptor)) {
1 -> identifier = decodeSerializableElement(descriptor, index, Identifier.serializer())
2 -> writeable = decodeBooleanElement(descriptor, index)
3 -> value = decodeSerializableElement(descriptor, index, optionalSerializer)

CompositeDecoder.DECODE_DONE -> break

else -> error("Unexpected descriptor index: $index")
}
}
}

val result = SimpleValue(identifier, writeable, dataSerializer)

result.writeOptional(value)

return result
}

override fun serialize(encoder: Encoder, value: SimpleValue<T>) {
encoder.encodeStructure(descriptor) {
encodeSerializableElement(descriptor, 0, Identifier.serializer(), value.identifier)
encodeBooleanElement(descriptor, 1, value.writable)
encodeSerializableElement(descriptor, 2, optionalSerializer, value.optional)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package dev.kordex.extra.web.values.serializers

import dev.kordex.extra.web.values.TimedContainer
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.*

public class TimedContainerSerializer<T : Any?>(
private val dataSerializer: KSerializer<T>,
) : KSerializer<TimedContainer<T>> {
override val descriptor: SerialDescriptor =
buildClassSerialDescriptor("TimedContainer", dataSerializer.descriptor) {
element<Instant>("time")
element("value", dataSerializer.descriptor)
}

override fun deserialize(decoder: Decoder): TimedContainer<T> {
var value: T? = null
var time: Instant = Clock.System.now()

decoder.decodeStructure(descriptor) {
while (true) {
when (val index = decodeElementIndex(descriptor)) {
0 -> time = decodeSerializableElement(descriptor, index, Instant.serializer())
1 -> value = decodeSerializableElement(descriptor, index, dataSerializer)

CompositeDecoder.DECODE_DONE -> break

else -> error("Unexpected descriptor index: $index")
}
}
}

@Suppress("UNCHECKED_CAST")
return TimedContainer(value as T, time)
}

override fun serialize(encoder: Encoder, value: TimedContainer<T>) {
encoder.encodeStructure(descriptor) {
encodeSerializableElement(descriptor, 0, Instant.serializer(), value.time)
encodeSerializableElement(descriptor, 1, dataSerializer, value.value)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

@file:Suppress("MagicNumber")

package dev.kordex.extra.web.values.serializers

import com.kotlindiscord.kord.extensions.utils.collections.FixedLengthQueue
import com.kotlindiscord.kord.extensions.utils.collections.serializers.FixedLengthQueueSerializer
import dev.kordex.extra.web.oldvalues.ValueInterval
import dev.kordex.extra.web.types.Identifier
import dev.kordex.extra.web.values.TimedContainer
import dev.kordex.extra.web.values.types.TrackedValue
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encodeToString
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.Json

public class TrackedValueSerializer<T : Any>(
private val dataSerializer: KSerializer<T>,
) : KSerializer<TrackedValue<T>> {
private val queueSerializer = FixedLengthQueueSerializer(
TimedContainer.serializer(dataSerializer.nullable)
)

override val descriptor: SerialDescriptor =
buildClassSerialDescriptor("TrackedValue", dataSerializer.descriptor) {
element<Identifier>("identifier")
element<Int>("maxValues")
element<ValueInterval>("precision")
element("values", queueSerializer.descriptor)
}

override fun deserialize(decoder: Decoder): TrackedValue<T> {
var identifier = Identifier("", "")
var maxValues = 48
var precision: ValueInterval = ValueInterval.HalfHour
var values: FixedLengthQueue<TimedContainer<T?>> = FixedLengthQueue(1)

decoder.decodeStructure(descriptor) {
while (true) {
when (val index = decodeElementIndex(descriptor)) {
0 -> identifier = decodeSerializableElement(descriptor, index, Identifier.serializer())
1 -> maxValues = decodeIntElement(descriptor, index)
2 -> precision = decodeSerializableElement(descriptor, index, ValueInterval.serializer())
3 -> values = decodeSerializableElement(descriptor, index, queueSerializer)

CompositeDecoder.DECODE_DONE -> break

else -> error("Unexpected descriptor index: $index")
}
}
}

val value = TrackedValue(
identifier,
maxValues,
precision,
dataSerializer
)

values.forEach { value.writeTimed(it) }

return value
}

override fun serialize(encoder: Encoder, value: TrackedValue<T>) {
encoder.encodeStructure(descriptor) {
encodeSerializableElement(descriptor, 0, Identifier.serializer(), value.identifier)
encodeIntElement(descriptor, 1, value.maxValues)
encodeSerializableElement(descriptor, 2, ValueInterval.serializer(), value.precision)
encodeSerializableElement(descriptor, 3, queueSerializer, value.values)
}
}
}

public fun main() {
val value = TrackedValue<String>(
Identifier("a:b"),
48,
ValueInterval.HalfHour,
)

value.write("a")
value.write("b")
value.write("c")
value.write("d")

val string = Json.encodeToString(value)
val output = Json.decodeFromString<TrackedValue<String>>(string)

println("== INPUT ==")
println("Max Values: ${value.maxValues}")
println("Precision: ${value.precision}")
println("Values: ${value.read()}")
println()

println("== SERIALIZED ==")
println(string)
println()

println("== OUTPUT ==")
println("Max Values: ${output.maxValues}")
println("Precision: ${output.precision}")
println("Values: ${output.read()}")
}
Loading

0 comments on commit 3abf50d

Please sign in to comment.