- serialization improvements (the tests work at least)

- fixing some version mis-alignments
This commit is contained in:
Adam 2021-12-01 13:35:32 +01:00
parent babe0161ae
commit 53862ef1b5
19 changed files with 305 additions and 260 deletions

View file

@ -1,61 +1,101 @@
package dev.adamko.factorioevents.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonClassDiscriminator
//@Serializable(with = FactorioEventSerializer::class)
@Serializable
data class FactorioEvent<T : FactorioObjectData>(
data class FactorioEvent<out T : FactorioObjectData>(
/** the initial Factorio event ({defines.events}) trigger */
@SerialName("event_type")
val eventType: String,
/** Schema versioning */
@SerialName("mod_version")
val modVersion: String,
/** game time */
@SerialName("tick")
val tick: UInt,
val data: T,
@SerialName("data")
val data: T
)
@JsonClassDiscriminator("object_name")
sealed interface FactorioObjectData {
val objectName: String
/** The [JsonClassDiscriminator] for [FactorioEvent] */
const val FactorioObjectDataDiscriminatorKey: String = "object_name"
@Serializable
@JsonClassDiscriminator(FactorioObjectDataDiscriminatorKey)
sealed class FactorioObjectData {
@SerialName(FactorioObjectDataDiscriminatorKey)
abstract val objectName: String
}
@Serializable
@SerialName("LuaPlayer")
data class PlayerData(
@SerialName(FactorioObjectDataDiscriminatorKey)
override val objectName: String,
val associatedCharactersUnitNumbers: List<UInt>,
val characterUnitNumber: UInt?,
val name: String,
val position: PositionData,
) : FactorioObjectData
@Serializable(with = ListAsObjectSerializer::class)
@SerialName("associated_characters_unit_numbers")
val associatedCharactersUnitNumbers: List<UInt>,
@SerialName("character_unit_number")
val characterUnitNumber: UInt?,
@SerialName("name")
val name: String,
@SerialName("position")
val position: PositionData,
) : FactorioObjectData()
@Serializable
@SerialName("LuaEntity")
data class EntityData(
@SerialName(FactorioObjectDataDiscriminatorKey)
override val objectName: String,
@SerialName("active")
val active: Boolean,
val health: Float?,
val healthRatio: Float,
@SerialName("health")
val health: Double?,
@SerialName("health_ratio")
val healthRatio: Double,
@SerialName("name")
val name: String,
@SerialName("position")
val position: PositionData,
@SerialName("surface_index")
val surfaceIndex: Int,
@SerialName("type")
val type: String,
@SerialName("unit_number")
val unitNumber: UInt?,
val playerIndex: UInt?,
) : FactorioObjectData
@SerialName("player_index")
val playerIndex: UInt? = null,
) : FactorioObjectData()
@Serializable
@SerialName("LuaSurface")
data class SurfaceData(
@SerialName(FactorioObjectDataDiscriminatorKey)
override val objectName: String,
val daytime: Float,
@SerialName("daytime")
val daytime: Double,
@SerialName("index")
val index: UInt,
@SerialName("name")
val name: String,
) : FactorioObjectData
) : FactorioObjectData()
@Serializable
data class PositionData(
val x: Int,
val y: Int,
)
@SerialName("x")
val x: Double,
@SerialName("y")
val y: Double,
)

View file

@ -0,0 +1,34 @@
package dev.adamko.factorioevents.model
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonTransformingSerializer
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.modules.SerializersModule
/** Factorio outputs lists as Json objects - this serializer converts an object back to a list. */
class ListAsObjectSerializer<T>(dataSerializer: KSerializer<T>) :
JsonTransformingSerializer<List<T>>(ListSerializer(dataSerializer)) {
override fun transformDeserialize(element: JsonElement): JsonElement =
JsonArray(
element.jsonObject.values.toList()
)
override fun transformSerialize(element: JsonElement): JsonElement =
JsonObject(
element.jsonArray.mapIndexed { index, jsonElement -> "$index" to jsonElement }.toMap()
)
}
val jsonMapper = Json {
prettyPrint = true
prettyPrintIndent = " "
serializersModule = SerializersModule { }
}

View file

@ -0,0 +1,139 @@
package dev.adamko.factorioevents.model
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.equality.shouldBeEqualToComparingFields
import kotlinx.serialization.decodeFromString
import org.intellij.lang.annotations.Language
class FactorioEventTest : BehaviorSpec({
Given("json on_player_joined_game") {
@Language("JSON")
val json = """
{
"data": {
"object_name": "LuaPlayer",
"name": "fredthedeadhead",
"character_unit_number": 1,
"associated_characters_unit_numbers": {},
"position": {
"x": -3.5703125,
"y": 29.75
}
},
"event_type": "on_player_joined_game",
"mod_version": "0.0.4",
"tick": 1278458
}
""".trimIndent()
Then("parse") {
val actual: FactorioEvent<FactorioObjectData> = jsonMapper.decodeFromString(json)
val expected = FactorioEvent(
data = PlayerData(
objectName = "LuaPlayer",
name = "fredthedeadhead",
characterUnitNumber = 1u,
associatedCharactersUnitNumbers = emptyList(),
position = PositionData(
x = -3.5703125,
y = 29.75
)
),
eventType = "on_player_joined_game",
modVersion = "0.0.4",
tick = 1278458u
)
actual shouldBeEqualToComparingFields expected
}
}
Given("json surface") {
@Language("JSON")
val json = """
{
"data": {
"object_name": "LuaSurface",
"name": "nauvis",
"index": 1,
"daytime": 0.8390800000511436707029133685864508152008056640625
},
"event_type": "on_tick",
"mod_version": "0.0.4",
"tick": 1278480
}
""".trimIndent()
Then("parse") {
val actual: FactorioEvent<FactorioObjectData> = jsonMapper.decodeFromString(json)
val expected = FactorioEvent(
data = SurfaceData(
objectName = "LuaSurface",
name = "nauvis",
index = 1u,
daytime = 0.8390800000511436707029133685864508152008056640625
),
eventType = "on_tick",
modVersion = "0.0.4",
tick = 1278480u
)
actual shouldBeEqualToComparingFields expected
}
}
Given("json entity") {
@Language("JSON")
val json = """
{
"data": {
"object_name": "LuaEntity",
"name": "character",
"type": "character",
"active": true,
"health": 250,
"health_ratio": 1,
"surface_index": 1,
"unit_number": 1,
"position": {
"x": -0.25,
"y": 30.09375
}
},
"event_type": "on_player_changed_position",
"mod_version": "0.0.6",
"tick": 1384507
}
""".trimIndent()
Then("parse") {
val actual: FactorioEvent<FactorioObjectData> = jsonMapper.decodeFromString(json)
val expected = FactorioEvent(
data = EntityData(
objectName = "LuaEntity",
name = "character",
type = "character",
active = true,
health = 250.0,
healthRatio = 1.0,
surfaceIndex = 1,
unitNumber = 1u,
playerIndex = null,
position = PositionData(
x = -0.25,
y = 30.09375,
)
),
eventType = "on_player_changed_position",
modVersion = "0.0.6",
tick = 1384507u
)
actual shouldBeEqualToComparingFields expected
}
}
})