-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for component data (de)serialization (#231)
- Loading branch information
Showing
43 changed files
with
1,341 additions
and
481 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 11 additions & 1 deletion
12
src/main/kotlin/io/github/freya022/botcommands/api/components/annotations/ComponentData.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,25 @@ | ||
package io.github.freya022.botcommands.api.components.annotations | ||
|
||
import io.github.freya022.botcommands.api.components.builder.IPersistentActionableComponent | ||
import io.github.freya022.botcommands.api.components.serialization.annotations.SerializableComponentData | ||
import io.github.freya022.botcommands.api.parameters.ParameterResolver | ||
import io.github.freya022.botcommands.api.parameters.resolvers.ComponentParameterResolver | ||
|
||
/** | ||
* Sets this parameter as data coming from [IPersistentActionableComponent.bindTo]. | ||
* | ||
* The order and types of the passed data must match with the handler parameters. | ||
* | ||
* ### Requirements | ||
* A compatible [ComponentParameterResolver] must exist for the annotated parameter, | ||
* the default supported types can be seen in [ParameterResolver]. | ||
* | ||
* If your parameter is a serializable object, | ||
* you can instead use [@SerializableComponentData][SerializableComponentData]. | ||
* | ||
* @see JDAButtonListener @JDAButtonListener | ||
* @see JDASelectMenuListener @JDASelectMenuListener | ||
*/ | ||
@Target(AnnotationTarget.VALUE_PARAMETER) | ||
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.ANNOTATION_CLASS) | ||
@Retention(AnnotationRetention.RUNTIME) | ||
annotation class ComponentData |
12 changes: 11 additions & 1 deletion
12
src/main/kotlin/io/github/freya022/botcommands/api/components/annotations/TimeoutData.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,25 @@ | ||
package io.github.freya022.botcommands.api.components.annotations | ||
|
||
import io.github.freya022.botcommands.api.components.builder.IPersistentTimeoutableComponent | ||
import io.github.freya022.botcommands.api.components.serialization.annotations.SerializableTimeoutData | ||
import io.github.freya022.botcommands.api.parameters.ParameterResolver | ||
import io.github.freya022.botcommands.api.parameters.resolvers.TimeoutParameterResolver | ||
|
||
/** | ||
* Sets this parameter as data coming from [IPersistentTimeoutableComponent.timeout]. | ||
* | ||
* The order and types of the passed data must match with the handler parameters. | ||
* | ||
* ### Requirements | ||
* A compatible [TimeoutParameterResolver] must exist for the annotated parameter, | ||
* the default supported types can be seen in [ParameterResolver]. | ||
* | ||
* If your parameter is a serializable object, | ||
* you can instead use [@SerializableTimeoutData][SerializableTimeoutData]. | ||
* | ||
* @see ComponentTimeoutHandler @ComponentTimeoutHandler | ||
* @see GroupTimeoutHandler @GroupTimeoutHandler | ||
*/ | ||
@Target(AnnotationTarget.VALUE_PARAMETER) | ||
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.ANNOTATION_CLASS) | ||
@Retention(AnnotationRetention.RUNTIME) | ||
annotation class TimeoutData |
509 changes: 327 additions & 182 deletions
509
...main/kotlin/io/github/freya022/botcommands/api/components/builder/IActionableComponent.kt
Large diffs are not rendered by default.
Oops, something went wrong.
538 changes: 340 additions & 198 deletions
538
...ain/kotlin/io/github/freya022/botcommands/api/components/builder/ITimeoutableComponent.kt
Large diffs are not rendered by default.
Oops, something went wrong.
60 changes: 60 additions & 0 deletions
60
...github/freya022/botcommands/api/components/serialization/GlobalComponentDataSerializer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package io.github.freya022.botcommands.api.components.serialization | ||
|
||
import io.github.freya022.botcommands.api.components.serialization.annotations.SerializableComponentData | ||
import io.github.freya022.botcommands.api.components.serialization.annotations.SerializableTimeoutData | ||
import io.github.freya022.botcommands.api.core.reflect.ParameterWrapper | ||
import io.github.freya022.botcommands.api.core.service.annotations.InterfacedService | ||
|
||
/** | ||
* Serializes and deserializes data from parameters annotated with [@SerializableComponentData][SerializableComponentData] | ||
* and [@SerializableTimeoutData][SerializableTimeoutData]. | ||
* | ||
* ### Default implementation | ||
* By default, a Jackson-based serializer with the Kotlin module is used. | ||
* | ||
* ### Overriding the default instance | ||
* You can override the default instance by creating a service implementing this interface, | ||
* in which you can use any serialization library you want. | ||
* | ||
* **Tip:** You will generally need to get the type of the to-be-deserialized parameter, | ||
* which you can get from the [ParameterWrapper]. | ||
* | ||
* Here's an example with [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization): | ||
* | ||
* ```kt | ||
* @BService | ||
* class KotlinxComponentDataSerializer : GlobalComponentDataSerializer { | ||
* | ||
* // Default instance, you can customize it later | ||
* private val json = Json | ||
* | ||
* override fun deserialize(parameter: ParameterWrapper, data: SerializedComponentData): Any { | ||
* return json.decodeFromString(serializer(parameter.type), data.asString())!! | ||
* } | ||
* | ||
* override fun serialize(parameter: ParameterWrapper, obj: Any): SerializedComponentData { | ||
* val json = json.encodeToString(serializer(parameter.type), obj) | ||
* return SerializedComponentData.fromString(json) | ||
* } | ||
* } | ||
* ``` | ||
*/ | ||
@InterfacedService(acceptMultiple = false) | ||
interface GlobalComponentDataSerializer { | ||
|
||
/** | ||
* Serializes the given object into a [SerializedComponentData]. | ||
* | ||
* @param parameter The parameter which this value is serialized for | ||
* @param obj The data to be serialized | ||
*/ | ||
fun serialize(parameter: ParameterWrapper, obj: Any): SerializedComponentData | ||
|
||
/** | ||
* Deserializes the [data] into an object compatible with the [parameter]. | ||
* | ||
* @param parameter The parameter which this value is deserialized for | ||
* @param data The data to be deserialized into a compatible object | ||
*/ | ||
fun deserialize(parameter: ParameterWrapper, data: SerializedComponentData): Any | ||
} |
87 changes: 87 additions & 0 deletions
87
...in/io/github/freya022/botcommands/api/components/serialization/SerializedComponentData.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package io.github.freya022.botcommands.api.components.serialization | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import com.fasterxml.jackson.module.kotlin.readValue | ||
import io.github.freya022.botcommands.api.components.serialization.SerializedComponentData.Companion.fromBytes | ||
import io.github.freya022.botcommands.api.components.serialization.SerializedComponentData.Companion.fromString | ||
import io.github.freya022.botcommands.api.core.reflect.KotlinTypeToken | ||
import io.github.freya022.botcommands.api.parameters.resolvers.ComponentParameterResolver | ||
|
||
/** | ||
* Contains the serialized data of a component argument. | ||
* | ||
* @see fromString | ||
* @see fromBytes | ||
* | ||
* @see ComponentParameterResolver.serialize | ||
* @see GlobalComponentDataSerializer.serialize | ||
* @see GlobalComponentDataSerializer.deserialize | ||
*/ | ||
class SerializedComponentData private constructor( | ||
private val bytes: ByteArray, | ||
) { | ||
|
||
/** | ||
* Returns the underlying byte array. | ||
*/ | ||
fun asBytes(): ByteArray = bytes.clone() | ||
|
||
/** | ||
* Decodes the data as a UTF-8 string. | ||
*/ | ||
fun asString() = bytes.decodeToString() | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (javaClass != other?.javaClass) return false | ||
|
||
other as SerializedComponentData | ||
|
||
return bytes.contentEquals(other.bytes) | ||
} | ||
|
||
override fun hashCode(): Int { | ||
return bytes.contentHashCode() | ||
} | ||
|
||
override fun toString(): String { | ||
return "SerializedComponentData(bytes=${bytes.contentToString()})" | ||
} | ||
|
||
companion object { | ||
|
||
/** | ||
* Creates a [SerializedComponentData] from the given bytes. | ||
*/ | ||
@JvmStatic | ||
fun fromBytes(bytes: ByteArray): SerializedComponentData { | ||
return SerializedComponentData(bytes.clone()) | ||
} | ||
|
||
/** | ||
* Creates a [SerializedComponentData] from the given string. | ||
*/ | ||
@JvmStatic | ||
fun fromString(string: String): SerializedComponentData { | ||
return SerializedComponentData(string.encodeToByteArray()) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Serializes the given [value] as a [SerializedComponentData] encoded as a UTF-8 string. | ||
*/ | ||
fun ObjectMapper.writeValueAsComponentData(value: Any): SerializedComponentData = | ||
fromBytes(writeValueAsBytes(value)) | ||
|
||
/** | ||
* Deserializes the given UTF-8 encoded JSON object [data] as a [T] instance. | ||
*/ | ||
inline fun <reified T : Any> ObjectMapper.readValue(data: SerializedComponentData): T = | ||
readValue(data.asBytes()) | ||
|
||
/** | ||
* Deserializes the given UTF-8 encoded JSON object [data] as a [T] instance. | ||
*/ | ||
fun <T : Any> ObjectMapper.readValue(data: SerializedComponentData, typeToken: KotlinTypeToken<T>): T = | ||
readValue(data.asBytes(), constructType(typeToken.javaType)) |
12 changes: 12 additions & 0 deletions
12
...reya022/botcommands/api/components/serialization/annotations/SerializableComponentData.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package io.github.freya022.botcommands.api.components.serialization.annotations | ||
|
||
import io.github.freya022.botcommands.api.components.annotations.ComponentData | ||
import io.github.freya022.botcommands.api.components.serialization.GlobalComponentDataSerializer | ||
|
||
/** | ||
* Same as [@ComponentData][ComponentData], | ||
* but also generates a resolver which (de)serializes the value using the [GlobalComponentDataSerializer]. | ||
*/ | ||
@ComponentData | ||
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.ANNOTATION_CLASS) | ||
annotation class SerializableComponentData |
12 changes: 12 additions & 0 deletions
12
.../freya022/botcommands/api/components/serialization/annotations/SerializableTimeoutData.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package io.github.freya022.botcommands.api.components.serialization.annotations | ||
|
||
import io.github.freya022.botcommands.api.components.annotations.TimeoutData | ||
import io.github.freya022.botcommands.api.components.serialization.GlobalComponentDataSerializer | ||
|
||
/** | ||
* Same as [@TimeoutData][TimeoutData], | ||
* but also generates a resolver which (de)serializes the value using the [GlobalComponentDataSerializer]. | ||
*/ | ||
@TimeoutData | ||
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.ANNOTATION_CLASS) | ||
annotation class SerializableTimeoutData |
13 changes: 13 additions & 0 deletions
13
...22/botcommands/api/components/serialization/exceptions/ComponentSerializationException.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package io.github.freya022.botcommands.api.components.serialization.exceptions | ||
|
||
import io.github.freya022.botcommands.api.parameters.resolvers.ComponentParameterResolver | ||
import io.github.freya022.botcommands.api.parameters.resolvers.TimeoutParameterResolver | ||
|
||
/** | ||
* An exception thrown when [ComponentParameterResolver.serialize] or [TimeoutParameterResolver.serialize] fails. | ||
*/ | ||
class ComponentSerializationException : RuntimeException { | ||
|
||
constructor(message: String) : super(message) | ||
constructor(message: String, cause: Throwable) : super(message, cause) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.