Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate kord enums with KSP #686

Merged
merged 53 commits into from
Sep 18, 2022
Merged

Generate kord enums with KSP #686

merged 53 commits into from
Sep 18, 2022

Conversation

lukellmann
Copy link
Member

@lukellmann lukellmann commented Sep 11, 2022

This PR adds KSP and KotlinPoet to the project and introduces an annotation to generate "kord enums" (classes like ChannelType). All these classes are now generated with this annotation.

We have a lot of sealed classes like ChannelType (representing this) that are very similar. They all have:

  • a single property that represents the value (the value discord actually uses)
  • a serializer
  • a set of object subtypes that represent all known values
  • an Unknown subclass that we use for values that aren't documented / added yet

I gave them the name "kord enums" because they are essentially enums with the ability to represent unknown cases.

Every time we add a new one of these classes (e.g. when discord adds a new feature), we have to write a lot of boilerplate to make the class similar to the existing ones. But it's easy to bring in slight variations which leads to inconsistent code over time.
Adding new subtypes to these classes can also be error-prone, since it requires adding the new type and also adding it in the serializer which can be easy to forget (see b75e33f).

This PR introduces a KSP processor to solve these problems. It allows us to define these classes via annotations which reduces boilerplate, keeps consistency across these classes and ensures a single point of truth for the value (we had the value in the subtype declaration and serializer before).

Another benefit is that when we want to change something about all these classes, we just have to adjust the processor.

In the future we might use KSP to also generate other boilerplate heavy parts of kord (flags, builders etc.).

This PR also

  • implements equals, hashCode and toString for every kord enum.
  • adds WebhookType.Application (was missing before).
  • replaces DiscordGuildApplicationCommandPermission.Type with ApplicationCommandPermissionType because of technical reasons (can't generate nested classes).
  • removes deprecation from EmbedType.
  • adds a new entries property in the companion object of every generated kord enum with type List<KordEnumType> that is a list of all known entries. The name is taken from Kotlin's upcoming Enum.entries.
  • fixes a bug that deleted public threads would produce UnknownChannelThreadDeleteEvent instead of TextChannelThreadDeleteEvent.

# Conflicts:
#	common/src/main/kotlin/entity/DiscordChannel.kt
#	core/src/main/kotlin/entity/channel/Channel.kt
# Conflicts:
#	build.gradle.kts
#	buildSrc/build.gradle.kts
#	buildSrc/src/main/kotlin/kord-module.gradle.kts
#	common/src/main/kotlin/entity/DiscordChannel.kt
#	common/src/main/kotlin/entity/DiscordMessage.kt
#	core/src/main/kotlin/gateway/handler/ThreadEventHandler.kt
# Conflicts:
#	common/src/main/kotlin/entity/DiscordChannel.kt
…resetType`, `AutoModerationRuleEventType` and `AutoModerationActionType`
…`ExplicitContentFilter`, `VerificationLevel`, `NsfwLevel` and `PremiumTier`
out/
dokka/

**/build/*
!**/build/generated/
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now I included the generated files by removing them from the .gitignore. I'm not sure if this is the way we should do it, @DRSchlaubi had some other ideas before, what do you say?

Comment on lines +7958 to +7959
public final class dev/kord/common/entity/WebhookType$Application : dev/kord/common/entity/WebhookType {
public static final field INSTANCE Ldev/kord/common/entity/WebhookType$Application;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WebhookType.Application is the only truly new API added in this PR (other API dump changes are only additions of equals(), hashCode(), toString(), entries, companion objects and deprecation artifacts)

PrivateThread -> TextChannelThread(data, kord)
PublicGuildThread -> TextChannelThread(data, kord)
PrivateThread, PublicGuildThread -> TextChannelThread(data, kord)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just code deduplication

Comment on lines -81 to +82
is ChannelType.PrivateThread,
is ChannelType.GuildText -> TextChannelThreadDeleteEvent(channel, old as? TextChannelThread, shard, context?.get())
ChannelType.PrivateThread,
ChannelType.PublicGuildThread -> TextChannelThreadDeleteEvent(channel, old as? TextChannelThread, shard, context?.get())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bug: PublicGuildThreads would result in an UnknownChannelThreadDeleteEvent instead of a TextChannelThreadDeleteEvent

Comment on lines +30 to +36
// TODO remove eventually
/** For migration purposes. */
val valuesPropertyName: String = "",
/** For migration purposes. */
val valuesPropertyType: ValuesPropertyType = NONE,
/** For migration purposes. */
val deprecatedSerializerName: String = "",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are only added to preserve binary compatibility and will be removed eventually after the corresponding deprecation cycle

# Conflicts:
#	common/src/main/kotlin/entity/DiscordGuild.kt
@lukellmann lukellmann force-pushed the refactor/enum-generation branch from 48a3e0c to e418baf Compare September 12, 2022 03:37
@lukellmann lukellmann marked this pull request as ready for review September 12, 2022 03:45
@lukellmann
Copy link
Member Author

This is ready

@HopeBaron
Copy link
Member

does this mean that all Add x code are now redundant?
I've not looked at the code yet tho

@lukellmann
Copy link
Member Author

Add x code? What do you mean?

@HopeBaron
Copy link
Member

Error codes from other PRs

@lukellmann
Copy link
Member Author

Nope, I haven't touched JsonErrorCode here. And apart from that, we still have to add new values.

@HopeBaron HopeBaron merged commit 10d8fc8 into 0.8.x Sep 18, 2022
@lukellmann lukellmann deleted the refactor/enum-generation branch September 20, 2022 14:15
@lukellmann lukellmann mentioned this pull request Oct 11, 2022
@lukellmann lukellmann mentioned this pull request Aug 24, 2023
lukellmann added a commit that referenced this pull request Aug 26, 2023
This PR adds bit flag classes to the code generation in Kord (see #686
for the reasoning of generating boilerplate heavy parts of Kord).

It also tries to unify the API shape of all existing bit flag classes by
deprecating some existing declarations and replacing them with
declarations that all bit flag classes have in common. They now have
this general API shape:

sealed class SomeFlag(val shift: Int) {
    val value: ValueType
    operator fun plus(flag: SomeFlag): SomeFlags
    operator fun plus(flags: SomeFlags): SomeFlags

    class Unknown : SomeFlag
    object Flag1 : SomeFlag
    object Flag2 : SomeFlag
    // ...

    companion object {
        val entries: List<SomeFlag>
        fun fromShift(shift: Int): SomeFlag
    }
}

@serializable
class SomeFlags(val value: ValueType) {
    val values: Set<SomeFlag>
    operator fun contains(flag: SomeFlag): Boolean
    operator fun contains(flags: SomeFlags): Boolean
    operator fun plus(flag: SomeFlag): SomeFlags
    operator fun plus(flags: SomeFlags): SomeFlags
    operator fun minus(flag: SomeFlag): SomeFlags
    operator fun minus(flags: SomeFlags): SomeFlags
    fun copy(builder: Builder.() -> Unit): SomeFlags

    class Builder(value: ValueType = /* ... */) {
        operator fun SomeFlag.unaryPlus()
        operator fun SomeFlags.unaryPlus()
        operator fun SomeFlag.unaryMinus()
        operator fun SomeFlags.unaryMinus()
        fun build(): SomeFlags
    }
}

fun SomeFlags(builder: SomeFlags.Builder.() -> Unit): SomeFlags
fun SomeFlags(vararg flags: SomeFlag): SomeFlags
fun SomeFlags(vararg flags: SomeFlags): SomeFlags
fun SomeFlags(flags: Iterable<SomeFlag>): SomeFlags
fun SomeFlags(flags: Iterable<SomeFlags>): SomeFlags

This means that flag classes are no longer enum classes. Binary
compatibility was preserved as far as possible, but the fact that these
classes now no longer have java.lang.Enum as their supertype is a binary
incompatible change.

This PR also

 * adds ActivityFlag.PartyPrivacyFriends,
   ActivityFlag.PartyPrivacyVoiceChannel and ActivityFlag.Embedded
   (these were missing before)

 * deprecates UserFlag.System (it was undocumented in 2021[1])

 * renames Intent.GuildBans to Intent.GuildModeration (see
   https://github.com/discord/discord-api-docs/pull/5849/files#diff-c24665b017972d8f7c266214d30655ea5e105826ef212048f9460632dd61e3df)

 * fixes a bug where .copy{ /* ... */ } would also modify the original
   instance for bit flags based on DiscordBitSet

 * fixes some minus functions that would toggle instead of remove bits
   (this.value xor other.value -> this.value and other.value.inv())

[1] discord/discord-api-docs@9293f0d490ac6acf9d627e429e5a8131b303b528S

---------

Co-authored-by: lukellmann <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants