From 714cda2575af3675c374fb3936eb7cefac4a7f59 Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Sun, 3 Apr 2022 01:46:58 +0200 Subject: [PATCH 01/14] Duration serializers --- .../serialization/DurationSerializers.kt | 66 +++++++++ .../serialization/DurationSerializersTests.kt | 128 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 common/src/main/kotlin/serialization/DurationSerializers.kt create mode 100644 common/src/test/kotlin/serialization/DurationSerializersTests.kt diff --git a/common/src/main/kotlin/serialization/DurationSerializers.kt b/common/src/main/kotlin/serialization/DurationSerializers.kt new file mode 100644 index 000000000000..d9482db4e200 --- /dev/null +++ b/common/src/main/kotlin/serialization/DurationSerializers.kt @@ -0,0 +1,66 @@ +package dev.kord.common.serialization + +import kotlinx.serialization.KSerializer +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 +import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.DurationUnit.* +import kotlin.time.toDuration + + +/** Serializer that encodes and decodes [Duration]s. */ +public sealed class DurationSerializer(private val unit: DurationUnit, name: String) : KSerializer { + + final override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("dev.kord.common.serialization.$name", PrimitiveKind.LONG) + + protected abstract fun Duration.convert(): Long + + final override fun serialize(encoder: Encoder, value: Duration) { + encoder.encodeLong(value.convert()) + } + + final override fun deserialize(decoder: Decoder): Duration { + return decoder.decodeLong().toDuration(unit) + } +} + + +/** Serializer that encodes and decodes [Duration]s in [whole nanoseconds][Duration.inWholeNanoseconds]. */ +public object DurationInWholeNanosecondsSerializer : DurationSerializer(NANOSECONDS, "DurationInWholeNanoseconds") { + override fun Duration.convert(): Long = inWholeNanoseconds +} + +/** Serializer that encodes and decodes [Duration]s in [whole microseconds][Duration.inWholeMicroseconds]. */ +public object DurationInWholeMicrosecondsSerializer : DurationSerializer(MICROSECONDS, "DurationInWholeMicroseconds") { + override fun Duration.convert(): Long = inWholeMicroseconds +} + +/** Serializer that encodes and decodes [Duration]s in [whole milliseconds][Duration.inWholeMilliseconds]. */ +public object DurationInWholeMillisecondsSerializer : DurationSerializer(MILLISECONDS, "DurationInWholeMilliseconds") { + override fun Duration.convert(): Long = inWholeMilliseconds +} + +/** Serializer that encodes and decodes [Duration]s in [whole seconds][Duration.inWholeSeconds]. */ +public object DurationInWholeSecondsSerializer : DurationSerializer(SECONDS, "DurationInWholeSeconds") { + override fun Duration.convert(): Long = inWholeSeconds +} + +/** Serializer that encodes and decodes [Duration]s in [whole minutes][Duration.inWholeMinutes]. */ +public object DurationInWholeMinutesSerializer : DurationSerializer(MINUTES, "DurationInWholeMinutes") { + override fun Duration.convert(): Long = inWholeMinutes +} + +/** Serializer that encodes and decodes [Duration]s in [whole hours][Duration.inWholeHours]. */ +public object DurationInWholeHoursSerializer : DurationSerializer(HOURS, "DurationInWholeHours") { + override fun Duration.convert(): Long = inWholeHours +} + +/** Serializer that encodes and decodes [Duration]s in [whole days][Duration.inWholeDays]. */ +public object DurationInWholeDaysSerializer : DurationSerializer(DAYS, "DurationInWholeDays") { + override fun Duration.convert(): Long = inWholeDays +} diff --git a/common/src/test/kotlin/serialization/DurationSerializersTests.kt b/common/src/test/kotlin/serialization/DurationSerializersTests.kt new file mode 100644 index 000000000000..c509860ab08e --- /dev/null +++ b/common/src/test/kotlin/serialization/DurationSerializersTests.kt @@ -0,0 +1,128 @@ +package serialization + +import dev.kord.common.serialization.* +import kotlinx.serialization.KSerializer +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.time.Duration +import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.hours +import kotlin.time.Duration.Companion.microseconds +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.Duration.Companion.seconds + +abstract class DurationSerializerTest( + private val json: String, + private val duration: Duration, + private val durationToRound: Duration, + private val serializer: KSerializer, +) { + init { + require(duration.isPositive()) + require(durationToRound.isPositive()) + } + + + @Test + fun `zero Duration can be serialized`() { + val serialized = Json.encodeToString(serializer, Duration.ZERO) + assertEquals(expected = "0", actual = serialized) + } + + @Test + fun `zero Duration can be deserialized`() { + val deserialized = Json.decodeFromString(serializer, "0") + assertEquals(expected = Duration.ZERO, actual = deserialized) + } + + + @Test + fun `positive Duration can be serialized`() { + val serialized = Json.encodeToString(serializer, duration) + assertEquals(expected = json, actual = serialized) + } + + @Test + fun `positive Duration can be rounded and serialized`() { + val serialized = Json.encodeToString(serializer, durationToRound) + assertEquals(expected = json, actual = serialized) + } + + @Test + fun `positive Duration can be deserialized`() { + val deserialized = Json.decodeFromString(serializer, json) + assertEquals(expected = duration, actual = deserialized) + } + + + @Test + fun `negative Duration can be serialized`() { + val serialized = Json.encodeToString(serializer, -duration) + assertEquals(expected = "-$json", actual = serialized) + } + + @Test + fun `negative Duration can be rounded and serialized`() { + val serialized = Json.encodeToString(serializer, -durationToRound) + assertEquals(expected = "-$json", actual = serialized) + } + + @Test + fun `negative Duration can be deserialized`() { + val deserialized = Json.decodeFromString(serializer, "-$json") + assertEquals(expected = -duration, actual = deserialized) + } +} + + +class DurationInWholeNanosecondsSerializerTest : DurationSerializerTest( + json = "84169", + duration = 84169.nanoseconds, + durationToRound = 84169.48.nanoseconds, + serializer = DurationInWholeNanosecondsSerializer, +) + +class DurationInWholeMicrosecondsSerializerTest : DurationSerializerTest( + json = "25622456", + duration = 25622456.microseconds, + durationToRound = 25622456.4.microseconds, + serializer = DurationInWholeMicrosecondsSerializer, +) + +class DurationInWholeMillisecondsSerializerTest : DurationSerializerTest( + json = "3495189", + duration = 3495189.milliseconds, + durationToRound = 3495189.24.milliseconds, + serializer = DurationInWholeMillisecondsSerializer, +) + +class DurationInWholeSecondsSerializerTest : DurationSerializerTest( + json = "987465", + duration = 987465.seconds, + durationToRound = 987465.489.seconds, + serializer = DurationInWholeSecondsSerializer, +) + +class DurationInWholeMinutesSerializerTest : DurationSerializerTest( + json = "24905", + duration = 24905.minutes, + durationToRound = 24905.164.minutes, + serializer = DurationInWholeMinutesSerializer, +) + +class DurationInWholeHoursSerializerTest : DurationSerializerTest( + json = "7245", + duration = 7245.hours, + durationToRound = 7245.24.hours, + serializer = DurationInWholeHoursSerializer, +) + +class DurationInWholeDaysSerializerTest : DurationSerializerTest( + json = "92", + duration = 92.days, + durationToRound = 92.12.days, + serializer = DurationInWholeDaysSerializer, +) From c019a5bd59852ca1f0f5b4f2ffb8110fdfb66962 Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Sun, 3 Apr 2022 03:04:36 +0200 Subject: [PATCH 02/14] use Duration for rate_limit_per_user --- common/src/main/kotlin/entity/AuditLog.kt | 5 ++++- common/src/main/kotlin/entity/DiscordChannel.kt | 6 ++++-- common/src/test/kotlin/json/ChannelTest.kt | 3 ++- core/src/main/kotlin/cache/data/ChannelData.kt | 4 +++- .../src/main/kotlin/entity/channel/TextChannel.kt | 10 +++++++--- .../kotlin/entity/channel/thread/ThreadChannel.kt | 11 +++++++---- gateway/src/test/kotlin/json/SerializationTest.kt | 9 +++++---- .../builder/channel/EditGuildChannelBuilder.kt | 12 +++++++----- .../builder/channel/TextChannelCreateBuilder.kt | 15 ++++++++------- .../builder/channel/thread/ThreadModifyBuilder.kt | 5 +++-- .../main/kotlin/json/request/ChannelRequests.kt | 4 +++- .../src/main/kotlin/json/request/GuildRequests.kt | 4 +++- 12 files changed, 56 insertions(+), 32 deletions(-) diff --git a/common/src/main/kotlin/entity/AuditLog.kt b/common/src/main/kotlin/entity/AuditLog.kt index b403db311362..3ba6952d7892 100644 --- a/common/src/main/kotlin/entity/AuditLog.kt +++ b/common/src/main/kotlin/entity/AuditLog.kt @@ -3,12 +3,14 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.orEmpty +import dev.kord.common.serialization.DurationInWholeSecondsSerializer import kotlinx.datetime.Instant import kotlinx.serialization.* import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* import kotlinx.serialization.json.* +import kotlin.time.Duration import dev.kord.common.Color as CommonColor import dev.kord.common.entity.DefaultMessageNotificationLevel as CommonDefaultMessageNotificationLevel import dev.kord.common.entity.ExplicitContentFilter as CommonExplicitContentFilter @@ -237,7 +239,8 @@ public sealed class AuditLogChangeKey(public val name: String, public val ser public object ApplicationId : AuditLogChangeKey("application_id", serializer()) @SerialName("rate_limit_per_user") - public object RateLimitPerUser : AuditLogChangeKey("rate_limit_per_user", serializer()) + public object RateLimitPerUser : + AuditLogChangeKey("rate_limit_per_user", DurationInWholeSecondsSerializer) @SerialName("permissions") public object Permissions : AuditLogChangeKey("permissions", serializer()) diff --git a/common/src/main/kotlin/entity/DiscordChannel.kt b/common/src/main/kotlin/entity/DiscordChannel.kt index 5285a91b2a46..cfbe63ca1778 100644 --- a/common/src/main/kotlin/entity/DiscordChannel.kt +++ b/common/src/main/kotlin/entity/DiscordChannel.kt @@ -14,6 +14,8 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlin.DeprecationLevel.WARNING +import kotlin.time.Duration +import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds /** * A representation of a [Discord Channel Structure](https://discord.com/developers/docs/resources/channel). @@ -29,7 +31,7 @@ import kotlin.DeprecationLevel.WARNING * @param lastMessageId The id of the last message sent in this channel (may not point to an existing or valid message). * @param bitrate The bitrate (in bits) of the voice channel. * @param userLimit The user limit of the voice channel. - * @param rateLimitPerUser amount of seconds a user has to wait before sending another message; bots, + * @param rateLimitPerUser amount of time a user has to wait before sending another message; bots, * as well as users with the permission [Permission.ManageMessages] or [Permission.ManageChannels] are unaffected. * @param recipients The recipients of the DM. * @param icon The icon hash. @@ -56,7 +58,7 @@ public data class DiscordChannel( @SerialName("user_limit") val userLimit: OptionalInt = OptionalInt.Missing, @SerialName("rate_limit_per_user") - val rateLimitPerUser: OptionalInt = OptionalInt.Missing, + val rateLimitPerUser: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), val recipients: Optional> = Optional.Missing(), val icon: Optional = Optional.Missing(), @SerialName("owner_id") diff --git a/common/src/test/kotlin/json/ChannelTest.kt b/common/src/test/kotlin/json/ChannelTest.kt index a4b007d0c5fa..9b87b0d9619b 100644 --- a/common/src/test/kotlin/json/ChannelTest.kt +++ b/common/src/test/kotlin/json/ChannelTest.kt @@ -4,6 +4,7 @@ import dev.kord.common.entity.DiscordChannel import dev.kord.common.entity.optional.value import kotlinx.serialization.json.Json import org.junit.jupiter.api.Test +import kotlin.time.Duration.Companion.seconds private fun file(name: String): String { val loader = ChannelTest::class.java.classLoader @@ -106,7 +107,7 @@ class ChannelTest { type.value shouldBe 0 position.asNullable!! shouldBe 6 permissionOverwrites.value shouldBe emptyList() - rateLimitPerUser.asNullable shouldBe 2 + rateLimitPerUser.value shouldBe 2.seconds nsfw.value shouldBe true topic.value shouldBe "24/7 chat about how to gank Mike #2" lastMessageId.value?.toString() shouldBe "155117677105512449" diff --git a/core/src/main/kotlin/cache/data/ChannelData.kt b/core/src/main/kotlin/cache/data/ChannelData.kt index 15ece86f54ac..b49b0a0a54c8 100644 --- a/core/src/main/kotlin/cache/data/ChannelData.kt +++ b/core/src/main/kotlin/cache/data/ChannelData.kt @@ -6,6 +6,8 @@ import dev.kord.common.entity.* import dev.kord.common.entity.optional.* import kotlinx.datetime.Instant import kotlinx.serialization.Serializable +import kotlin.time.Duration +import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds @Serializable public data class ChannelData( @@ -20,7 +22,7 @@ public data class ChannelData( val lastMessageId: OptionalSnowflake? = OptionalSnowflake.Missing, val bitrate: OptionalInt = OptionalInt.Missing, val userLimit: OptionalInt = OptionalInt.Missing, - val rateLimitPerUser: OptionalInt = OptionalInt.Missing, + val rateLimitPerUser: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), val recipients: Optional> = Optional.Missing(), val icon: Optional = Optional.Missing(), val ownerId: OptionalSnowflake = OptionalSnowflake.Missing, diff --git a/core/src/main/kotlin/entity/channel/TextChannel.kt b/core/src/main/kotlin/entity/channel/TextChannel.kt index 2d95f394b156..0024e3fb0db3 100644 --- a/core/src/main/kotlin/entity/channel/TextChannel.kt +++ b/core/src/main/kotlin/entity/channel/TextChannel.kt @@ -1,6 +1,7 @@ package dev.kord.core.entity.channel -import dev.kord.common.entity.optional.getOrThrow +import dev.kord.common.entity.Permission.ManageChannels +import dev.kord.common.entity.Permission.ManageMessages import dev.kord.core.Kord import dev.kord.core.behavior.channel.ChannelBehavior import dev.kord.core.behavior.channel.GuildChannelBehavior @@ -9,6 +10,7 @@ import dev.kord.core.cache.data.ChannelData import dev.kord.core.supplier.EntitySupplier import dev.kord.core.supplier.EntitySupplyStrategy import java.util.* +import kotlin.time.Duration /** * An instance of a Discord Text Channel associated to a guild. @@ -25,9 +27,11 @@ public class TextChannel( public val isNsfw: Boolean get() = data.nsfw.discordBoolean /** - * The amount of seconds a user has to wait before sending another message. + * The amount of time a user has to wait before sending another message. + * + * Bots, as well as users with the permission [ManageMessages] or [ManageChannels], are unaffected. */ - public val userRateLimit: Int get() = data.rateLimitPerUser.getOrThrow() + public val userRateLimit: Duration? get() = data.rateLimitPerUser.value /** * returns a new [TextChannel] with the given [strategy]. diff --git a/core/src/main/kotlin/entity/channel/thread/ThreadChannel.kt b/core/src/main/kotlin/entity/channel/thread/ThreadChannel.kt index e87baa6995bb..4e5e254aee8d 100644 --- a/core/src/main/kotlin/entity/channel/thread/ThreadChannel.kt +++ b/core/src/main/kotlin/entity/channel/thread/ThreadChannel.kt @@ -1,6 +1,8 @@ package dev.kord.core.entity.channel.thread import dev.kord.common.entity.ArchiveDuration +import dev.kord.common.entity.Permission.ManageChannels +import dev.kord.common.entity.Permission.ManageMessages import dev.kord.common.entity.Snowflake import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.unwrap @@ -14,6 +16,7 @@ import dev.kord.core.supplier.EntitySupplier import dev.kord.core.supplier.EntitySupplyStrategy import kotlinx.datetime.Instant import kotlinx.datetime.toInstant +import kotlin.time.Duration public interface ThreadChannel : GuildMessageChannel, ThreadChannelBehavior { @@ -80,11 +83,11 @@ public interface ThreadChannel : GuildMessageChannel, ThreadChannelBehavior { public val createTimestamp: Instant? get() = threadData.createTimestamp.value /** - * amount of seconds a user has to wait before sending another message - * bots, users with the permission [Manage Messages][dev.kord.common.entity.Permission.ManageMessages] or - * [Manage Messages][dev.kord.common.entity.Permission.ManageChannels] are unaffected. + * The amount of time a user has to wait before sending another message. + * + * Bots, as well as users with the permission [ManageMessages] or [ManageChannels], are unaffected. */ - public val rateLimitPerUser: Int? get() = data.rateLimitPerUser.value + public val rateLimitPerUser: Duration? get() = data.rateLimitPerUser.value /** * member count for this thread. diff --git a/gateway/src/test/kotlin/json/SerializationTest.kt b/gateway/src/test/kotlin/json/SerializationTest.kt index 3f3818b05aa9..e2da037b72ce 100644 --- a/gateway/src/test/kotlin/json/SerializationTest.kt +++ b/gateway/src/test/kotlin/json/SerializationTest.kt @@ -6,10 +6,11 @@ import dev.kord.common.entity.optional.value import dev.kord.gateway.* import kotlinx.serialization.json.Json import org.junit.jupiter.api.Test +import kotlin.time.Duration.Companion.seconds private fun file(name: String): String { val loader = SerializationTest::class.java.classLoader - return loader.getResource("json/event/$name.json").readText() + return loader.getResource("json/event/$name.json")!!.readText() } class SerializationTest { @@ -102,7 +103,7 @@ class SerializationTest { type.value shouldBe 0 position.value shouldBe 6 permissionOverwrites.value shouldBe emptyList() - rateLimitPerUser.value shouldBe 2 + rateLimitPerUser.value shouldBe 2.seconds nsfw.value shouldBe true topic.value shouldBe "24/7 chat about how to gank Mike #2" lastMessageId.value?.toString() shouldBe "155117677105512449" @@ -121,7 +122,7 @@ class SerializationTest { type.value shouldBe 0 position.value shouldBe 6 permissionOverwrites.value shouldBe emptyList() - rateLimitPerUser.value shouldBe 2 + rateLimitPerUser.value shouldBe 2.seconds nsfw.value shouldBe true topic.value shouldBe "24/7 chat about how to gank Mike #2" lastMessageId.value?.toString() shouldBe "155117677105512449" @@ -139,7 +140,7 @@ class SerializationTest { type.value shouldBe 0 position.value shouldBe 6 permissionOverwrites.value shouldBe emptyList() - rateLimitPerUser.value shouldBe 2 + rateLimitPerUser.value shouldBe 2.seconds nsfw.value shouldBe true topic.value shouldBe "24/7 chat about how to gank Mike #2" lastMessageId.value?.toString() shouldBe "155117677105512449" diff --git a/rest/src/main/kotlin/builder/channel/EditGuildChannelBuilder.kt b/rest/src/main/kotlin/builder/channel/EditGuildChannelBuilder.kt index 2bb9b2fb0a42..8dc99222d433 100644 --- a/rest/src/main/kotlin/builder/channel/EditGuildChannelBuilder.kt +++ b/rest/src/main/kotlin/builder/channel/EditGuildChannelBuilder.kt @@ -11,6 +11,7 @@ import dev.kord.common.entity.optional.delegate.delegate import dev.kord.rest.builder.AuditRequestBuilder import dev.kord.rest.json.request.ChannelModifyPatchRequest import kotlin.DeprecationLevel.WARNING +import kotlin.time.Duration @KordDsl public class TextChannelModifyBuilder : PermissionOverwritesModifyBuilder, @@ -32,8 +33,8 @@ public class TextChannelModifyBuilder : PermissionOverwritesModifyBuilder, private var _parentId: OptionalSnowflake? = OptionalSnowflake.Missing public var parentId: Snowflake? by ::_parentId.delegate() - private var _rateLimitPerUser: OptionalInt? = OptionalInt.Missing - public var rateLimitPerUser: Int? by ::_rateLimitPerUser.delegate() + private var _rateLimitPerUser: Optional = Optional.Missing() + public var rateLimitPerUser: Duration? by ::_rateLimitPerUser.delegate() private var _permissionOverwrites: Optional> = Optional.Missing() override var permissionOverwrites: MutableSet? by ::_permissionOverwrites.delegate() @@ -140,8 +141,8 @@ public class NewsChannelModifyBuilder : AuditRequestBuilder = Optional.Missing() + public var rateLimitPerUser: Duration? by ::_rateLimitPerUser.delegate() private var _permissionOverwrites: Optional?> = Optional.Missing() public var permissionOverwrites: MutableSet? by ::_permissionOverwrites.delegate() @@ -151,8 +152,9 @@ public class NewsChannelModifyBuilder : AuditRequestBuilder = Optional.Missing() public var topic: String? by ::_topic.delegate() - private var _rateLimitPerUser: OptionalInt = OptionalInt.Missing - public var rateLimitPerUser: Int? by ::_rateLimitPerUser.delegate() + private var _rateLimitPerUser: Optional = Optional.Missing() + public var rateLimitPerUser: Duration? by ::_rateLimitPerUser.delegate() private var _position: OptionalInt = OptionalInt.Missing public var position: Int? by ::_position.delegate() @@ -36,11 +37,11 @@ public class TextChannelCreateBuilder(public var name: String) : override var permissionOverwrites: MutableSet = mutableSetOf() override fun toRequest(): GuildChannelCreateRequest = GuildChannelCreateRequest( - name, - ChannelType.GuildText, - _topic, - _rateLimitPerUser, - _position, + name = name, + type = ChannelType.GuildText, + topic = _topic, + rateLimitPerUser = _rateLimitPerUser, + position = _position, parentId = _parentId, nsfw = _nsfw, permissionOverwrite = Optional.missingOnEmpty(permissionOverwrites), diff --git a/rest/src/main/kotlin/builder/channel/thread/ThreadModifyBuilder.kt b/rest/src/main/kotlin/builder/channel/thread/ThreadModifyBuilder.kt index 88afc0bf131a..e1871bce32a5 100644 --- a/rest/src/main/kotlin/builder/channel/thread/ThreadModifyBuilder.kt +++ b/rest/src/main/kotlin/builder/channel/thread/ThreadModifyBuilder.kt @@ -6,6 +6,7 @@ import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.delegate.delegate import dev.kord.rest.builder.AuditRequestBuilder import dev.kord.rest.json.request.ChannelModifyPatchRequest +import kotlin.time.Duration public class ThreadModifyBuilder : AuditRequestBuilder { @@ -18,8 +19,8 @@ public class ThreadModifyBuilder : AuditRequestBuilder = Optional.Missing() + public var rateLimitPerUser: Duration? by ::_rateLimitPerUser.delegate() private var _autoArchiveDuration: OptionalInt = OptionalInt.Missing public var autoArchiveDuration: Int? by ::_autoArchiveDuration.delegate() diff --git a/rest/src/main/kotlin/json/request/ChannelRequests.kt b/rest/src/main/kotlin/json/request/ChannelRequests.kt index 90f50a6473dc..ea053e57d2ac 100644 --- a/rest/src/main/kotlin/json/request/ChannelRequests.kt +++ b/rest/src/main/kotlin/json/request/ChannelRequests.kt @@ -8,6 +8,8 @@ import dev.kord.common.entity.optional.OptionalSnowflake import kotlinx.datetime.Instant import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlin.time.Duration +import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds @Serializable public data class ChannelModifyPutRequest( @@ -33,7 +35,7 @@ public data class ChannelModifyPatchRequest( val topic: Optional = Optional.Missing(), val nsfw: OptionalBoolean? = OptionalBoolean.Missing, @SerialName("rate_limit_per_user") - val rateLimitPerUser: OptionalInt? = OptionalInt.Missing, + val rateLimitPerUser: Optional<@Serializable(InWholeSeconds::class) Duration?> = Optional.Missing(), val bitrate: OptionalInt? = OptionalInt.Missing, @SerialName("user_limit") val userLimit: OptionalInt? = OptionalInt.Missing, diff --git a/rest/src/main/kotlin/json/request/GuildRequests.kt b/rest/src/main/kotlin/json/request/GuildRequests.kt index 286124ca94b6..3d7006a18a54 100644 --- a/rest/src/main/kotlin/json/request/GuildRequests.kt +++ b/rest/src/main/kotlin/json/request/GuildRequests.kt @@ -15,6 +15,8 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.listSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlin.time.Duration +import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds @Serializable public data class GuildCreateRequest( @@ -45,7 +47,7 @@ public data class GuildChannelCreateRequest( @SerialName("user_limit") val userLimit: OptionalInt = OptionalInt.Missing, @SerialName("rate_limit_per_user") - val rateLimitPerUser: Optional = Optional.Missing(), + val rateLimitPerUser: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), val position: OptionalInt = OptionalInt.Missing, @SerialName("permission_overwrites") val permissionOverwrite: Optional> = Optional.Missing(), From f747805cc98105b4ac2e8888a674c9ea069d557c Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Sun, 3 Apr 2022 03:13:54 +0200 Subject: [PATCH 03/14] use Duration for reset_after --- rest/src/main/kotlin/json/response/Gateway.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rest/src/main/kotlin/json/response/Gateway.kt b/rest/src/main/kotlin/json/response/Gateway.kt index e3cd80d95147..0fec497c9eec 100644 --- a/rest/src/main/kotlin/json/response/Gateway.kt +++ b/rest/src/main/kotlin/json/response/Gateway.kt @@ -1,7 +1,9 @@ package dev.kord.rest.json.response +import dev.kord.common.serialization.DurationInWholeMillisecondsSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlin.time.Duration @Serializable public data class GatewayResponse(val url: String) @@ -19,7 +21,8 @@ public data class SessionStartLimitResponse( val total: Int, val remaining: Int, @SerialName("reset_after") - val resetAfter: Int, + @Serializable(with = DurationInWholeMillisecondsSerializer::class) + val resetAfter: Duration, @SerialName("max_concurrency") val maxConcurrency: Int ) From 2c0dd7f0f29cb666c6b65febf2ffbfe742fde9f6 Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Sun, 3 Apr 2022 03:41:58 +0200 Subject: [PATCH 04/14] use Duration for max_age --- common/src/main/kotlin/entity/AuditLog.kt | 2 +- common/src/main/kotlin/entity/DiscordInvite.kt | 5 ++++- .../main/kotlin/cache/data/InviteCreateData.kt | 5 ++++- core/src/main/kotlin/cache/data/InviteData.kt | 5 ++++- core/src/main/kotlin/entity/Invite.kt | 4 +--- .../kotlin/event/guild/InviteCreateEvent.kt | 4 +--- gateway/src/main/kotlin/Event.kt | 5 ++++- .../builder/channel/InviteCreateBuilder.kt | 18 +++++++----------- .../kotlin/json/request/InviteCreateRequest.kt | 11 +++++------ 9 files changed, 31 insertions(+), 28 deletions(-) diff --git a/common/src/main/kotlin/entity/AuditLog.kt b/common/src/main/kotlin/entity/AuditLog.kt index 3ba6952d7892..608eeb5c7db2 100644 --- a/common/src/main/kotlin/entity/AuditLog.kt +++ b/common/src/main/kotlin/entity/AuditLog.kt @@ -282,7 +282,7 @@ public sealed class AuditLogChangeKey(public val name: String, public val ser public object Uses : AuditLogChangeKey("uses", serializer()) @SerialName("max_age") - public object MaxAges : AuditLogChangeKey("max_age", serializer()) + public object MaxAges : AuditLogChangeKey("max_age", DurationInWholeSecondsSerializer) @SerialName("temporary") public object Temporary : AuditLogChangeKey("temporary", serializer()) diff --git a/common/src/main/kotlin/entity/DiscordInvite.kt b/common/src/main/kotlin/entity/DiscordInvite.kt index b46d2242855d..2e2e11f27dfc 100644 --- a/common/src/main/kotlin/entity/DiscordInvite.kt +++ b/common/src/main/kotlin/entity/DiscordInvite.kt @@ -2,6 +2,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalInt +import dev.kord.common.serialization.DurationInWholeSecondsSerializer import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -11,6 +12,7 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlin.time.Duration public sealed interface BaseDiscordInvite { public val code: String @@ -78,7 +80,8 @@ public data class DiscordInviteWithMetadata( @SerialName("max_uses") val maxUses: Int, @SerialName("max_age") - val maxAge: Int, + @Serializable(with = DurationInWholeSecondsSerializer::class) + val maxAge: Duration, val temporary: Boolean, @SerialName("created_at") val createdAt: Instant, diff --git a/core/src/main/kotlin/cache/data/InviteCreateData.kt b/core/src/main/kotlin/cache/data/InviteCreateData.kt index d7cc2f25880c..b0b21f66c27b 100644 --- a/core/src/main/kotlin/cache/data/InviteCreateData.kt +++ b/core/src/main/kotlin/cache/data/InviteCreateData.kt @@ -6,8 +6,10 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.map import dev.kord.common.entity.optional.mapSnowflake +import dev.kord.common.serialization.DurationInWholeSecondsSerializer import dev.kord.gateway.DiscordCreatedInvite import kotlinx.serialization.Serializable +import kotlin.time.Duration @Serializable public data class InviteCreateData( @@ -16,7 +18,8 @@ public data class InviteCreateData( val createdAt: String, val guildId: OptionalSnowflake = OptionalSnowflake.Missing, val inviterId: OptionalSnowflake = OptionalSnowflake.Missing, - val maxAge: Int, + @Serializable(with = DurationInWholeSecondsSerializer::class) + val maxAge: Duration, val maxUses: Int, val targetType: Optional = Optional.Missing(), val targetUserId: OptionalSnowflake = OptionalSnowflake.Missing, diff --git a/core/src/main/kotlin/cache/data/InviteData.kt b/core/src/main/kotlin/cache/data/InviteData.kt index b72a253eeace..dc3e3e43a538 100644 --- a/core/src/main/kotlin/cache/data/InviteData.kt +++ b/core/src/main/kotlin/cache/data/InviteData.kt @@ -2,8 +2,10 @@ package dev.kord.core.cache.data import dev.kord.common.entity.* import dev.kord.common.entity.optional.* +import dev.kord.common.serialization.DurationInWholeSecondsSerializer import kotlinx.datetime.Instant import kotlinx.serialization.Serializable +import kotlin.time.Duration public sealed interface BaseInviteData { public val code: String @@ -73,7 +75,8 @@ public data class InviteWithMetadataData( override val guildScheduledEvent: Optional = Optional.Missing(), val uses: Int, val maxUses: Int, - val maxAge: Int, + @Serializable(with = DurationInWholeSecondsSerializer::class) + val maxAge: Duration, val temporary: Boolean, val createdAt: Instant, ) : BaseInviteData { diff --git a/core/src/main/kotlin/entity/Invite.kt b/core/src/main/kotlin/entity/Invite.kt index 71085f17ab80..2c3a0d4eab6f 100644 --- a/core/src/main/kotlin/entity/Invite.kt +++ b/core/src/main/kotlin/entity/Invite.kt @@ -17,8 +17,6 @@ import dev.kord.core.supplier.EntitySupplier import dev.kord.core.supplier.EntitySupplyStrategy import kotlinx.datetime.Instant import kotlin.time.Duration -import kotlin.time.DurationUnit.SECONDS -import kotlin.time.toDuration /** * An instance of a [Discord Invite](https://discord.com/developers/docs/resources/invite). @@ -180,7 +178,7 @@ public class InviteWithMetadata( public val maxUses: Int get() = data.maxUses /** Duration after which the invite expires. */ - public val maxAge: Duration get() = data.maxAge.toDuration(unit = SECONDS) + public val maxAge: Duration get() = data.maxAge /** Whether this invite only grants temporary membership. */ public val temporary: Boolean get() = data.temporary diff --git a/core/src/main/kotlin/event/guild/InviteCreateEvent.kt b/core/src/main/kotlin/event/guild/InviteCreateEvent.kt index b249f5b86e8e..24d06b93f80c 100644 --- a/core/src/main/kotlin/event/guild/InviteCreateEvent.kt +++ b/core/src/main/kotlin/event/guild/InviteCreateEvent.kt @@ -22,8 +22,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.datetime.Instant import kotlinx.datetime.toInstant import kotlin.time.Duration -import kotlin.time.DurationUnit.SECONDS -import kotlin.time.toDuration /** * Sent when a new invite to a channel is created. @@ -87,7 +85,7 @@ public class InviteCreateEvent( /** * How long the invite is valid for. */ - public val maxAge: Duration get() = data.maxAge.toDuration(unit = SECONDS) + public val maxAge: Duration get() = data.maxAge /** * The maximum number of times the invite can be used. diff --git a/gateway/src/main/kotlin/Event.kt b/gateway/src/main/kotlin/Event.kt index 8c1c71e1c122..35850ab784a0 100644 --- a/gateway/src/main/kotlin/Event.kt +++ b/gateway/src/main/kotlin/Event.kt @@ -4,6 +4,7 @@ import dev.kord.common.entity.* import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalSnowflake +import dev.kord.common.serialization.DurationInWholeSecondsSerializer import kotlinx.serialization.* import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer @@ -17,6 +18,7 @@ import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import mu.KotlinLogging +import kotlin.time.Duration import kotlinx.serialization.DeserializationStrategy as KDeserializationStrategy private val jsonLogger = KotlinLogging.logger { } @@ -626,7 +628,8 @@ public data class DiscordCreatedInvite( val guildId: OptionalSnowflake = OptionalSnowflake.Missing, val inviter: Optional = Optional.Missing(), @SerialName("max_age") - val maxAge: Int, + @Serializable(with = DurationInWholeSecondsSerializer::class) + val maxAge: Duration, @SerialName("max_uses") val maxUses: Int, @SerialName("target_type") diff --git a/rest/src/main/kotlin/builder/channel/InviteCreateBuilder.kt b/rest/src/main/kotlin/builder/channel/InviteCreateBuilder.kt index 2fc454d0aad4..de40b3696960 100644 --- a/rest/src/main/kotlin/builder/channel/InviteCreateBuilder.kt +++ b/rest/src/main/kotlin/builder/channel/InviteCreateBuilder.kt @@ -16,25 +16,22 @@ import kotlin.time.toDuration public class InviteCreateBuilder : AuditRequestBuilder { override var reason: String? = null - private var _maxAge: OptionalInt = OptionalInt.Missing + private var _maxAge: Optional = Optional.Missing() /** * The duration of invite in seconds before expiry, or 0 for never. 86400 (24 hours) by default. */ @Deprecated("'age' was renamed to 'maxAge'", ReplaceWith("this.maxAge")) - public var age: Int? by ::_maxAge.delegate() + public var age: Int? + get() = _maxAge.value?.inWholeSeconds?.toInt() + set(value) { + _maxAge = value?.toDuration(unit = SECONDS)?.optional() ?: Optional.Missing() + } /** * The duration before invite expiry, or 0 for never. Between 0 and 604800 seconds (7 days). 24 hours by default. */ - public var maxAge: Duration? - get() = _maxAge.value?.toDuration(unit = SECONDS) - set(value) { - _maxAge = when (value) { - null -> OptionalInt.Missing - else -> OptionalInt.Value(value.inWholeSeconds.coerceIn(intValues).toInt()) - } - } + public var maxAge: Duration? by ::_maxAge.delegate() private var _maxUses: OptionalInt = OptionalInt.Missing @@ -124,4 +121,3 @@ public class InviteCreateBuilder : AuditRequestBuilder { private inline val OptionalSnowflake.isMissing get() = this == OptionalSnowflake.Missing private inline val OptionalSnowflake.isPresent get() = this != OptionalSnowflake.Missing -private inline val intValues get() = Int.MIN_VALUE.toLong()..Int.MAX_VALUE.toLong() diff --git a/rest/src/main/kotlin/json/request/InviteCreateRequest.kt b/rest/src/main/kotlin/json/request/InviteCreateRequest.kt index 3fa8bd7fbe5c..ca769f62e3a6 100644 --- a/rest/src/main/kotlin/json/request/InviteCreateRequest.kt +++ b/rest/src/main/kotlin/json/request/InviteCreateRequest.kt @@ -1,17 +1,16 @@ package dev.kord.rest.json.request import dev.kord.common.entity.InviteTargetType -import dev.kord.common.entity.optional.Optional -import dev.kord.common.entity.optional.OptionalBoolean -import dev.kord.common.entity.optional.OptionalInt -import dev.kord.common.entity.optional.OptionalSnowflake +import dev.kord.common.entity.optional.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlin.time.Duration +import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds @Serializable public data class InviteCreateRequest( @SerialName("max_age") - val maxAge: OptionalInt = OptionalInt.Missing, + val maxAge: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), @SerialName("max_uses") val maxUses: OptionalInt = OptionalInt.Missing, val temporary: OptionalBoolean = OptionalBoolean.Missing, @@ -31,7 +30,7 @@ public data class InviteCreateRequest( ) { @Deprecated("'age' was renamed to 'maxAge'", ReplaceWith("this.maxAge")) public val age: OptionalInt - get() = maxAge + get() = maxAge.value?.inWholeSeconds?.toInt()?.optionalInt() ?: OptionalInt.Missing @Deprecated("'uses' was renamed to 'maxUses'", ReplaceWith("this.maxUses")) public val uses: OptionalInt From d0e5c4c86ec8d92b6194ac687586380a8b093bad Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Sun, 3 Apr 2022 04:11:01 +0200 Subject: [PATCH 05/14] use Duration for afk_timeout --- common/src/main/kotlin/entity/AuditLog.kt | 2 +- common/src/main/kotlin/entity/DiscordGuild.kt | 8 ++++++-- common/src/test/kotlin/json/GuildTest.kt | 3 ++- core/src/main/kotlin/cache/data/GuildData.kt | 5 ++++- core/src/main/kotlin/entity/Guild.kt | 5 +++-- core/src/test/kotlin/live/LiveGuildTest.kt | 7 ++++--- core/src/test/kotlin/performance/KordEventDropTest.kt | 3 ++- rest/src/main/kotlin/builder/guild/GuildCreateBuilder.kt | 8 ++++---- rest/src/main/kotlin/builder/guild/GuildModifyBuilder.kt | 6 +++--- rest/src/main/kotlin/json/request/GuildRequests.kt | 4 ++-- 10 files changed, 31 insertions(+), 20 deletions(-) diff --git a/common/src/main/kotlin/entity/AuditLog.kt b/common/src/main/kotlin/entity/AuditLog.kt index 608eeb5c7db2..3c8576cc9d7e 100644 --- a/common/src/main/kotlin/entity/AuditLog.kt +++ b/common/src/main/kotlin/entity/AuditLog.kt @@ -183,7 +183,7 @@ public sealed class AuditLogChangeKey(public val name: String, public val ser public object AfkChannelId : AuditLogChangeKey("afk_channel_id", serializer()) @SerialName("afk_timeout") - public object AfkTimeout : AuditLogChangeKey("afk_timeout", serializer()) + public object AfkTimeout : AuditLogChangeKey("afk_timeout", DurationInWholeSecondsSerializer) @SerialName("mfa_level") public object MFALevel : AuditLogChangeKey("mfa_level", serializer()) diff --git a/common/src/main/kotlin/entity/DiscordGuild.kt b/common/src/main/kotlin/entity/DiscordGuild.kt index 90ffac1bd8d2..7d8ffa066437 100644 --- a/common/src/main/kotlin/entity/DiscordGuild.kt +++ b/common/src/main/kotlin/entity/DiscordGuild.kt @@ -4,6 +4,7 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake +import dev.kord.common.serialization.DurationInWholeSecondsSerializer import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -13,6 +14,7 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlin.time.Duration /** * A partial representation of a [DiscordGuild] that may be [unavailable]. @@ -40,7 +42,7 @@ public data class DiscordUnavailableGuild( * @param permissions The total permissions for [DiscordUser] in the guild (excludes [overwrites][Overwrite]). * @param region [DiscordVoiceRegion] id for the guild. * @param afkChannelId The id of afk channel. - * @param afkTimeout The afk timeout in seconds. + * @param afkTimeout The afk timeout. * @param widgetEnabled True if the server widget is enabled. * @param widgetChannelId The channel id that the widget will generate an invite to, or `null` if set to no invite. * @param verificationLevel [VerificationLevel] required for the guild. @@ -93,7 +95,9 @@ public data class DiscordGuild( ReplaceWith("DiscordChannel#rtcRegion") ) val region: String, @SerialName("afk_channel_id") val afkChannelId: Snowflake?, - @SerialName("afk_timeout") val afkTimeout: Int, + @SerialName("afk_timeout") + @Serializable(with = DurationInWholeSecondsSerializer::class) + val afkTimeout: Duration, @SerialName("widget_enabled") val widgetEnabled: OptionalBoolean = OptionalBoolean.Missing, @SerialName("widget_channel_id") val widgetChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, @SerialName("verification_level") val verificationLevel: VerificationLevel, diff --git a/common/src/test/kotlin/json/GuildTest.kt b/common/src/test/kotlin/json/GuildTest.kt index 00ac332dee13..5251a69711ee 100644 --- a/common/src/test/kotlin/json/GuildTest.kt +++ b/common/src/test/kotlin/json/GuildTest.kt @@ -3,6 +3,7 @@ package json import dev.kord.common.entity.* import kotlinx.serialization.json.Json import org.junit.jupiter.api.Test +import kotlin.time.Duration.Companion.seconds private fun file(name: String): String { @@ -40,7 +41,7 @@ class GuildTest { @Suppress("DEPRECATION") region shouldBe "us-west" afkChannelId shouldBe null - afkTimeout shouldBe 300 + afkTimeout shouldBe 300.seconds systemChannelId shouldBe null widgetEnabled shouldBe true widgetChannelId shouldBe null diff --git a/core/src/main/kotlin/cache/data/GuildData.kt b/core/src/main/kotlin/cache/data/GuildData.kt index a0b4043f256a..09bce0c13bdf 100644 --- a/core/src/main/kotlin/cache/data/GuildData.kt +++ b/core/src/main/kotlin/cache/data/GuildData.kt @@ -4,7 +4,9 @@ import dev.kord.cache.api.data.DataDescription import dev.kord.cache.api.data.description import dev.kord.common.entity.* import dev.kord.common.entity.optional.* +import dev.kord.common.serialization.DurationInWholeSecondsSerializer import kotlinx.serialization.Serializable +import kotlin.time.Duration private val MessageData.nullableGuildId get() = guildId.value private val ChannelData.nullableGuildId get() = guildId.value @@ -24,7 +26,8 @@ public data class GuildData( @Deprecated("The region field has been moved to Channel#rtcRegion in Discord API v9", ReplaceWith("ChannelData#rtcRegion")) val region: String, val afkChannelId: Snowflake? = null, - val afkTimeout: Int, + @Serializable(with = DurationInWholeSecondsSerializer::class) + val afkTimeout: Duration, val widgetEnabled: OptionalBoolean = OptionalBoolean.Missing, val widgetChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, val verificationLevel: VerificationLevel, diff --git a/core/src/main/kotlin/entity/Guild.kt b/core/src/main/kotlin/entity/Guild.kt index 314c08502020..d4e2283467f0 100644 --- a/core/src/main/kotlin/entity/Guild.kt +++ b/core/src/main/kotlin/entity/Guild.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.flow import kotlinx.datetime.Instant import kotlinx.datetime.toInstant import java.util.* +import kotlin.time.Duration /** * An instance of a [Discord Guild](https://discord.com/developers/docs/resources/guild). @@ -60,9 +61,9 @@ public class Guild( } /** - * The afk timeout in seconds. + * The afk timeout. */ - public val afkTimeout: Int get() = data.afkTimeout + public val afkTimeout: Duration get() = data.afkTimeout /** * The id of the guild creator if it is bot-created. diff --git a/core/src/test/kotlin/live/LiveGuildTest.kt b/core/src/test/kotlin/live/LiveGuildTest.kt index 50d6ff7efae9..481e8944f4a8 100644 --- a/core/src/test/kotlin/live/LiveGuildTest.kt +++ b/core/src/test/kotlin/live/LiveGuildTest.kt @@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.time.Duration.Companion.seconds @TestInstance(TestInstance.Lifecycle.PER_CLASS) @OptIn(KordPreview::class) @@ -39,7 +40,7 @@ class LiveGuildTest : AbstractLiveEntityTest() { name = "", ownerId = randomId(), region = "", - afkTimeout = 0, + afkTimeout = 0.seconds, verificationLevel = VerificationLevel.None, defaultMessageNotifications = DefaultMessageNotificationLevel.AllMessages, explicitContentFilter = ExplicitContentFilter.Disabled, @@ -697,7 +698,7 @@ class LiveGuildTest : AbstractLiveEntityTest() { ownerId = randomId(), region = "", afkChannelId = null, - afkTimeout = 0, + afkTimeout = 0.seconds, verificationLevel = VerificationLevel.None, defaultMessageNotifications = DefaultMessageNotificationLevel.AllMessages, explicitContentFilter = ExplicitContentFilter.Disabled, @@ -741,7 +742,7 @@ class LiveGuildTest : AbstractLiveEntityTest() { ownerId = randomId(), region = "", afkChannelId = null, - afkTimeout = 0, + afkTimeout = 0.seconds, verificationLevel = VerificationLevel.None, defaultMessageNotifications = DefaultMessageNotificationLevel.AllMessages, explicitContentFilter = ExplicitContentFilter.Disabled, diff --git a/core/src/test/kotlin/performance/KordEventDropTest.kt b/core/src/test/kotlin/performance/KordEventDropTest.kt index 6e2cd01fdf0d..8d71fbee5858 100644 --- a/core/src/test/kotlin/performance/KordEventDropTest.kt +++ b/core/src/test/kotlin/performance/KordEventDropTest.kt @@ -26,6 +26,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds class KordEventDropTest { @@ -64,7 +65,7 @@ class KordEventDropTest { DiscordGuild( Snowflake("1337"), "discord guild", - afkTimeout = 0, + afkTimeout = 0.seconds, defaultMessageNotifications = DefaultMessageNotificationLevel.AllMessages, emojis = emptyList(), explicitContentFilter = ExplicitContentFilter.AllMembers, diff --git a/rest/src/main/kotlin/builder/guild/GuildCreateBuilder.kt b/rest/src/main/kotlin/builder/guild/GuildCreateBuilder.kt index ae598dae73bd..f8ac4e2ce8ab 100644 --- a/rest/src/main/kotlin/builder/guild/GuildCreateBuilder.kt +++ b/rest/src/main/kotlin/builder/guild/GuildCreateBuilder.kt @@ -6,7 +6,6 @@ import dev.kord.common.entity.ExplicitContentFilter import dev.kord.common.entity.Snowflake import dev.kord.common.entity.VerificationLevel import dev.kord.common.entity.optional.Optional -import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.delegate.delegate import dev.kord.common.entity.optional.map @@ -23,6 +22,7 @@ import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.random.Random import kotlin.random.nextULong +import kotlin.time.Duration @KordDsl public class GuildCreateBuilder(public var name: String) : RequestBuilder { @@ -75,12 +75,12 @@ public class GuildCreateBuilder(public var name: String) : RequestBuilder = Optional.Missing() /** - * The afk timeout in seconds. + * The afk timeout. */ - public var afkTimeout: Int? by ::_afkTimeout.delegate() + public var afkTimeout: Duration? by ::_afkTimeout.delegate() private var _systemChannelId: OptionalSnowflake = OptionalSnowflake.Missing diff --git a/rest/src/main/kotlin/builder/guild/GuildModifyBuilder.kt b/rest/src/main/kotlin/builder/guild/GuildModifyBuilder.kt index e807092f03f2..1ac624dffaab 100644 --- a/rest/src/main/kotlin/builder/guild/GuildModifyBuilder.kt +++ b/rest/src/main/kotlin/builder/guild/GuildModifyBuilder.kt @@ -6,7 +6,6 @@ import dev.kord.common.entity.ExplicitContentFilter import dev.kord.common.entity.Snowflake import dev.kord.common.entity.VerificationLevel import dev.kord.common.entity.optional.Optional -import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.delegate.delegate import dev.kord.common.entity.optional.map @@ -14,6 +13,7 @@ import dev.kord.rest.Image import dev.kord.rest.builder.AuditRequestBuilder import dev.kord.rest.json.request.GuildModifyRequest import java.util.* +import kotlin.time.Duration @KordDsl public class GuildModifyBuilder : AuditRequestBuilder { @@ -37,8 +37,8 @@ public class GuildModifyBuilder : AuditRequestBuilder { private var _afkChannelId: OptionalSnowflake? = OptionalSnowflake.Missing public var afkChannelId: Snowflake? by ::_afkChannelId.delegate() - private var _afkTimeout: OptionalInt = OptionalInt.Missing - public var afkTimeout: Int? by ::_afkTimeout.delegate() + private var _afkTimeout: Optional = Optional.Missing() + public var afkTimeout: Duration? by ::_afkTimeout.delegate() private var _icon: Optional = Optional.Missing() public var icon: Image? by ::_icon.delegate() diff --git a/rest/src/main/kotlin/json/request/GuildRequests.kt b/rest/src/main/kotlin/json/request/GuildRequests.kt index 3d7006a18a54..81f810e180f2 100644 --- a/rest/src/main/kotlin/json/request/GuildRequests.kt +++ b/rest/src/main/kotlin/json/request/GuildRequests.kt @@ -33,7 +33,7 @@ public data class GuildCreateRequest( @SerialName("afk_channel_id") val afkChannelId: OptionalSnowflake = OptionalSnowflake.Missing, @SerialName("afk_timeout") - val afkTimeout: OptionalInt = OptionalInt.Missing, + val afkTimeout: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), @SerialName("system_channel_id") val systemChannelId: OptionalSnowflake = OptionalSnowflake.Missing ) @@ -230,7 +230,7 @@ public data class GuildModifyRequest( @SerialName("afk_channel_id") val afkChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, @SerialName("afk_timeout") - val afkTimeout: OptionalInt = OptionalInt.Missing, + val afkTimeout: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), val icon: Optional = Optional.Missing(), @SerialName("owner_id") val ownerId: OptionalSnowflake = OptionalSnowflake.Missing, From f42dc634c586053ef94c4363cd44f5b847c06b36 Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Sun, 3 Apr 2022 04:42:20 +0200 Subject: [PATCH 06/14] use Duration for auto_archive_duration --- .../src/main/kotlin/entity/DiscordChannel.kt | 37 ++++++++++--------- .../channel/thread/ThreadModifyBuilder.kt | 6 +-- .../kotlin/json/request/ChannelRequests.kt | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/common/src/main/kotlin/entity/DiscordChannel.kt b/common/src/main/kotlin/entity/DiscordChannel.kt index cfbe63ca1778..8a293fe21003 100644 --- a/common/src/main/kotlin/entity/DiscordChannel.kt +++ b/common/src/main/kotlin/entity/DiscordChannel.kt @@ -4,6 +4,7 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake +import dev.kord.common.serialization.DurationInWholeMinutesSerializer import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -15,6 +16,7 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlin.DeprecationLevel.WARNING import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds /** @@ -196,34 +198,33 @@ public data class DiscordThreadMetadata( ) @Serializable(with = ArchiveDuration.Serializer::class) -public sealed class ArchiveDuration(public val duration: Int) { - public class Unknown(duration: Int) : ArchiveDuration(duration) - public object Hour : ArchiveDuration(60) - public object Day : ArchiveDuration(1440) - public object ThreeDays : ArchiveDuration(4320) - public object Week : ArchiveDuration(10080) +public sealed class ArchiveDuration(public val duration: Duration) { + public class Unknown(duration: Duration) : ArchiveDuration(duration) + public object Hour : ArchiveDuration(60.minutes) + public object Day : ArchiveDuration(1440.minutes) + public object ThreeDays : ArchiveDuration(4320.minutes) + public object Week : ArchiveDuration(10080.minutes) public object Serializer : KSerializer { + + override val descriptor: SerialDescriptor get() = DurationInWholeMinutesSerializer.descriptor + override fun deserialize(decoder: Decoder): ArchiveDuration { - val value = decoder.decodeInt() + val value = decoder.decodeSerializableValue(DurationInWholeMinutesSerializer) return values.firstOrNull { it.duration == value } ?: Unknown(value) } - override val descriptor: SerialDescriptor - get() = PrimitiveSerialDescriptor("AutoArchieveDuration", PrimitiveKind.INT) - override fun serialize(encoder: Encoder, value: ArchiveDuration) { - encoder.encodeInt(value.duration) + encoder.encodeSerializableValue(DurationInWholeMinutesSerializer, value.duration) } } public companion object { - public val values: Set - get() = setOf( - Hour, - Day, - ThreeDays, - Week, - ) + public val values: Set = setOf( + Hour, + Day, + ThreeDays, + Week, + ) } } diff --git a/rest/src/main/kotlin/builder/channel/thread/ThreadModifyBuilder.kt b/rest/src/main/kotlin/builder/channel/thread/ThreadModifyBuilder.kt index e1871bce32a5..c4a25fa4888d 100644 --- a/rest/src/main/kotlin/builder/channel/thread/ThreadModifyBuilder.kt +++ b/rest/src/main/kotlin/builder/channel/thread/ThreadModifyBuilder.kt @@ -1,8 +1,8 @@ package dev.kord.rest.builder.channel.thread +import dev.kord.common.entity.ArchiveDuration import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean -import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.delegate.delegate import dev.kord.rest.builder.AuditRequestBuilder import dev.kord.rest.json.request.ChannelModifyPatchRequest @@ -22,8 +22,8 @@ public class ThreadModifyBuilder : AuditRequestBuilder = Optional.Missing() public var rateLimitPerUser: Duration? by ::_rateLimitPerUser.delegate() - private var _autoArchiveDuration: OptionalInt = OptionalInt.Missing - public var autoArchiveDuration: Int? by ::_autoArchiveDuration.delegate() + private var _autoArchiveDuration: Optional = Optional.Missing() + public var autoArchiveDuration: ArchiveDuration? by ::_autoArchiveDuration.delegate() private var _invitable: OptionalBoolean = OptionalBoolean.Missing public var invitable: Boolean? by ::_invitable.delegate() diff --git a/rest/src/main/kotlin/json/request/ChannelRequests.kt b/rest/src/main/kotlin/json/request/ChannelRequests.kt index ea053e57d2ac..84fa42d1c0e9 100644 --- a/rest/src/main/kotlin/json/request/ChannelRequests.kt +++ b/rest/src/main/kotlin/json/request/ChannelRequests.kt @@ -45,7 +45,7 @@ public data class ChannelModifyPatchRequest( val parentId: OptionalSnowflake? = OptionalSnowflake.Missing, val archived: OptionalBoolean = OptionalBoolean.Missing, @SerialName("auto_archive_duration") - val autoArchiveDuration: OptionalInt = OptionalInt.Missing, + val autoArchiveDuration: Optional = Optional.Missing(), val locked: OptionalBoolean = OptionalBoolean.Missing, @SerialName("rtc_region") val rtcRegion: Optional = Optional.Missing(), From ed14bb2bd35199c0ee94ae528e7f23f8ab0f2831 Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Sun, 3 Apr 2022 06:00:22 +0200 Subject: [PATCH 07/14] getter --- common/src/main/kotlin/entity/DiscordChannel.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/common/src/main/kotlin/entity/DiscordChannel.kt b/common/src/main/kotlin/entity/DiscordChannel.kt index 8a293fe21003..85712a5caf44 100644 --- a/common/src/main/kotlin/entity/DiscordChannel.kt +++ b/common/src/main/kotlin/entity/DiscordChannel.kt @@ -220,11 +220,12 @@ public sealed class ArchiveDuration(public val duration: Duration) { } public companion object { - public val values: Set = setOf( - Hour, - Day, - ThreeDays, - Week, - ) + public val values: Set + get() = setOf( + Hour, + Day, + ThreeDays, + Week, + ) } } From 467799a58b9442207728d3bc047a1e3d1da70699 Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Sun, 3 Apr 2022 21:48:41 +0200 Subject: [PATCH 08/14] use Duration.toLong() --- .../serialization/DurationSerializers.kt | 32 +++++-------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/common/src/main/kotlin/serialization/DurationSerializers.kt b/common/src/main/kotlin/serialization/DurationSerializers.kt index d9482db4e200..e46b85b11600 100644 --- a/common/src/main/kotlin/serialization/DurationSerializers.kt +++ b/common/src/main/kotlin/serialization/DurationSerializers.kt @@ -18,10 +18,8 @@ public sealed class DurationSerializer(private val unit: DurationUnit, name: Str final override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("dev.kord.common.serialization.$name", PrimitiveKind.LONG) - protected abstract fun Duration.convert(): Long - final override fun serialize(encoder: Encoder, value: Duration) { - encoder.encodeLong(value.convert()) + encoder.encodeLong(value.toLong(unit)) } final override fun deserialize(decoder: Decoder): Duration { @@ -31,36 +29,22 @@ public sealed class DurationSerializer(private val unit: DurationUnit, name: Str /** Serializer that encodes and decodes [Duration]s in [whole nanoseconds][Duration.inWholeNanoseconds]. */ -public object DurationInWholeNanosecondsSerializer : DurationSerializer(NANOSECONDS, "DurationInWholeNanoseconds") { - override fun Duration.convert(): Long = inWholeNanoseconds -} +public object DurationInWholeNanosecondsSerializer : DurationSerializer(NANOSECONDS, "DurationInWholeNanoseconds") /** Serializer that encodes and decodes [Duration]s in [whole microseconds][Duration.inWholeMicroseconds]. */ -public object DurationInWholeMicrosecondsSerializer : DurationSerializer(MICROSECONDS, "DurationInWholeMicroseconds") { - override fun Duration.convert(): Long = inWholeMicroseconds -} +public object DurationInWholeMicrosecondsSerializer : DurationSerializer(MICROSECONDS, "DurationInWholeMicroseconds") /** Serializer that encodes and decodes [Duration]s in [whole milliseconds][Duration.inWholeMilliseconds]. */ -public object DurationInWholeMillisecondsSerializer : DurationSerializer(MILLISECONDS, "DurationInWholeMilliseconds") { - override fun Duration.convert(): Long = inWholeMilliseconds -} +public object DurationInWholeMillisecondsSerializer : DurationSerializer(MILLISECONDS, "DurationInWholeMilliseconds") /** Serializer that encodes and decodes [Duration]s in [whole seconds][Duration.inWholeSeconds]. */ -public object DurationInWholeSecondsSerializer : DurationSerializer(SECONDS, "DurationInWholeSeconds") { - override fun Duration.convert(): Long = inWholeSeconds -} +public object DurationInWholeSecondsSerializer : DurationSerializer(SECONDS, "DurationInWholeSeconds") /** Serializer that encodes and decodes [Duration]s in [whole minutes][Duration.inWholeMinutes]. */ -public object DurationInWholeMinutesSerializer : DurationSerializer(MINUTES, "DurationInWholeMinutes") { - override fun Duration.convert(): Long = inWholeMinutes -} +public object DurationInWholeMinutesSerializer : DurationSerializer(MINUTES, "DurationInWholeMinutes") /** Serializer that encodes and decodes [Duration]s in [whole hours][Duration.inWholeHours]. */ -public object DurationInWholeHoursSerializer : DurationSerializer(HOURS, "DurationInWholeHours") { - override fun Duration.convert(): Long = inWholeHours -} +public object DurationInWholeHoursSerializer : DurationSerializer(HOURS, "DurationInWholeHours") /** Serializer that encodes and decodes [Duration]s in [whole days][Duration.inWholeDays]. */ -public object DurationInWholeDaysSerializer : DurationSerializer(DAYS, "DurationInWholeDays") { - override fun Duration.convert(): Long = inWholeDays -} +public object DurationInWholeDaysSerializer : DurationSerializer(DAYS, "DurationInWholeDays") From 2c4c823b58d660bb18a9d4a13709cc730f255e24 Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Sun, 3 Apr 2022 23:31:48 +0200 Subject: [PATCH 09/14] use Duration for expire_grace_period --- common/src/main/kotlin/entity/AuditLog.kt | 3 ++- common/src/main/kotlin/entity/DiscordIntegration.kt | 5 ++++- core/src/main/kotlin/cache/data/IntegrationData.kt | 5 ++++- core/src/main/kotlin/entity/Integration.kt | 6 ++---- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/entity/AuditLog.kt b/common/src/main/kotlin/entity/AuditLog.kt index 3c8576cc9d7e..d953c61d4637 100644 --- a/common/src/main/kotlin/entity/AuditLog.kt +++ b/common/src/main/kotlin/entity/AuditLog.kt @@ -3,6 +3,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.orEmpty +import dev.kord.common.serialization.DurationInWholeDaysSerializer import dev.kord.common.serialization.DurationInWholeSecondsSerializer import kotlinx.datetime.Instant import kotlinx.serialization.* @@ -348,7 +349,7 @@ public sealed class AuditLogChangeKey(public val name: String, public val ser public object ExpireBehavior : AuditLogChangeKey("expire_behavior", serializer()) @SerialName("expire_grace_period") - public object ExpireGracePeriod : AuditLogChangeKey("expire_grace_period", serializer()) + public object ExpireGracePeriod : AuditLogChangeKey("expire_grace_period", DurationInWholeDaysSerializer) @SerialName("user_limit") public object UserLimit : AuditLogChangeKey("user_limit", serializer()) diff --git a/common/src/main/kotlin/entity/DiscordIntegration.kt b/common/src/main/kotlin/entity/DiscordIntegration.kt index 02806e7acf48..d2a4268ec03e 100644 --- a/common/src/main/kotlin/entity/DiscordIntegration.kt +++ b/common/src/main/kotlin/entity/DiscordIntegration.kt @@ -2,6 +2,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean +import dev.kord.common.serialization.DurationInWholeDaysSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -10,6 +11,7 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlin.time.Duration @Serializable public data class DiscordIntegration( @@ -25,7 +27,8 @@ public data class DiscordIntegration( @SerialName("expire_behavior") val expireBehavior: IntegrationExpireBehavior, @SerialName("expire_grace_period") - val expireGracePeriod: Int, + @Serializable(with = DurationInWholeDaysSerializer::class) + val expireGracePeriod: Duration, val user: DiscordUser, val account: DiscordIntegrationsAccount, @SerialName("synced_at") diff --git a/core/src/main/kotlin/cache/data/IntegrationData.kt b/core/src/main/kotlin/cache/data/IntegrationData.kt index 2dd25ad0a662..523a2de9729e 100644 --- a/core/src/main/kotlin/cache/data/IntegrationData.kt +++ b/core/src/main/kotlin/cache/data/IntegrationData.kt @@ -2,7 +2,9 @@ package dev.kord.core.cache.data import dev.kord.common.entity.* import dev.kord.common.entity.optional.OptionalBoolean +import dev.kord.common.serialization.DurationInWholeDaysSerializer import kotlinx.serialization.Serializable +import kotlin.time.Duration @Serializable public data class IntegrationData( @@ -15,7 +17,8 @@ public data class IntegrationData( val roleId: Snowflake, val enableEmoticons: OptionalBoolean = OptionalBoolean.Missing, val expireBehavior: IntegrationExpireBehavior, - val expireGracePeriod: Int, + @Serializable(with = DurationInWholeDaysSerializer::class) + val expireGracePeriod: Duration, val user: DiscordUser, val account: IntegrationsAccountData, val syncedAt: String, diff --git a/core/src/main/kotlin/entity/Integration.kt b/core/src/main/kotlin/entity/Integration.kt index d2ae7ede8e94..6a5ccfd9e2a5 100644 --- a/core/src/main/kotlin/entity/Integration.kt +++ b/core/src/main/kotlin/entity/Integration.kt @@ -19,7 +19,6 @@ import java.util.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.time.Duration -import kotlin.time.Duration.Companion.days /** * A [Discord integration](https://discord.com/developers/docs/resources/guild#get-guild-integrations). @@ -95,10 +94,9 @@ public class Integration( get() = data.expireBehavior /** - * The grace period in days before expiring subscribers. + * The grace period before expiring subscribers. */ - public val expireGracePeriod: Duration - get() = data.expireGracePeriod.days + public val expireGracePeriod: Duration get() = data.expireGracePeriod /** * The id of the [user][User] for this integration. From 1275cc7a558e2140711cadfaa2db68c37a7199de Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Mon, 4 Apr 2022 04:49:47 +0200 Subject: [PATCH 10/14] @Serializable typealiases --- .../serialization/DurationSerializers.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/common/src/main/kotlin/serialization/DurationSerializers.kt b/common/src/main/kotlin/serialization/DurationSerializers.kt index e46b85b11600..2277573af03c 100644 --- a/common/src/main/kotlin/serialization/DurationSerializers.kt +++ b/common/src/main/kotlin/serialization/DurationSerializers.kt @@ -1,6 +1,7 @@ package dev.kord.common.serialization import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor @@ -28,23 +29,64 @@ public sealed class DurationSerializer(private val unit: DurationUnit, name: Str } +// nanoseconds + /** Serializer that encodes and decodes [Duration]s in [whole nanoseconds][Duration.inWholeNanoseconds]. */ public object DurationInWholeNanosecondsSerializer : DurationSerializer(NANOSECONDS, "DurationInWholeNanoseconds") +/** A [Duration] that is [serializable][Serializable] with [DurationInWholeNanosecondsSerializer]. */ +public typealias DurationInWholeNanoseconds = @Serializable(with = DurationInWholeNanosecondsSerializer::class) Duration + + +// microseconds + /** Serializer that encodes and decodes [Duration]s in [whole microseconds][Duration.inWholeMicroseconds]. */ public object DurationInWholeMicrosecondsSerializer : DurationSerializer(MICROSECONDS, "DurationInWholeMicroseconds") +/** A [Duration] that is [serializable][Serializable] with [DurationInWholeMicrosecondsSerializer]. */ +public typealias DurationInWholeMicroseconds = @Serializable(with = DurationInWholeMicrosecondsSerializer::class) Duration + + +// milliseconds + /** Serializer that encodes and decodes [Duration]s in [whole milliseconds][Duration.inWholeMilliseconds]. */ public object DurationInWholeMillisecondsSerializer : DurationSerializer(MILLISECONDS, "DurationInWholeMilliseconds") +/** A [Duration] that is [serializable][Serializable] with [DurationInWholeMillisecondsSerializer]. */ +public typealias DurationInWholeMilliseconds = @Serializable(with = DurationInWholeMillisecondsSerializer::class) Duration + + +// seconds + /** Serializer that encodes and decodes [Duration]s in [whole seconds][Duration.inWholeSeconds]. */ public object DurationInWholeSecondsSerializer : DurationSerializer(SECONDS, "DurationInWholeSeconds") +/** A [Duration] that is [serializable][Serializable] with [DurationInWholeSecondsSerializer]. */ +public typealias DurationInWholeSeconds = @Serializable(with = DurationInWholeSecondsSerializer::class) Duration + + +// minutes + /** Serializer that encodes and decodes [Duration]s in [whole minutes][Duration.inWholeMinutes]. */ public object DurationInWholeMinutesSerializer : DurationSerializer(MINUTES, "DurationInWholeMinutes") +/** A [Duration] that is [serializable][Serializable] with [DurationInWholeMinutesSerializer]. */ +public typealias DurationInWholeMinutes = @Serializable(with = DurationInWholeMinutesSerializer::class) Duration + + +// hours + /** Serializer that encodes and decodes [Duration]s in [whole hours][Duration.inWholeHours]. */ public object DurationInWholeHoursSerializer : DurationSerializer(HOURS, "DurationInWholeHours") +/** A [Duration] that is [serializable][Serializable] with [DurationInWholeHoursSerializer]. */ +public typealias DurationInWholeHours = @Serializable(with = DurationInWholeHoursSerializer::class) Duration + + +// days + /** Serializer that encodes and decodes [Duration]s in [whole days][Duration.inWholeDays]. */ public object DurationInWholeDaysSerializer : DurationSerializer(DAYS, "DurationInWholeDays") + +/** A [Duration] that is [serializable][Serializable] with [DurationInWholeDaysSerializer]. */ +public typealias DurationInWholeDays = @Serializable(with = DurationInWholeDaysSerializer::class) Duration From 5c77c9ae41f41fb6be5d3b6db3244f41e65faa43 Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Mon, 4 Apr 2022 04:54:57 +0200 Subject: [PATCH 11/14] use typealiases --- common/src/main/kotlin/entity/DiscordChannel.kt | 4 ++-- common/src/main/kotlin/entity/DiscordGuild.kt | 7 ++----- common/src/main/kotlin/entity/DiscordIntegration.kt | 6 ++---- common/src/main/kotlin/entity/DiscordInvite.kt | 6 ++---- core/src/main/kotlin/cache/data/ChannelData.kt | 5 ++--- core/src/main/kotlin/cache/data/GuildData.kt | 6 ++---- core/src/main/kotlin/cache/data/IntegrationData.kt | 6 ++---- core/src/main/kotlin/cache/data/InviteCreateData.kt | 6 ++---- core/src/main/kotlin/cache/data/InviteData.kt | 6 ++---- gateway/src/main/kotlin/Event.kt | 11 ++++++----- rest/src/main/kotlin/json/request/ChannelRequests.kt | 5 ++--- rest/src/main/kotlin/json/request/GuildRequests.kt | 9 ++++----- .../main/kotlin/json/request/InviteCreateRequest.kt | 5 ++--- rest/src/main/kotlin/json/response/Gateway.kt | 6 ++---- 14 files changed, 34 insertions(+), 54 deletions(-) diff --git a/common/src/main/kotlin/entity/DiscordChannel.kt b/common/src/main/kotlin/entity/DiscordChannel.kt index 85712a5caf44..7d2b25b46763 100644 --- a/common/src/main/kotlin/entity/DiscordChannel.kt +++ b/common/src/main/kotlin/entity/DiscordChannel.kt @@ -5,6 +5,7 @@ import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.serialization.DurationInWholeMinutesSerializer +import dev.kord.common.serialization.DurationInWholeSeconds import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -17,7 +18,6 @@ import kotlinx.serialization.encoding.Encoder import kotlin.DeprecationLevel.WARNING import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes -import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds /** * A representation of a [Discord Channel Structure](https://discord.com/developers/docs/resources/channel). @@ -60,7 +60,7 @@ public data class DiscordChannel( @SerialName("user_limit") val userLimit: OptionalInt = OptionalInt.Missing, @SerialName("rate_limit_per_user") - val rateLimitPerUser: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), + val rateLimitPerUser: Optional = Optional.Missing(), val recipients: Optional> = Optional.Missing(), val icon: Optional = Optional.Missing(), @SerialName("owner_id") diff --git a/common/src/main/kotlin/entity/DiscordGuild.kt b/common/src/main/kotlin/entity/DiscordGuild.kt index 7d8ffa066437..a53a9632ccf7 100644 --- a/common/src/main/kotlin/entity/DiscordGuild.kt +++ b/common/src/main/kotlin/entity/DiscordGuild.kt @@ -4,7 +4,7 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake -import dev.kord.common.serialization.DurationInWholeSecondsSerializer +import dev.kord.common.serialization.DurationInWholeSeconds import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -14,7 +14,6 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import kotlin.time.Duration /** * A partial representation of a [DiscordGuild] that may be [unavailable]. @@ -95,9 +94,7 @@ public data class DiscordGuild( ReplaceWith("DiscordChannel#rtcRegion") ) val region: String, @SerialName("afk_channel_id") val afkChannelId: Snowflake?, - @SerialName("afk_timeout") - @Serializable(with = DurationInWholeSecondsSerializer::class) - val afkTimeout: Duration, + @SerialName("afk_timeout") val afkTimeout: DurationInWholeSeconds, @SerialName("widget_enabled") val widgetEnabled: OptionalBoolean = OptionalBoolean.Missing, @SerialName("widget_channel_id") val widgetChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, @SerialName("verification_level") val verificationLevel: VerificationLevel, diff --git a/common/src/main/kotlin/entity/DiscordIntegration.kt b/common/src/main/kotlin/entity/DiscordIntegration.kt index d2a4268ec03e..560e78bd2e30 100644 --- a/common/src/main/kotlin/entity/DiscordIntegration.kt +++ b/common/src/main/kotlin/entity/DiscordIntegration.kt @@ -2,7 +2,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean -import dev.kord.common.serialization.DurationInWholeDaysSerializer +import dev.kord.common.serialization.DurationInWholeDays import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -11,7 +11,6 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import kotlin.time.Duration @Serializable public data class DiscordIntegration( @@ -27,8 +26,7 @@ public data class DiscordIntegration( @SerialName("expire_behavior") val expireBehavior: IntegrationExpireBehavior, @SerialName("expire_grace_period") - @Serializable(with = DurationInWholeDaysSerializer::class) - val expireGracePeriod: Duration, + val expireGracePeriod: DurationInWholeDays, val user: DiscordUser, val account: DiscordIntegrationsAccount, @SerialName("synced_at") diff --git a/common/src/main/kotlin/entity/DiscordInvite.kt b/common/src/main/kotlin/entity/DiscordInvite.kt index 2e2e11f27dfc..a1628a8fbf5e 100644 --- a/common/src/main/kotlin/entity/DiscordInvite.kt +++ b/common/src/main/kotlin/entity/DiscordInvite.kt @@ -2,7 +2,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalInt -import dev.kord.common.serialization.DurationInWholeSecondsSerializer +import dev.kord.common.serialization.DurationInWholeSeconds import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -12,7 +12,6 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import kotlin.time.Duration public sealed interface BaseDiscordInvite { public val code: String @@ -80,8 +79,7 @@ public data class DiscordInviteWithMetadata( @SerialName("max_uses") val maxUses: Int, @SerialName("max_age") - @Serializable(with = DurationInWholeSecondsSerializer::class) - val maxAge: Duration, + val maxAge: DurationInWholeSeconds, val temporary: Boolean, @SerialName("created_at") val createdAt: Instant, diff --git a/core/src/main/kotlin/cache/data/ChannelData.kt b/core/src/main/kotlin/cache/data/ChannelData.kt index b49b0a0a54c8..66081f3fbcb7 100644 --- a/core/src/main/kotlin/cache/data/ChannelData.kt +++ b/core/src/main/kotlin/cache/data/ChannelData.kt @@ -4,10 +4,9 @@ import dev.kord.cache.api.data.DataDescription import dev.kord.cache.api.data.description import dev.kord.common.entity.* import dev.kord.common.entity.optional.* +import dev.kord.common.serialization.DurationInWholeSeconds import kotlinx.datetime.Instant import kotlinx.serialization.Serializable -import kotlin.time.Duration -import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds @Serializable public data class ChannelData( @@ -22,7 +21,7 @@ public data class ChannelData( val lastMessageId: OptionalSnowflake? = OptionalSnowflake.Missing, val bitrate: OptionalInt = OptionalInt.Missing, val userLimit: OptionalInt = OptionalInt.Missing, - val rateLimitPerUser: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), + val rateLimitPerUser: Optional = Optional.Missing(), val recipients: Optional> = Optional.Missing(), val icon: Optional = Optional.Missing(), val ownerId: OptionalSnowflake = OptionalSnowflake.Missing, diff --git a/core/src/main/kotlin/cache/data/GuildData.kt b/core/src/main/kotlin/cache/data/GuildData.kt index 09bce0c13bdf..e826c8e5dd34 100644 --- a/core/src/main/kotlin/cache/data/GuildData.kt +++ b/core/src/main/kotlin/cache/data/GuildData.kt @@ -4,9 +4,8 @@ import dev.kord.cache.api.data.DataDescription import dev.kord.cache.api.data.description import dev.kord.common.entity.* import dev.kord.common.entity.optional.* -import dev.kord.common.serialization.DurationInWholeSecondsSerializer +import dev.kord.common.serialization.DurationInWholeSeconds import kotlinx.serialization.Serializable -import kotlin.time.Duration private val MessageData.nullableGuildId get() = guildId.value private val ChannelData.nullableGuildId get() = guildId.value @@ -26,8 +25,7 @@ public data class GuildData( @Deprecated("The region field has been moved to Channel#rtcRegion in Discord API v9", ReplaceWith("ChannelData#rtcRegion")) val region: String, val afkChannelId: Snowflake? = null, - @Serializable(with = DurationInWholeSecondsSerializer::class) - val afkTimeout: Duration, + val afkTimeout: DurationInWholeSeconds, val widgetEnabled: OptionalBoolean = OptionalBoolean.Missing, val widgetChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, val verificationLevel: VerificationLevel, diff --git a/core/src/main/kotlin/cache/data/IntegrationData.kt b/core/src/main/kotlin/cache/data/IntegrationData.kt index 523a2de9729e..b4f20c4cf078 100644 --- a/core/src/main/kotlin/cache/data/IntegrationData.kt +++ b/core/src/main/kotlin/cache/data/IntegrationData.kt @@ -2,9 +2,8 @@ package dev.kord.core.cache.data import dev.kord.common.entity.* import dev.kord.common.entity.optional.OptionalBoolean -import dev.kord.common.serialization.DurationInWholeDaysSerializer +import dev.kord.common.serialization.DurationInWholeDays import kotlinx.serialization.Serializable -import kotlin.time.Duration @Serializable public data class IntegrationData( @@ -17,8 +16,7 @@ public data class IntegrationData( val roleId: Snowflake, val enableEmoticons: OptionalBoolean = OptionalBoolean.Missing, val expireBehavior: IntegrationExpireBehavior, - @Serializable(with = DurationInWholeDaysSerializer::class) - val expireGracePeriod: Duration, + val expireGracePeriod: DurationInWholeDays, val user: DiscordUser, val account: IntegrationsAccountData, val syncedAt: String, diff --git a/core/src/main/kotlin/cache/data/InviteCreateData.kt b/core/src/main/kotlin/cache/data/InviteCreateData.kt index b0b21f66c27b..fd7ddbf71d6b 100644 --- a/core/src/main/kotlin/cache/data/InviteCreateData.kt +++ b/core/src/main/kotlin/cache/data/InviteCreateData.kt @@ -6,10 +6,9 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.map import dev.kord.common.entity.optional.mapSnowflake -import dev.kord.common.serialization.DurationInWholeSecondsSerializer +import dev.kord.common.serialization.DurationInWholeSeconds import dev.kord.gateway.DiscordCreatedInvite import kotlinx.serialization.Serializable -import kotlin.time.Duration @Serializable public data class InviteCreateData( @@ -18,8 +17,7 @@ public data class InviteCreateData( val createdAt: String, val guildId: OptionalSnowflake = OptionalSnowflake.Missing, val inviterId: OptionalSnowflake = OptionalSnowflake.Missing, - @Serializable(with = DurationInWholeSecondsSerializer::class) - val maxAge: Duration, + val maxAge: DurationInWholeSeconds, val maxUses: Int, val targetType: Optional = Optional.Missing(), val targetUserId: OptionalSnowflake = OptionalSnowflake.Missing, diff --git a/core/src/main/kotlin/cache/data/InviteData.kt b/core/src/main/kotlin/cache/data/InviteData.kt index dc3e3e43a538..b8f9b8d5157f 100644 --- a/core/src/main/kotlin/cache/data/InviteData.kt +++ b/core/src/main/kotlin/cache/data/InviteData.kt @@ -2,10 +2,9 @@ package dev.kord.core.cache.data import dev.kord.common.entity.* import dev.kord.common.entity.optional.* -import dev.kord.common.serialization.DurationInWholeSecondsSerializer +import dev.kord.common.serialization.DurationInWholeSeconds import kotlinx.datetime.Instant import kotlinx.serialization.Serializable -import kotlin.time.Duration public sealed interface BaseInviteData { public val code: String @@ -75,8 +74,7 @@ public data class InviteWithMetadataData( override val guildScheduledEvent: Optional = Optional.Missing(), val uses: Int, val maxUses: Int, - @Serializable(with = DurationInWholeSecondsSerializer::class) - val maxAge: Duration, + val maxAge: DurationInWholeSeconds, val temporary: Boolean, val createdAt: Instant, ) : BaseInviteData { diff --git a/gateway/src/main/kotlin/Event.kt b/gateway/src/main/kotlin/Event.kt index 35850ab784a0..6adb977d3c74 100644 --- a/gateway/src/main/kotlin/Event.kt +++ b/gateway/src/main/kotlin/Event.kt @@ -4,8 +4,11 @@ import dev.kord.common.entity.* import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalSnowflake -import dev.kord.common.serialization.DurationInWholeSecondsSerializer -import kotlinx.serialization.* +import dev.kord.common.serialization.DurationInWholeSeconds +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.PrimitiveKind @@ -18,7 +21,6 @@ import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import mu.KotlinLogging -import kotlin.time.Duration import kotlinx.serialization.DeserializationStrategy as KDeserializationStrategy private val jsonLogger = KotlinLogging.logger { } @@ -628,8 +630,7 @@ public data class DiscordCreatedInvite( val guildId: OptionalSnowflake = OptionalSnowflake.Missing, val inviter: Optional = Optional.Missing(), @SerialName("max_age") - @Serializable(with = DurationInWholeSecondsSerializer::class) - val maxAge: Duration, + val maxAge: DurationInWholeSeconds, @SerialName("max_uses") val maxUses: Int, @SerialName("target_type") diff --git a/rest/src/main/kotlin/json/request/ChannelRequests.kt b/rest/src/main/kotlin/json/request/ChannelRequests.kt index 84fa42d1c0e9..a29c4fdbebb8 100644 --- a/rest/src/main/kotlin/json/request/ChannelRequests.kt +++ b/rest/src/main/kotlin/json/request/ChannelRequests.kt @@ -5,11 +5,10 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake +import dev.kord.common.serialization.DurationInWholeSeconds import kotlinx.datetime.Instant import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlin.time.Duration -import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds @Serializable public data class ChannelModifyPutRequest( @@ -35,7 +34,7 @@ public data class ChannelModifyPatchRequest( val topic: Optional = Optional.Missing(), val nsfw: OptionalBoolean? = OptionalBoolean.Missing, @SerialName("rate_limit_per_user") - val rateLimitPerUser: Optional<@Serializable(InWholeSeconds::class) Duration?> = Optional.Missing(), + val rateLimitPerUser: Optional = Optional.Missing(), val bitrate: OptionalInt? = OptionalInt.Missing, @SerialName("user_limit") val userLimit: OptionalInt? = OptionalInt.Missing, diff --git a/rest/src/main/kotlin/json/request/GuildRequests.kt b/rest/src/main/kotlin/json/request/GuildRequests.kt index 81f810e180f2..76e2e139e4de 100644 --- a/rest/src/main/kotlin/json/request/GuildRequests.kt +++ b/rest/src/main/kotlin/json/request/GuildRequests.kt @@ -8,6 +8,7 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake +import dev.kord.common.serialization.DurationInWholeSeconds import kotlinx.datetime.Instant import kotlinx.serialization.* import kotlinx.serialization.builtins.ListSerializer @@ -15,8 +16,6 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.listSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import kotlin.time.Duration -import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds @Serializable public data class GuildCreateRequest( @@ -33,7 +32,7 @@ public data class GuildCreateRequest( @SerialName("afk_channel_id") val afkChannelId: OptionalSnowflake = OptionalSnowflake.Missing, @SerialName("afk_timeout") - val afkTimeout: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), + val afkTimeout: Optional = Optional.Missing(), @SerialName("system_channel_id") val systemChannelId: OptionalSnowflake = OptionalSnowflake.Missing ) @@ -47,7 +46,7 @@ public data class GuildChannelCreateRequest( @SerialName("user_limit") val userLimit: OptionalInt = OptionalInt.Missing, @SerialName("rate_limit_per_user") - val rateLimitPerUser: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), + val rateLimitPerUser: Optional = Optional.Missing(), val position: OptionalInt = OptionalInt.Missing, @SerialName("permission_overwrites") val permissionOverwrite: Optional> = Optional.Missing(), @@ -230,7 +229,7 @@ public data class GuildModifyRequest( @SerialName("afk_channel_id") val afkChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, @SerialName("afk_timeout") - val afkTimeout: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), + val afkTimeout: Optional = Optional.Missing(), val icon: Optional = Optional.Missing(), @SerialName("owner_id") val ownerId: OptionalSnowflake = OptionalSnowflake.Missing, diff --git a/rest/src/main/kotlin/json/request/InviteCreateRequest.kt b/rest/src/main/kotlin/json/request/InviteCreateRequest.kt index ca769f62e3a6..0a801ef608f2 100644 --- a/rest/src/main/kotlin/json/request/InviteCreateRequest.kt +++ b/rest/src/main/kotlin/json/request/InviteCreateRequest.kt @@ -2,15 +2,14 @@ package dev.kord.rest.json.request import dev.kord.common.entity.InviteTargetType import dev.kord.common.entity.optional.* +import dev.kord.common.serialization.DurationInWholeSeconds import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlin.time.Duration -import dev.kord.common.serialization.DurationInWholeSecondsSerializer as InWholeSeconds @Serializable public data class InviteCreateRequest( @SerialName("max_age") - val maxAge: Optional<@Serializable(InWholeSeconds::class) Duration> = Optional.Missing(), + val maxAge: Optional = Optional.Missing(), @SerialName("max_uses") val maxUses: OptionalInt = OptionalInt.Missing, val temporary: OptionalBoolean = OptionalBoolean.Missing, diff --git a/rest/src/main/kotlin/json/response/Gateway.kt b/rest/src/main/kotlin/json/response/Gateway.kt index 0fec497c9eec..10cc89cfb0ed 100644 --- a/rest/src/main/kotlin/json/response/Gateway.kt +++ b/rest/src/main/kotlin/json/response/Gateway.kt @@ -1,9 +1,8 @@ package dev.kord.rest.json.response -import dev.kord.common.serialization.DurationInWholeMillisecondsSerializer +import dev.kord.common.serialization.DurationInWholeMilliseconds import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlin.time.Duration @Serializable public data class GatewayResponse(val url: String) @@ -21,8 +20,7 @@ public data class SessionStartLimitResponse( val total: Int, val remaining: Int, @SerialName("reset_after") - @Serializable(with = DurationInWholeMillisecondsSerializer::class) - val resetAfter: Duration, + val resetAfter: DurationInWholeMilliseconds, @SerialName("max_concurrency") val maxConcurrency: Int ) From e11c0fbf1c7fe88e70c0e1e6d4e23e1c61246602 Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Mon, 4 Apr 2022 21:19:12 +0200 Subject: [PATCH 12/14] remove "Whole" --- common/src/main/kotlin/entity/AuditLog.kt | 13 +++--- .../src/main/kotlin/entity/DiscordChannel.kt | 12 ++--- common/src/main/kotlin/entity/DiscordGuild.kt | 4 +- .../main/kotlin/entity/DiscordIntegration.kt | 4 +- .../src/main/kotlin/entity/DiscordInvite.kt | 4 +- .../serialization/DurationSerializers.kt | 46 +++++++++---------- .../serialization/DurationSerializersTests.kt | 28 +++++------ .../src/main/kotlin/cache/data/ChannelData.kt | 4 +- core/src/main/kotlin/cache/data/GuildData.kt | 4 +- .../main/kotlin/cache/data/IntegrationData.kt | 4 +- .../kotlin/cache/data/InviteCreateData.kt | 4 +- core/src/main/kotlin/cache/data/InviteData.kt | 4 +- gateway/src/main/kotlin/Event.kt | 4 +- .../kotlin/json/request/ChannelRequests.kt | 4 +- .../main/kotlin/json/request/GuildRequests.kt | 8 ++-- .../json/request/InviteCreateRequest.kt | 4 +- rest/src/main/kotlin/json/response/Gateway.kt | 4 +- 17 files changed, 77 insertions(+), 78 deletions(-) diff --git a/common/src/main/kotlin/entity/AuditLog.kt b/common/src/main/kotlin/entity/AuditLog.kt index d953c61d4637..3917efc5639e 100644 --- a/common/src/main/kotlin/entity/AuditLog.kt +++ b/common/src/main/kotlin/entity/AuditLog.kt @@ -3,8 +3,8 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.orEmpty -import dev.kord.common.serialization.DurationInWholeDaysSerializer -import dev.kord.common.serialization.DurationInWholeSecondsSerializer +import dev.kord.common.serialization.DurationInDaysSerializer +import dev.kord.common.serialization.DurationInSecondsSerializer import kotlinx.datetime.Instant import kotlinx.serialization.* import kotlinx.serialization.builtins.serializer @@ -184,7 +184,7 @@ public sealed class AuditLogChangeKey(public val name: String, public val ser public object AfkChannelId : AuditLogChangeKey("afk_channel_id", serializer()) @SerialName("afk_timeout") - public object AfkTimeout : AuditLogChangeKey("afk_timeout", DurationInWholeSecondsSerializer) + public object AfkTimeout : AuditLogChangeKey("afk_timeout", DurationInSecondsSerializer) @SerialName("mfa_level") public object MFALevel : AuditLogChangeKey("mfa_level", serializer()) @@ -240,8 +240,7 @@ public sealed class AuditLogChangeKey(public val name: String, public val ser public object ApplicationId : AuditLogChangeKey("application_id", serializer()) @SerialName("rate_limit_per_user") - public object RateLimitPerUser : - AuditLogChangeKey("rate_limit_per_user", DurationInWholeSecondsSerializer) + public object RateLimitPerUser : AuditLogChangeKey("rate_limit_per_user", DurationInSecondsSerializer) @SerialName("permissions") public object Permissions : AuditLogChangeKey("permissions", serializer()) @@ -283,7 +282,7 @@ public sealed class AuditLogChangeKey(public val name: String, public val ser public object Uses : AuditLogChangeKey("uses", serializer()) @SerialName("max_age") - public object MaxAges : AuditLogChangeKey("max_age", DurationInWholeSecondsSerializer) + public object MaxAges : AuditLogChangeKey("max_age", DurationInSecondsSerializer) @SerialName("temporary") public object Temporary : AuditLogChangeKey("temporary", serializer()) @@ -349,7 +348,7 @@ public sealed class AuditLogChangeKey(public val name: String, public val ser public object ExpireBehavior : AuditLogChangeKey("expire_behavior", serializer()) @SerialName("expire_grace_period") - public object ExpireGracePeriod : AuditLogChangeKey("expire_grace_period", DurationInWholeDaysSerializer) + public object ExpireGracePeriod : AuditLogChangeKey("expire_grace_period", DurationInDaysSerializer) @SerialName("user_limit") public object UserLimit : AuditLogChangeKey("user_limit", serializer()) diff --git a/common/src/main/kotlin/entity/DiscordChannel.kt b/common/src/main/kotlin/entity/DiscordChannel.kt index 7d2b25b46763..a9aeb3da84fc 100644 --- a/common/src/main/kotlin/entity/DiscordChannel.kt +++ b/common/src/main/kotlin/entity/DiscordChannel.kt @@ -4,8 +4,8 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake -import dev.kord.common.serialization.DurationInWholeMinutesSerializer -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInMinutesSerializer +import dev.kord.common.serialization.DurationInSeconds import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -60,7 +60,7 @@ public data class DiscordChannel( @SerialName("user_limit") val userLimit: OptionalInt = OptionalInt.Missing, @SerialName("rate_limit_per_user") - val rateLimitPerUser: Optional = Optional.Missing(), + val rateLimitPerUser: Optional = Optional.Missing(), val recipients: Optional> = Optional.Missing(), val icon: Optional = Optional.Missing(), @SerialName("owner_id") @@ -207,15 +207,15 @@ public sealed class ArchiveDuration(public val duration: Duration) { public object Serializer : KSerializer { - override val descriptor: SerialDescriptor get() = DurationInWholeMinutesSerializer.descriptor + override val descriptor: SerialDescriptor get() = DurationInMinutesSerializer.descriptor override fun deserialize(decoder: Decoder): ArchiveDuration { - val value = decoder.decodeSerializableValue(DurationInWholeMinutesSerializer) + val value = decoder.decodeSerializableValue(DurationInMinutesSerializer) return values.firstOrNull { it.duration == value } ?: Unknown(value) } override fun serialize(encoder: Encoder, value: ArchiveDuration) { - encoder.encodeSerializableValue(DurationInWholeMinutesSerializer, value.duration) + encoder.encodeSerializableValue(DurationInMinutesSerializer, value.duration) } } diff --git a/common/src/main/kotlin/entity/DiscordGuild.kt b/common/src/main/kotlin/entity/DiscordGuild.kt index a53a9632ccf7..8c597897549d 100644 --- a/common/src/main/kotlin/entity/DiscordGuild.kt +++ b/common/src/main/kotlin/entity/DiscordGuild.kt @@ -4,7 +4,7 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -94,7 +94,7 @@ public data class DiscordGuild( ReplaceWith("DiscordChannel#rtcRegion") ) val region: String, @SerialName("afk_channel_id") val afkChannelId: Snowflake?, - @SerialName("afk_timeout") val afkTimeout: DurationInWholeSeconds, + @SerialName("afk_timeout") val afkTimeout: DurationInSeconds, @SerialName("widget_enabled") val widgetEnabled: OptionalBoolean = OptionalBoolean.Missing, @SerialName("widget_channel_id") val widgetChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, @SerialName("verification_level") val verificationLevel: VerificationLevel, diff --git a/common/src/main/kotlin/entity/DiscordIntegration.kt b/common/src/main/kotlin/entity/DiscordIntegration.kt index 560e78bd2e30..0e76eb5611a0 100644 --- a/common/src/main/kotlin/entity/DiscordIntegration.kt +++ b/common/src/main/kotlin/entity/DiscordIntegration.kt @@ -2,7 +2,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean -import dev.kord.common.serialization.DurationInWholeDays +import dev.kord.common.serialization.DurationInDays import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -26,7 +26,7 @@ public data class DiscordIntegration( @SerialName("expire_behavior") val expireBehavior: IntegrationExpireBehavior, @SerialName("expire_grace_period") - val expireGracePeriod: DurationInWholeDays, + val expireGracePeriod: DurationInDays, val user: DiscordUser, val account: DiscordIntegrationsAccount, @SerialName("synced_at") diff --git a/common/src/main/kotlin/entity/DiscordInvite.kt b/common/src/main/kotlin/entity/DiscordInvite.kt index a1628a8fbf5e..9aca9e033811 100644 --- a/common/src/main/kotlin/entity/DiscordInvite.kt +++ b/common/src/main/kotlin/entity/DiscordInvite.kt @@ -2,7 +2,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalInt -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -79,7 +79,7 @@ public data class DiscordInviteWithMetadata( @SerialName("max_uses") val maxUses: Int, @SerialName("max_age") - val maxAge: DurationInWholeSeconds, + val maxAge: DurationInSeconds, val temporary: Boolean, @SerialName("created_at") val createdAt: Instant, diff --git a/common/src/main/kotlin/serialization/DurationSerializers.kt b/common/src/main/kotlin/serialization/DurationSerializers.kt index 2277573af03c..946b8ff0935a 100644 --- a/common/src/main/kotlin/serialization/DurationSerializers.kt +++ b/common/src/main/kotlin/serialization/DurationSerializers.kt @@ -13,8 +13,8 @@ import kotlin.time.DurationUnit.* import kotlin.time.toDuration -/** Serializer that encodes and decodes [Duration]s. */ -public sealed class DurationSerializer(private val unit: DurationUnit, name: String) : KSerializer { +/** Serializer that encodes and decodes [Duration]s as a [Long] number of the specified [unit]. */ +public sealed class DurationAsLongSerializer(public val unit: DurationUnit, name: String) : KSerializer { final override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("dev.kord.common.serialization.$name", PrimitiveKind.LONG) @@ -32,61 +32,61 @@ public sealed class DurationSerializer(private val unit: DurationUnit, name: Str // nanoseconds /** Serializer that encodes and decodes [Duration]s in [whole nanoseconds][Duration.inWholeNanoseconds]. */ -public object DurationInWholeNanosecondsSerializer : DurationSerializer(NANOSECONDS, "DurationInWholeNanoseconds") +public object DurationInNanosecondsSerializer : DurationAsLongSerializer(NANOSECONDS, "DurationInNanoseconds") -/** A [Duration] that is [serializable][Serializable] with [DurationInWholeNanosecondsSerializer]. */ -public typealias DurationInWholeNanoseconds = @Serializable(with = DurationInWholeNanosecondsSerializer::class) Duration +/** A [Duration] that is [serializable][Serializable] with [DurationInNanosecondsSerializer]. */ +public typealias DurationInNanoseconds = @Serializable(with = DurationInNanosecondsSerializer::class) Duration // microseconds /** Serializer that encodes and decodes [Duration]s in [whole microseconds][Duration.inWholeMicroseconds]. */ -public object DurationInWholeMicrosecondsSerializer : DurationSerializer(MICROSECONDS, "DurationInWholeMicroseconds") +public object DurationInMicrosecondsSerializer : DurationAsLongSerializer(MICROSECONDS, "DurationInMicroseconds") -/** A [Duration] that is [serializable][Serializable] with [DurationInWholeMicrosecondsSerializer]. */ -public typealias DurationInWholeMicroseconds = @Serializable(with = DurationInWholeMicrosecondsSerializer::class) Duration +/** A [Duration] that is [serializable][Serializable] with [DurationInMicrosecondsSerializer]. */ +public typealias DurationInMicroseconds = @Serializable(with = DurationInMicrosecondsSerializer::class) Duration // milliseconds /** Serializer that encodes and decodes [Duration]s in [whole milliseconds][Duration.inWholeMilliseconds]. */ -public object DurationInWholeMillisecondsSerializer : DurationSerializer(MILLISECONDS, "DurationInWholeMilliseconds") +public object DurationInMillisecondsSerializer : DurationAsLongSerializer(MILLISECONDS, "DurationInMilliseconds") -/** A [Duration] that is [serializable][Serializable] with [DurationInWholeMillisecondsSerializer]. */ -public typealias DurationInWholeMilliseconds = @Serializable(with = DurationInWholeMillisecondsSerializer::class) Duration +/** A [Duration] that is [serializable][Serializable] with [DurationInMillisecondsSerializer]. */ +public typealias DurationInMilliseconds = @Serializable(with = DurationInMillisecondsSerializer::class) Duration // seconds /** Serializer that encodes and decodes [Duration]s in [whole seconds][Duration.inWholeSeconds]. */ -public object DurationInWholeSecondsSerializer : DurationSerializer(SECONDS, "DurationInWholeSeconds") +public object DurationInSecondsSerializer : DurationAsLongSerializer(SECONDS, "DurationInSeconds") -/** A [Duration] that is [serializable][Serializable] with [DurationInWholeSecondsSerializer]. */ -public typealias DurationInWholeSeconds = @Serializable(with = DurationInWholeSecondsSerializer::class) Duration +/** A [Duration] that is [serializable][Serializable] with [DurationInSecondsSerializer]. */ +public typealias DurationInSeconds = @Serializable(with = DurationInSecondsSerializer::class) Duration // minutes /** Serializer that encodes and decodes [Duration]s in [whole minutes][Duration.inWholeMinutes]. */ -public object DurationInWholeMinutesSerializer : DurationSerializer(MINUTES, "DurationInWholeMinutes") +public object DurationInMinutesSerializer : DurationAsLongSerializer(MINUTES, "DurationInMinutes") -/** A [Duration] that is [serializable][Serializable] with [DurationInWholeMinutesSerializer]. */ -public typealias DurationInWholeMinutes = @Serializable(with = DurationInWholeMinutesSerializer::class) Duration +/** A [Duration] that is [serializable][Serializable] with [DurationInMinutesSerializer]. */ +public typealias DurationInMinutes = @Serializable(with = DurationInMinutesSerializer::class) Duration // hours /** Serializer that encodes and decodes [Duration]s in [whole hours][Duration.inWholeHours]. */ -public object DurationInWholeHoursSerializer : DurationSerializer(HOURS, "DurationInWholeHours") +public object DurationInHoursSerializer : DurationAsLongSerializer(HOURS, "DurationInHours") -/** A [Duration] that is [serializable][Serializable] with [DurationInWholeHoursSerializer]. */ -public typealias DurationInWholeHours = @Serializable(with = DurationInWholeHoursSerializer::class) Duration +/** A [Duration] that is [serializable][Serializable] with [DurationInHoursSerializer]. */ +public typealias DurationInHours = @Serializable(with = DurationInHoursSerializer::class) Duration // days /** Serializer that encodes and decodes [Duration]s in [whole days][Duration.inWholeDays]. */ -public object DurationInWholeDaysSerializer : DurationSerializer(DAYS, "DurationInWholeDays") +public object DurationInDaysSerializer : DurationAsLongSerializer(DAYS, "DurationInDays") -/** A [Duration] that is [serializable][Serializable] with [DurationInWholeDaysSerializer]. */ -public typealias DurationInWholeDays = @Serializable(with = DurationInWholeDaysSerializer::class) Duration +/** A [Duration] that is [serializable][Serializable] with [DurationInDaysSerializer]. */ +public typealias DurationInDays = @Serializable(with = DurationInDaysSerializer::class) Duration diff --git a/common/src/test/kotlin/serialization/DurationSerializersTests.kt b/common/src/test/kotlin/serialization/DurationSerializersTests.kt index c509860ab08e..a107485f988b 100644 --- a/common/src/test/kotlin/serialization/DurationSerializersTests.kt +++ b/common/src/test/kotlin/serialization/DurationSerializersTests.kt @@ -78,51 +78,51 @@ abstract class DurationSerializerTest( } -class DurationInWholeNanosecondsSerializerTest : DurationSerializerTest( +class DurationInNanosecondsSerializerTest : DurationSerializerTest( json = "84169", duration = 84169.nanoseconds, durationToRound = 84169.48.nanoseconds, - serializer = DurationInWholeNanosecondsSerializer, + serializer = DurationInNanosecondsSerializer, ) -class DurationInWholeMicrosecondsSerializerTest : DurationSerializerTest( +class DurationInMicrosecondsSerializerTest : DurationSerializerTest( json = "25622456", duration = 25622456.microseconds, durationToRound = 25622456.4.microseconds, - serializer = DurationInWholeMicrosecondsSerializer, + serializer = DurationInMicrosecondsSerializer, ) -class DurationInWholeMillisecondsSerializerTest : DurationSerializerTest( +class DurationInMillisecondsSerializerTest : DurationSerializerTest( json = "3495189", duration = 3495189.milliseconds, durationToRound = 3495189.24.milliseconds, - serializer = DurationInWholeMillisecondsSerializer, + serializer = DurationInMillisecondsSerializer, ) -class DurationInWholeSecondsSerializerTest : DurationSerializerTest( +class DurationInSecondsSerializerTest : DurationSerializerTest( json = "987465", duration = 987465.seconds, durationToRound = 987465.489.seconds, - serializer = DurationInWholeSecondsSerializer, + serializer = DurationInSecondsSerializer, ) -class DurationInWholeMinutesSerializerTest : DurationSerializerTest( +class DurationInMinutesSerializerTest : DurationSerializerTest( json = "24905", duration = 24905.minutes, durationToRound = 24905.164.minutes, - serializer = DurationInWholeMinutesSerializer, + serializer = DurationInMinutesSerializer, ) -class DurationInWholeHoursSerializerTest : DurationSerializerTest( +class DurationInHoursSerializerTest : DurationSerializerTest( json = "7245", duration = 7245.hours, durationToRound = 7245.24.hours, - serializer = DurationInWholeHoursSerializer, + serializer = DurationInHoursSerializer, ) -class DurationInWholeDaysSerializerTest : DurationSerializerTest( +class DurationInDaysSerializerTest : DurationSerializerTest( json = "92", duration = 92.days, durationToRound = 92.12.days, - serializer = DurationInWholeDaysSerializer, + serializer = DurationInDaysSerializer, ) diff --git a/core/src/main/kotlin/cache/data/ChannelData.kt b/core/src/main/kotlin/cache/data/ChannelData.kt index 66081f3fbcb7..b24971ddd6a2 100644 --- a/core/src/main/kotlin/cache/data/ChannelData.kt +++ b/core/src/main/kotlin/cache/data/ChannelData.kt @@ -4,7 +4,7 @@ import dev.kord.cache.api.data.DataDescription import dev.kord.cache.api.data.description import dev.kord.common.entity.* import dev.kord.common.entity.optional.* -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import kotlinx.datetime.Instant import kotlinx.serialization.Serializable @@ -21,7 +21,7 @@ public data class ChannelData( val lastMessageId: OptionalSnowflake? = OptionalSnowflake.Missing, val bitrate: OptionalInt = OptionalInt.Missing, val userLimit: OptionalInt = OptionalInt.Missing, - val rateLimitPerUser: Optional = Optional.Missing(), + val rateLimitPerUser: Optional = Optional.Missing(), val recipients: Optional> = Optional.Missing(), val icon: Optional = Optional.Missing(), val ownerId: OptionalSnowflake = OptionalSnowflake.Missing, diff --git a/core/src/main/kotlin/cache/data/GuildData.kt b/core/src/main/kotlin/cache/data/GuildData.kt index e826c8e5dd34..5637a01f1e97 100644 --- a/core/src/main/kotlin/cache/data/GuildData.kt +++ b/core/src/main/kotlin/cache/data/GuildData.kt @@ -4,7 +4,7 @@ import dev.kord.cache.api.data.DataDescription import dev.kord.cache.api.data.description import dev.kord.common.entity.* import dev.kord.common.entity.optional.* -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import kotlinx.serialization.Serializable private val MessageData.nullableGuildId get() = guildId.value @@ -25,7 +25,7 @@ public data class GuildData( @Deprecated("The region field has been moved to Channel#rtcRegion in Discord API v9", ReplaceWith("ChannelData#rtcRegion")) val region: String, val afkChannelId: Snowflake? = null, - val afkTimeout: DurationInWholeSeconds, + val afkTimeout: DurationInSeconds, val widgetEnabled: OptionalBoolean = OptionalBoolean.Missing, val widgetChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, val verificationLevel: VerificationLevel, diff --git a/core/src/main/kotlin/cache/data/IntegrationData.kt b/core/src/main/kotlin/cache/data/IntegrationData.kt index b4f20c4cf078..b7ecfa568c66 100644 --- a/core/src/main/kotlin/cache/data/IntegrationData.kt +++ b/core/src/main/kotlin/cache/data/IntegrationData.kt @@ -2,7 +2,7 @@ package dev.kord.core.cache.data import dev.kord.common.entity.* import dev.kord.common.entity.optional.OptionalBoolean -import dev.kord.common.serialization.DurationInWholeDays +import dev.kord.common.serialization.DurationInDays import kotlinx.serialization.Serializable @Serializable @@ -16,7 +16,7 @@ public data class IntegrationData( val roleId: Snowflake, val enableEmoticons: OptionalBoolean = OptionalBoolean.Missing, val expireBehavior: IntegrationExpireBehavior, - val expireGracePeriod: DurationInWholeDays, + val expireGracePeriod: DurationInDays, val user: DiscordUser, val account: IntegrationsAccountData, val syncedAt: String, diff --git a/core/src/main/kotlin/cache/data/InviteCreateData.kt b/core/src/main/kotlin/cache/data/InviteCreateData.kt index fd7ddbf71d6b..e607fdc8af62 100644 --- a/core/src/main/kotlin/cache/data/InviteCreateData.kt +++ b/core/src/main/kotlin/cache/data/InviteCreateData.kt @@ -6,7 +6,7 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.map import dev.kord.common.entity.optional.mapSnowflake -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import dev.kord.gateway.DiscordCreatedInvite import kotlinx.serialization.Serializable @@ -17,7 +17,7 @@ public data class InviteCreateData( val createdAt: String, val guildId: OptionalSnowflake = OptionalSnowflake.Missing, val inviterId: OptionalSnowflake = OptionalSnowflake.Missing, - val maxAge: DurationInWholeSeconds, + val maxAge: DurationInSeconds, val maxUses: Int, val targetType: Optional = Optional.Missing(), val targetUserId: OptionalSnowflake = OptionalSnowflake.Missing, diff --git a/core/src/main/kotlin/cache/data/InviteData.kt b/core/src/main/kotlin/cache/data/InviteData.kt index b8f9b8d5157f..e89d649b0b87 100644 --- a/core/src/main/kotlin/cache/data/InviteData.kt +++ b/core/src/main/kotlin/cache/data/InviteData.kt @@ -2,7 +2,7 @@ package dev.kord.core.cache.data import dev.kord.common.entity.* import dev.kord.common.entity.optional.* -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import kotlinx.datetime.Instant import kotlinx.serialization.Serializable @@ -74,7 +74,7 @@ public data class InviteWithMetadataData( override val guildScheduledEvent: Optional = Optional.Missing(), val uses: Int, val maxUses: Int, - val maxAge: DurationInWholeSeconds, + val maxAge: DurationInSeconds, val temporary: Boolean, val createdAt: Instant, ) : BaseInviteData { diff --git a/gateway/src/main/kotlin/Event.kt b/gateway/src/main/kotlin/Event.kt index 6adb977d3c74..44dc0ec302be 100644 --- a/gateway/src/main/kotlin/Event.kt +++ b/gateway/src/main/kotlin/Event.kt @@ -4,7 +4,7 @@ import dev.kord.common.entity.* import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalSnowflake -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -630,7 +630,7 @@ public data class DiscordCreatedInvite( val guildId: OptionalSnowflake = OptionalSnowflake.Missing, val inviter: Optional = Optional.Missing(), @SerialName("max_age") - val maxAge: DurationInWholeSeconds, + val maxAge: DurationInSeconds, @SerialName("max_uses") val maxUses: Int, @SerialName("target_type") diff --git a/rest/src/main/kotlin/json/request/ChannelRequests.kt b/rest/src/main/kotlin/json/request/ChannelRequests.kt index a29c4fdbebb8..5778bd4bb7f3 100644 --- a/rest/src/main/kotlin/json/request/ChannelRequests.kt +++ b/rest/src/main/kotlin/json/request/ChannelRequests.kt @@ -5,7 +5,7 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import kotlinx.datetime.Instant import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -34,7 +34,7 @@ public data class ChannelModifyPatchRequest( val topic: Optional = Optional.Missing(), val nsfw: OptionalBoolean? = OptionalBoolean.Missing, @SerialName("rate_limit_per_user") - val rateLimitPerUser: Optional = Optional.Missing(), + val rateLimitPerUser: Optional = Optional.Missing(), val bitrate: OptionalInt? = OptionalInt.Missing, @SerialName("user_limit") val userLimit: OptionalInt? = OptionalInt.Missing, diff --git a/rest/src/main/kotlin/json/request/GuildRequests.kt b/rest/src/main/kotlin/json/request/GuildRequests.kt index 76e2e139e4de..d4ae858f2622 100644 --- a/rest/src/main/kotlin/json/request/GuildRequests.kt +++ b/rest/src/main/kotlin/json/request/GuildRequests.kt @@ -8,7 +8,7 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import kotlinx.datetime.Instant import kotlinx.serialization.* import kotlinx.serialization.builtins.ListSerializer @@ -32,7 +32,7 @@ public data class GuildCreateRequest( @SerialName("afk_channel_id") val afkChannelId: OptionalSnowflake = OptionalSnowflake.Missing, @SerialName("afk_timeout") - val afkTimeout: Optional = Optional.Missing(), + val afkTimeout: Optional = Optional.Missing(), @SerialName("system_channel_id") val systemChannelId: OptionalSnowflake = OptionalSnowflake.Missing ) @@ -46,7 +46,7 @@ public data class GuildChannelCreateRequest( @SerialName("user_limit") val userLimit: OptionalInt = OptionalInt.Missing, @SerialName("rate_limit_per_user") - val rateLimitPerUser: Optional = Optional.Missing(), + val rateLimitPerUser: Optional = Optional.Missing(), val position: OptionalInt = OptionalInt.Missing, @SerialName("permission_overwrites") val permissionOverwrite: Optional> = Optional.Missing(), @@ -229,7 +229,7 @@ public data class GuildModifyRequest( @SerialName("afk_channel_id") val afkChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, @SerialName("afk_timeout") - val afkTimeout: Optional = Optional.Missing(), + val afkTimeout: Optional = Optional.Missing(), val icon: Optional = Optional.Missing(), @SerialName("owner_id") val ownerId: OptionalSnowflake = OptionalSnowflake.Missing, diff --git a/rest/src/main/kotlin/json/request/InviteCreateRequest.kt b/rest/src/main/kotlin/json/request/InviteCreateRequest.kt index 0a801ef608f2..cf9d622e1194 100644 --- a/rest/src/main/kotlin/json/request/InviteCreateRequest.kt +++ b/rest/src/main/kotlin/json/request/InviteCreateRequest.kt @@ -2,14 +2,14 @@ package dev.kord.rest.json.request import dev.kord.common.entity.InviteTargetType import dev.kord.common.entity.optional.* -import dev.kord.common.serialization.DurationInWholeSeconds +import dev.kord.common.serialization.DurationInSeconds import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable public data class InviteCreateRequest( @SerialName("max_age") - val maxAge: Optional = Optional.Missing(), + val maxAge: Optional = Optional.Missing(), @SerialName("max_uses") val maxUses: OptionalInt = OptionalInt.Missing, val temporary: OptionalBoolean = OptionalBoolean.Missing, diff --git a/rest/src/main/kotlin/json/response/Gateway.kt b/rest/src/main/kotlin/json/response/Gateway.kt index 10cc89cfb0ed..25af4e368440 100644 --- a/rest/src/main/kotlin/json/response/Gateway.kt +++ b/rest/src/main/kotlin/json/response/Gateway.kt @@ -1,6 +1,6 @@ package dev.kord.rest.json.response -import dev.kord.common.serialization.DurationInWholeMilliseconds +import dev.kord.common.serialization.DurationInMilliseconds import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -20,7 +20,7 @@ public data class SessionStartLimitResponse( val total: Int, val remaining: Int, @SerialName("reset_after") - val resetAfter: DurationInWholeMilliseconds, + val resetAfter: DurationInMilliseconds, @SerialName("max_concurrency") val maxConcurrency: Int ) From 60de4e675a51bcc553a865e2b33bfacf30ee73ff Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Tue, 5 Apr 2022 01:15:08 +0200 Subject: [PATCH 13/14] throw SerializationException on overflow or infinite durations --- .../serialization/DurationSerializers.kt | 21 ++++- .../serialization/DurationSerializersTests.kt | 87 +++++++++++++++---- 2 files changed, 88 insertions(+), 20 deletions(-) diff --git a/common/src/main/kotlin/serialization/DurationSerializers.kt b/common/src/main/kotlin/serialization/DurationSerializers.kt index 946b8ff0935a..ea6339c797e6 100644 --- a/common/src/main/kotlin/serialization/DurationSerializers.kt +++ b/common/src/main/kotlin/serialization/DurationSerializers.kt @@ -2,6 +2,7 @@ package dev.kord.common.serialization import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationException import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor @@ -14,13 +15,29 @@ import kotlin.time.toDuration /** Serializer that encodes and decodes [Duration]s as a [Long] number of the specified [unit]. */ -public sealed class DurationAsLongSerializer(public val unit: DurationUnit, name: String) : KSerializer { +public sealed class DurationAsLongSerializer( + public val unit: DurationUnit, + private val name: String, +) : KSerializer { final override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("dev.kord.common.serialization.$name", PrimitiveKind.LONG) final override fun serialize(encoder: Encoder, value: Duration) { - encoder.encodeLong(value.toLong(unit)) + when (val valueAsLong = value.toLong(unit)) { + + Long.MIN_VALUE, Long.MAX_VALUE -> throw SerializationException( + if (value.isInfinite()) { + "Infinite Durations cannot be serialized, got $value" + } else { + "The Duration $value expressed as a number of ${ + unit.name.lowercase() + } does not fit in the range of Long type and therefore cannot be serialized with ${name}Serializer" + } + ) + + else -> encoder.encodeLong(valueAsLong) + } } final override fun deserialize(decoder: Decoder): Duration { diff --git a/common/src/test/kotlin/serialization/DurationSerializersTests.kt b/common/src/test/kotlin/serialization/DurationSerializersTests.kt index a107485f988b..640ecf31e864 100644 --- a/common/src/test/kotlin/serialization/DurationSerializersTests.kt +++ b/common/src/test/kotlin/serialization/DurationSerializersTests.kt @@ -1,10 +1,11 @@ package serialization import dev.kord.common.serialization.* -import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFailsWith import kotlin.time.Duration import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.hours @@ -13,67 +14,106 @@ import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.Duration.Companion.seconds +import kotlin.time.DurationUnit.MILLISECONDS abstract class DurationSerializerTest( private val json: String, private val duration: Duration, private val durationToRound: Duration, - private val serializer: KSerializer, + private val durationThatWouldOverflowInTargetUnit: Duration? = null, + private val largeJson: String? = null, + private val serializer: DurationAsLongSerializer, ) { init { require(duration.isPositive()) require(durationToRound.isPositive()) + + if (serializer.unit < MILLISECONDS) { + require(durationThatWouldOverflowInTargetUnit != null) + require(largeJson == null) + } else { + require(durationThatWouldOverflowInTargetUnit == null) + require(largeJson != null) + } } + private fun serialize(duration: Duration) = Json.encodeToString(serializer, duration) + private fun deserialize(json: String) = Json.decodeFromString(serializer, json) + @Test fun `zero Duration can be serialized`() { - val serialized = Json.encodeToString(serializer, Duration.ZERO) - assertEquals(expected = "0", actual = serialized) + assertEquals(expected = "0", actual = serialize(Duration.ZERO)) } @Test fun `zero Duration can be deserialized`() { - val deserialized = Json.decodeFromString(serializer, "0") - assertEquals(expected = Duration.ZERO, actual = deserialized) + assertEquals(expected = Duration.ZERO, actual = deserialize("0")) + } + + + @Test + fun `infinite Durations cannot be serialized`() { + assertFailsWith { serialize(Duration.INFINITE) } + assertFailsWith { serialize(-Duration.INFINITE) } } @Test fun `positive Duration can be serialized`() { - val serialized = Json.encodeToString(serializer, duration) - assertEquals(expected = json, actual = serialized) + assertEquals(expected = json, actual = serialize(duration)) } @Test fun `positive Duration can be rounded and serialized`() { - val serialized = Json.encodeToString(serializer, durationToRound) - assertEquals(expected = json, actual = serialized) + assertEquals(expected = json, actual = serialize(durationToRound)) } @Test fun `positive Duration can be deserialized`() { - val deserialized = Json.decodeFromString(serializer, json) - assertEquals(expected = duration, actual = deserialized) + assertEquals(expected = duration, actual = deserialize(json)) } @Test fun `negative Duration can be serialized`() { - val serialized = Json.encodeToString(serializer, -duration) - assertEquals(expected = "-$json", actual = serialized) + assertEquals(expected = "-$json", actual = serialize(-duration)) } @Test fun `negative Duration can be rounded and serialized`() { - val serialized = Json.encodeToString(serializer, -durationToRound) - assertEquals(expected = "-$json", actual = serialized) + assertEquals(expected = "-$json", actual = serialize(-durationToRound)) } @Test fun `negative Duration can be deserialized`() { - val deserialized = Json.decodeFromString(serializer, "-$json") - assertEquals(expected = -duration, actual = deserialized) + assertEquals(expected = -duration, actual = deserialize("-$json")) + } + + + @Test + fun `positive Duration that would overflow in target unit cannot be serialized`() { + if (durationThatWouldOverflowInTargetUnit != null) assertFailsWith { + serialize(durationThatWouldOverflowInTargetUnit) + } + } + + @Test + fun `negative Duration that would overflow in target unit cannot be serialized`() { + if (durationThatWouldOverflowInTargetUnit != null) assertFailsWith { + serialize(-durationThatWouldOverflowInTargetUnit) + } + } + + + @Test + fun `large positive Duration gets deserialized as Infinity`() { + if (largeJson != null) assertEquals(expected = Duration.INFINITE, deserialize(largeJson)) + } + + @Test + fun `large negative Duration gets deserialized as -Infinity`() { + if (largeJson != null) assertEquals(expected = -Duration.INFINITE, deserialize("-$largeJson")) } } @@ -82,6 +122,9 @@ class DurationInNanosecondsSerializerTest : DurationSerializerTest( json = "84169", duration = 84169.nanoseconds, durationToRound = 84169.48.nanoseconds, + // use Long.MAX_VALUE / 1_000_000 + 1 (the smallest value that would overflow when multiplied by 1_000_000) + // Long.MAX_VALUE: 9_223_372_036_854_775_807 + durationThatWouldOverflowInTargetUnit = 9_223_372_036_855.milliseconds, serializer = DurationInNanosecondsSerializer, ) @@ -89,6 +132,9 @@ class DurationInMicrosecondsSerializerTest : DurationSerializerTest( json = "25622456", duration = 25622456.microseconds, durationToRound = 25622456.4.microseconds, + // use Long.MAX_VALUE / 1_000 + 1 (the smallest value that would overflow when multiplied by 1_000) + // Long.MAX_VALUE: 9_223_372_036_854_775_807 + durationThatWouldOverflowInTargetUnit = 9_223_372_036_854_776.milliseconds, serializer = DurationInMicrosecondsSerializer, ) @@ -96,6 +142,7 @@ class DurationInMillisecondsSerializerTest : DurationSerializerTest( json = "3495189", duration = 3495189.milliseconds, durationToRound = 3495189.24.milliseconds, + largeJson = "4611686018427387903", // the Duration implementation internal `MAX_MILLIS` serializer = DurationInMillisecondsSerializer, ) @@ -103,6 +150,7 @@ class DurationInSecondsSerializerTest : DurationSerializerTest( json = "987465", duration = 987465.seconds, durationToRound = 987465.489.seconds, + largeJson = "4611686018427388", // MAX_MILLIS / 1_000 + 1 serializer = DurationInSecondsSerializer, ) @@ -110,6 +158,7 @@ class DurationInMinutesSerializerTest : DurationSerializerTest( json = "24905", duration = 24905.minutes, durationToRound = 24905.164.minutes, + largeJson = "76861433640457", // MAX_MILLIS / 1_000 / 60 + 1 serializer = DurationInMinutesSerializer, ) @@ -117,6 +166,7 @@ class DurationInHoursSerializerTest : DurationSerializerTest( json = "7245", duration = 7245.hours, durationToRound = 7245.24.hours, + largeJson = "1281023894008", // MAX_MILLIS / 1_000 / 60 / 60 + 1 serializer = DurationInHoursSerializer, ) @@ -124,5 +174,6 @@ class DurationInDaysSerializerTest : DurationSerializerTest( json = "92", duration = 92.days, durationToRound = 92.12.days, + largeJson = "53375995584", // MAX_MILLIS / 1_000 / 60 / 60 / 24 + 1 serializer = DurationInDaysSerializer, ) From 6ecbe47849cecb78241c9d827b859c46ddb297cb Mon Sep 17 00:00:00 2001 From: Lukellmann Date: Wed, 6 Apr 2022 02:51:13 +0200 Subject: [PATCH 14/14] require in test --- common/src/test/kotlin/serialization/DurationSerializersTests.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/test/kotlin/serialization/DurationSerializersTests.kt b/common/src/test/kotlin/serialization/DurationSerializersTests.kt index 640ecf31e864..be071f4a8dbb 100644 --- a/common/src/test/kotlin/serialization/DurationSerializersTests.kt +++ b/common/src/test/kotlin/serialization/DurationSerializersTests.kt @@ -30,6 +30,7 @@ abstract class DurationSerializerTest( if (serializer.unit < MILLISECONDS) { require(durationThatWouldOverflowInTargetUnit != null) + require(durationThatWouldOverflowInTargetUnit.isPositive()) require(largeJson == null) } else { require(durationThatWouldOverflowInTargetUnit == null)