From c642c8f3063de4a3c319fe7ade64b6921263990e Mon Sep 17 00:00:00 2001 From: mavolin <48887425+mavolin@users.noreply.github.com> Date: Mon, 11 May 2020 03:14:30 +0200 Subject: [PATCH 1/9] Discord: refactor: move json.OptionT to dedicated json/option package --- api/channel.go | 16 ++++++++-------- api/guild.go | 12 ++++++------ api/member.go | 8 ++++---- utils/json/option.go | 34 ---------------------------------- utils/json/option/bool.go | 14 ++++++++++++++ utils/json/option/number.go | 25 +++++++++++++++++++++++++ utils/json/option/string.go | 12 ++++++++++++ 7 files changed, 69 insertions(+), 52 deletions(-) delete mode 100644 utils/json/option.go create mode 100644 utils/json/option/bool.go create mode 100644 utils/json/option/number.go create mode 100644 utils/json/option/string.go diff --git a/api/channel.go b/api/channel.go index 875be2f..c758374 100644 --- a/api/channel.go +++ b/api/channel.go @@ -3,7 +3,7 @@ package api import ( "github.com/diamondburned/arikawa/discord" "github.com/diamondburned/arikawa/utils/httputil" - "github.com/diamondburned/arikawa/utils/json" + "github.com/diamondburned/arikawa/utils/json/option" ) var EndpointChannels = Endpoint + "channels/" @@ -48,7 +48,7 @@ func (c *Client) CreateChannel( type MoveChannelData struct { ID discord.Snowflake `json:"id"` - Position json.OptionInt `json:"position"` + Position option.Int `json:"position"` } // MoveChannel modifies the position of channels in the guild. Requires @@ -69,21 +69,21 @@ func (c *Client) Channel(channelID discord.Snowflake) (*discord.Channel, error) type ModifyChannelData struct { // All types Name string `json:"name,omitempty"` - Position json.OptionInt `json:"position,omitempty"` + Position option.Int `json:"position,omitempty"` Permissions []discord.Overwrite `json:"permission_overwrites,omitempty"` // Text only - Topic json.OptionString `json:"topic,omitempty"` - NSFW json.OptionBool `json:"nsfw,omitempty"` + Topic option.String `json:"topic,omitempty"` + NSFW option.Bool `json:"nsfw,omitempty"` // 0-21600 seconds, refer to (discord.Channel).UserRateLimit. - UserRateLimit json.OptionInt `json:"rate_limit_per_user,omitempty"` + UserRateLimit option.Int `json:"rate_limit_per_user,omitempty"` // Voice only // 8000 - 96000 (or 128000 for Nitro) - VoiceBitrate json.OptionUint `json:"bitrate,omitempty"` + VoiceBitrate option.Uint `json:"bitrate,omitempty"` // 0 no limit, 1-99 - VoiceUserLimit json.OptionUint `json:"user_limit,omitempty"` + VoiceUserLimit option.Uint `json:"user_limit,omitempty"` // Text OR Voice CategoryID discord.Snowflake `json:"parent_id,string,omitempty"` diff --git a/api/guild.go b/api/guild.go index 2c6624a..3ce9ea3 100644 --- a/api/guild.go +++ b/api/guild.go @@ -6,7 +6,7 @@ import ( "github.com/diamondburned/arikawa/discord" // for clarity "github.com/diamondburned/arikawa/utils/httputil" - "github.com/diamondburned/arikawa/utils/json" + "github.com/diamondburned/arikawa/utils/json/option" ) var EndpointGuilds = Endpoint + "guilds/" @@ -134,8 +134,8 @@ func (c *Client) LeaveGuild(id discord.Snowflake) error { // https://discordapp.com/developers/docs/resources/guild#modify-guild-json-params type ModifyGuildData struct { - Name string `json:"name,omitempty"` - Region json.OptionString `json:"region,omitempty"` + Name string `json:"name,omitempty"` + Region option.String `json:"region,omitempty"` // package d is just package discord Verification *discord.Verification `json:"verification_level,omitempty"` @@ -155,7 +155,7 @@ type ModifyGuildData struct { RulesChannelID discord.Snowflake `json:"rules_channel_id,omitempty"` PublicUpdatesChannelID discord.Snowflake `json:"public_updates_channel_id,omitempty"` - PreferredLocale json.OptionString `json:"preferred_locale,omitempty"` + PreferredLocale option.String `json:"preferred_locale,omitempty"` } func (c *Client) ModifyGuild(id discord.Snowflake, data ModifyGuildData) (*discord.Guild, error) { @@ -236,8 +236,8 @@ func (c *Client) AttachIntegration( // https://discord.com/developers/docs/resources/guild#modify-guild-integration-json-params type ModifyIntegrationData struct { ExpireBehavior *discord.ExpireBehavior `json:"expire_behavior"` - ExpireGracePeriod json.OptionInt `json:"expire_grace_period"` - EnableEmoticons json.OptionBool `json:"enable_emoticons"` // limited to twitch + ExpireGracePeriod option.Int `json:"expire_grace_period"` + EnableEmoticons option.Bool `json:"enable_emoticons"` // limited to twitch } // ModifyIntegration requires MANAGE_GUILD. diff --git a/api/member.go b/api/member.go index 52b3d95..414a0b5 100644 --- a/api/member.go +++ b/api/member.go @@ -3,7 +3,7 @@ package api import ( "github.com/diamondburned/arikawa/discord" "github.com/diamondburned/arikawa/utils/httputil" - "github.com/diamondburned/arikawa/utils/json" + "github.com/diamondburned/arikawa/utils/json/option" ) func (c *Client) Member(guildID, userID discord.Snowflake) (*discord.Member, error) { @@ -80,9 +80,9 @@ func (c *Client) MembersAfter( // AnyMemberData, all fields are optional. type AnyMemberData struct { - Nick json.OptionString `json:"nick,omitempty"` - Mute json.OptionBool `json:"mute,omitempty"` - Deaf json.OptionBool `json:"deaf,omitempty"` + Nick option.String `json:"nick,omitempty"` + Mute option.Bool `json:"mute,omitempty"` + Deaf option.Bool `json:"deaf,omitempty"` Roles *[]discord.Snowflake `json:"roles,omitempty"` diff --git a/utils/json/option.go b/utils/json/option.go deleted file mode 100644 index 0edbc1a..0000000 --- a/utils/json/option.go +++ /dev/null @@ -1,34 +0,0 @@ -package json - -type ( - OptionBool = *bool - OptionString = *string - OptionUint = *uint - OptionInt = *int -) - -var ( - True = getBool(true) - False = getBool(false) - - ZeroUint = Uint(0) - ZeroInt = Int(0) - - EmptyString = String("") -) - -func Uint(u uint) OptionUint { - return &u -} - -func Int(i int) OptionInt { - return &i -} - -func String(s string) OptionString { - return &s -} - -func getBool(Bool bool) OptionBool { - return &Bool -} diff --git a/utils/json/option/bool.go b/utils/json/option/bool.go new file mode 100644 index 0000000..92396f0 --- /dev/null +++ b/utils/json/option/bool.go @@ -0,0 +1,14 @@ +package option + +// Bool is the option type for bool. +type Bool *bool + +var ( + True = newBool(true) + False = newBool(false) +) + +// newBool creates a new Bool with the value of the passed bool. +func newBool(b bool) Bool { + return &b +} diff --git a/utils/json/option/number.go b/utils/json/option/number.go new file mode 100644 index 0000000..f92e4f5 --- /dev/null +++ b/utils/json/option/number.go @@ -0,0 +1,25 @@ +package option + +type ( + // Uint is the option type for unsigned integers (uint). + Uint *uint + // Int is the option type for integers (int). + Int *int +) + +var ( + // ZeroUint is a Uint with 0 as value. + ZeroUint = NewUint(0) + // ZeroInt is an Int with 0 as value. + ZeroInt = NewInt(0) +) + +// NewUint creates a new Uint using the value of the passed uint. +func NewUint(u uint) Uint { + return &u +} + +// NewInt creates a new Int using the value of the passed int. +func NewInt(i int) Int { + return &i +} diff --git a/utils/json/option/string.go b/utils/json/option/string.go new file mode 100644 index 0000000..b31217c --- /dev/null +++ b/utils/json/option/string.go @@ -0,0 +1,12 @@ +package option + +// String is the option type for strings. +type String *string + +// EmptyString is a zero-length string. +var EmptyString = NewString("") + +// NewString creates a new String with the value of the passed string. +func NewString(s string) String { + return &s +} From 795182df16e8bbc95d5c4429ab10c38e30d87b32 Mon Sep 17 00:00:00 2001 From: mavolin <48887425+mavolin@users.noreply.github.com> Date: Mon, 11 May 2020 03:14:46 +0200 Subject: [PATCH 2/9] Discord: docs: add --- utils/json/option/doc.go | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 utils/json/option/doc.go diff --git a/utils/json/option/doc.go b/utils/json/option/doc.go new file mode 100644 index 0000000..55ed92a --- /dev/null +++ b/utils/json/option/doc.go @@ -0,0 +1,5 @@ +// Package option provides the ability to create omittable primitives. +// This is accomplished by pointerrizing common primitive types so that they may assume a nil value, which is considered +// as omitted by encoding/json. +// To generate pointerrized primitives, there are helper functions `NewT` for each option type. +package option From e522aaef9b53adbc9b8f51cf5be6f278978537bc Mon Sep 17 00:00:00 2001 From: mavolin <48887425+mavolin@users.noreply.github.com> Date: Mon, 11 May 2020 03:19:11 +0200 Subject: [PATCH 3/9] Discord: add Enum type --- utils/json/option/enum.go | 34 ++++++++++++++ utils/json/option/enum_test.go | 83 ++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 utils/json/option/enum.go create mode 100644 utils/json/option/enum_test.go diff --git a/utils/json/option/enum.go b/utils/json/option/enum.go new file mode 100644 index 0000000..740ae02 --- /dev/null +++ b/utils/json/option/enum.go @@ -0,0 +1,34 @@ +package option + +import "strconv" + +// EnumNull is the value used to represent JSON null. +// It should never be used as a value, as it won't get serialized as such. +const EnumNull = -1 + +// Enum is a nullable version of a uint8. +// Enum values should only consist of positive values, as negative values are reserved for internal constants, such as +// EnumNull. +// This also mean that only 7 of the 8 Bit will be available for storage. +type Enum int8 + +// Int8ToJSON converts the passed Enum to a byte slice with it's JSON representation. +func EnumToJSON(i Enum) []byte { + if i == EnumNull { + return []byte("null") + } else { + return []byte(strconv.Itoa(int(i))) + } +} + +// Int8FromJSON decodes the Enum stored as JSON src the passed byte slice. +func EnumFromJSON(b []byte) (Enum, error) { + s := string(b) + + if s == "null" { + return EnumNull, nil + } else { + i, err := strconv.ParseUint(s, 10, 7) + return Enum(i), err + } +} diff --git a/utils/json/option/enum_test.go b/utils/json/option/enum_test.go new file mode 100644 index 0000000..fd9b877 --- /dev/null +++ b/utils/json/option/enum_test.go @@ -0,0 +1,83 @@ +package option + +import ( + "reflect" + "testing" +) + +func TestInt8ToJSON(t *testing.T) { + testCases := []struct { + name string + src Enum + expect []byte + }{ + { + name: "null", + src: EnumNull, + expect: []byte("null"), + }, + { + name: "value", + src: 12, + expect: []byte("12"), + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + actual := EnumToJSON(c.src) + + if !reflect.DeepEqual(actual, c.expect) { + t.Errorf("expected nullable.Int8ToJSON to return: %+v, but got: %+v", c.expect, actual) + } + }) + } +} + +func TestInt8FromJSON(t *testing.T) { + testCases := []struct { + name string + src []byte + expect Enum + err bool + }{ + { + name: "null", + src: []byte("null"), + expect: EnumNull, + err: false, + }, + { + name: "value", + src: []byte("12"), + expect: 12, + err: false, + }, + { + name: "invalid input", + src: []byte("NaN"), + expect: 0, + err: true, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + actual, err := EnumFromJSON(c.src) + + if c.err { + if err == nil { + t.Error("expected nullable.Int8FromJSON to return an error, but it did not") + } + } else { + if !reflect.DeepEqual(actual, c.expect) { + t.Errorf("expected nullable.Int8FromJSON to return: %+v, but got: %+v", c.expect, actual) + } + + if err != nil { + t.Errorf("nullable.Int8FromJSON returned an error: %s", err.Error()) + } + } + }) + } +} From c1f942bb92326363f96a81b1b41e72bb44f3c726 Mon Sep 17 00:00:00 2001 From: mavolin <48887425+mavolin@users.noreply.github.com> Date: Mon, 11 May 2020 03:26:46 +0200 Subject: [PATCH 4/9] Discord: use Enum type for types mentioned in #29 --- discord/guild_const.go | 79 +++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/discord/guild_const.go b/discord/guild_const.go index 0eb8d0c..bb87376 100644 --- a/discord/guild_const.go +++ b/discord/guild_const.go @@ -1,5 +1,7 @@ package discord +import "github.com/diamondburned/arikawa/utils/json/option" + // Guild.MaxPresences is 5000 when it's 0. const DefaultMaxPresences = 5000 @@ -49,38 +51,83 @@ const ( Banner GuildFeature = "BANNER" ) -type ExplicitFilter uint8 +// ExplicitFilter is the explicit content filter level of a guild. +type ExplicitFilter option.Enum -const ( - NoContentFilter ExplicitFilter = iota - MembersWithoutRoles - AllMembers +var ( + // NullExplicitFilter serialized to JSON null. + // This should only be used on nullable fields. + NullExplicitFilter ExplicitFilter = option.EnumNull + // NoContentFilter disables content filtering for the guild. + NoContentFilter ExplicitFilter = 0 + // MembersWithoutRoles filters only members without roles. + MembersWithoutRoles ExplicitFilter = 1 + // AllMembers enables content filtering for all members. + AllMembers ExplicitFilter = 2 ) -type Notification uint8 +func (f *ExplicitFilter) UnmarshalJSON(b []byte) error { + i, err := option.EnumFromJSON(b) + *f = ExplicitFilter(i) -const ( - AllMessages Notification = iota - OnlyMentions + return err +} + +func (f ExplicitFilter) MarshalJSON() ([]byte, error) { return option.EnumToJSON(option.Enum(f)), nil } + +// Notification is the default message notification level of a guild. +type Notification option.Enum + +var ( + // NullNotification serialized to JSON null. + // This should only be used on nullable fields. + NullNotification Notification = option.EnumNull + // AllMessages sends notifications for all messages. + AllMessages Notification = 0 + // OnlyMentions sends notifications only on mention. + OnlyMentions Notification = 1 ) -type Verification uint8 +func (n *Notification) UnmarshalJSON(b []byte) error { + i, err := option.EnumFromJSON(b) + *n = Notification(i) -const ( - NoVerification Verification = iota + return err +} + +func (n Notification) MarshalJSON() ([]byte, error) { return option.EnumToJSON(option.Enum(n)), nil } + +// Verification is the verification level required for a guild. +type Verification option.Enum + +var ( + // NullVerification serialized to JSON null. + // This should only be used on nullable fields. + NullVerification Verification = option.EnumNull + // NoVerification required no verification. + NoVerification Verification = 0 // LowVerification requires a verified email - LowVerification + LowVerification Verification = 1 // MediumVerification requires the user be registered for at least 5 // minutes. - MediumVerification + MediumVerification Verification = 2 // HighVerification requires the member be in the server for more than 10 // minutes. - HighVerification + HighVerification Verification = 3 // VeryHighVerification requires the member to have a verified phone // number. - VeryHighVerification + VeryHighVerification Verification = 4 ) +func (v *Verification) UnmarshalJSON(b []byte) error { + i, err := option.EnumFromJSON(b) + *v = Verification(i) + + return err +} + +func (v Verification) MarshalJSON() ([]byte, error) { return option.EnumToJSON(option.Enum(v)), nil } + // Service is used for guild integrations and user connections. type Service string From a29a521c5a6ad4e8fc374aeec457adbf80ea502d Mon Sep 17 00:00:00 2001 From: mavolin <48887425+mavolin@users.noreply.github.com> Date: Mon, 11 May 2020 04:30:34 +0200 Subject: [PATCH 5/9] Utils: add nullable package --- api/channel.go | 2 +- api/guild.go | 12 ++++---- discord/channel.go | 16 +++++------ discord/guild_const.go | 30 +++++++++++--------- utils/json/nullable/bool.go | 14 +++++++++ utils/json/nullable/doc.go | 2 ++ utils/json/{option => nullable}/enum.go | 2 +- utils/json/{option => nullable}/enum_test.go | 2 +- utils/json/nullable/number.go | 25 ++++++++++++++++ utils/json/nullable/string.go | 12 ++++++++ 10 files changed, 87 insertions(+), 30 deletions(-) create mode 100644 utils/json/nullable/bool.go create mode 100644 utils/json/nullable/doc.go rename utils/json/{option => nullable}/enum.go (98%) rename utils/json/{option => nullable}/enum_test.go (98%) create mode 100644 utils/json/nullable/number.go create mode 100644 utils/json/nullable/string.go diff --git a/api/channel.go b/api/channel.go index c758374..e101217 100644 --- a/api/channel.go +++ b/api/channel.go @@ -20,7 +20,7 @@ type CreateChannelData struct { Name string `json:"name"` // 2-100 Topic string `json:"topic,omitempty"` - Type discord.ChannelType `json:"type,omitempty"` + Type *discord.ChannelType `json:"type,omitempty"` VoiceBitrate uint `json:"bitrate,omitempty"` VoiceUserLimit uint `json:"user_limit,omitempty"` diff --git a/api/guild.go b/api/guild.go index 3ce9ea3..933595f 100644 --- a/api/guild.go +++ b/api/guild.go @@ -17,9 +17,9 @@ type CreateGuildData struct { Icon Image `json:"image,omitempty"` // package dc is just package discord - Verification discord.Verification `json:"verification_level"` - Notification discord.Notification `json:"default_message_notifications"` - ExplicitFilter discord.ExplicitFilter `json:"explicit_content_filter"` + Verification *discord.Verification `json:"verification_level"` + Notification *discord.Notification `json:"default_message_notifications"` + ExplicitFilter *discord.ExplicitFilter `json:"explicit_content_filter"` // [0] (First entry) is ALWAYS @everyone. Roles []discord.Role `json:"roles,omitempty"` @@ -138,9 +138,9 @@ type ModifyGuildData struct { Region option.String `json:"region,omitempty"` // package d is just package discord - Verification *discord.Verification `json:"verification_level,omitempty"` - Notification *discord.Notification `json:"default_message_notifications,omitempty"` - ExplicitFilter *discord.ExplicitFilter `json:"explicit_content_filter,omitempty"` + Verification *discord.Verification `json:"verification_level,omitempty"` // nullable + Notification *discord.Notification `json:"default_message_notifications,omitempty"` // nullable + ExplicitFilter *discord.ExplicitFilter `json:"explicit_content_filter,omitempty"` // nullable AFKChannelID discord.Snowflake `json:"afk_channel_id,string,omitempty"` AFKTimeout discord.Seconds `json:"afk_timeout,omitempty"` diff --git a/discord/channel.go b/discord/channel.go index c8b97e7..4de39c1 100644 --- a/discord/channel.go +++ b/discord/channel.go @@ -58,14 +58,14 @@ func (ch Channel) IconURL() string { type ChannelType uint8 -const ( - GuildText ChannelType = iota - DirectMessage - GuildVoice - GroupDM - GuildCategory - GuildNews - GuildStore +var ( + GuildText ChannelType = 0 + DirectMessage ChannelType = 1 + GuildVoice ChannelType = 2 + GroupDM ChannelType = 3 + GuildCategory ChannelType = 4 + GuildNews ChannelType = 5 + GuildStore ChannelType = 6 ) type Overwrite struct { diff --git a/discord/guild_const.go b/discord/guild_const.go index bb87376..af1ee4e 100644 --- a/discord/guild_const.go +++ b/discord/guild_const.go @@ -1,6 +1,8 @@ package discord -import "github.com/diamondburned/arikawa/utils/json/option" +import ( + "github.com/diamondburned/arikawa/utils/json/nullable" +) // Guild.MaxPresences is 5000 when it's 0. const DefaultMaxPresences = 5000 @@ -52,12 +54,12 @@ const ( ) // ExplicitFilter is the explicit content filter level of a guild. -type ExplicitFilter option.Enum +type ExplicitFilter nullable.Enum var ( // NullExplicitFilter serialized to JSON null. // This should only be used on nullable fields. - NullExplicitFilter ExplicitFilter = option.EnumNull + NullExplicitFilter ExplicitFilter = nullable.EnumNull // NoContentFilter disables content filtering for the guild. NoContentFilter ExplicitFilter = 0 // MembersWithoutRoles filters only members without roles. @@ -67,21 +69,23 @@ var ( ) func (f *ExplicitFilter) UnmarshalJSON(b []byte) error { - i, err := option.EnumFromJSON(b) + i, err := nullable.EnumFromJSON(b) *f = ExplicitFilter(i) return err } -func (f ExplicitFilter) MarshalJSON() ([]byte, error) { return option.EnumToJSON(option.Enum(f)), nil } +func (f ExplicitFilter) MarshalJSON() ([]byte, error) { + return nullable.EnumToJSON(nullable.Enum(f)), nil +} // Notification is the default message notification level of a guild. -type Notification option.Enum +type Notification nullable.Enum var ( // NullNotification serialized to JSON null. // This should only be used on nullable fields. - NullNotification Notification = option.EnumNull + NullNotification Notification = nullable.EnumNull // AllMessages sends notifications for all messages. AllMessages Notification = 0 // OnlyMentions sends notifications only on mention. @@ -89,21 +93,21 @@ var ( ) func (n *Notification) UnmarshalJSON(b []byte) error { - i, err := option.EnumFromJSON(b) + i, err := nullable.EnumFromJSON(b) *n = Notification(i) return err } -func (n Notification) MarshalJSON() ([]byte, error) { return option.EnumToJSON(option.Enum(n)), nil } +func (n Notification) MarshalJSON() ([]byte, error) { return nullable.EnumToJSON(nullable.Enum(n)), nil } // Verification is the verification level required for a guild. -type Verification option.Enum +type Verification nullable.Enum var ( // NullVerification serialized to JSON null. // This should only be used on nullable fields. - NullVerification Verification = option.EnumNull + NullVerification Verification = nullable.EnumNull // NoVerification required no verification. NoVerification Verification = 0 // LowVerification requires a verified email @@ -120,13 +124,13 @@ var ( ) func (v *Verification) UnmarshalJSON(b []byte) error { - i, err := option.EnumFromJSON(b) + i, err := nullable.EnumFromJSON(b) *v = Verification(i) return err } -func (v Verification) MarshalJSON() ([]byte, error) { return option.EnumToJSON(option.Enum(v)), nil } +func (v Verification) MarshalJSON() ([]byte, error) { return nullable.EnumToJSON(nullable.Enum(v)), nil } // Service is used for guild integrations and user connections. type Service string diff --git a/utils/json/nullable/bool.go b/utils/json/nullable/bool.go new file mode 100644 index 0000000..0f76d29 --- /dev/null +++ b/utils/json/nullable/bool.go @@ -0,0 +1,14 @@ +package nullable + +// Bool is a nullable version of a bool. +type Bool *bool + +var ( + True = newBool(true) + False = newBool(false) +) + +// newBool creates a new Bool with the value of the passed bool. +func newBool(b bool) Bool { + return &b +} diff --git a/utils/json/nullable/doc.go b/utils/json/nullable/doc.go new file mode 100644 index 0000000..dab82ee --- /dev/null +++ b/utils/json/nullable/doc.go @@ -0,0 +1,2 @@ +// Package nullable provides nullable types that get serialized to JSON null. +package nullable diff --git a/utils/json/option/enum.go b/utils/json/nullable/enum.go similarity index 98% rename from utils/json/option/enum.go rename to utils/json/nullable/enum.go index 740ae02..7a21517 100644 --- a/utils/json/option/enum.go +++ b/utils/json/nullable/enum.go @@ -1,4 +1,4 @@ -package option +package nullable import "strconv" diff --git a/utils/json/option/enum_test.go b/utils/json/nullable/enum_test.go similarity index 98% rename from utils/json/option/enum_test.go rename to utils/json/nullable/enum_test.go index fd9b877..68fc749 100644 --- a/utils/json/option/enum_test.go +++ b/utils/json/nullable/enum_test.go @@ -1,4 +1,4 @@ -package option +package nullable import ( "reflect" diff --git a/utils/json/nullable/number.go b/utils/json/nullable/number.go new file mode 100644 index 0000000..97ac219 --- /dev/null +++ b/utils/json/nullable/number.go @@ -0,0 +1,25 @@ +package nullable + +type ( + // Uint is a nullable version of an unsigned integer (uint). + Uint *uint + // Int is a nullable version of an integer (int). + Int *int +) + +var ( + // ZeroUint is a Uint with 0 as value. + ZeroUint = NewUint(0) + // ZeroInt is an Int with 0 as value. + ZeroInt = NewInt(0) +) + +// NewUint creates a new Uint using the value of the passed uint. +func NewUint(u uint) Uint { + return &u +} + +// NewInt creates a new Int using the value of the passed int. +func NewInt(i int) Int { + return &i +} diff --git a/utils/json/nullable/string.go b/utils/json/nullable/string.go new file mode 100644 index 0000000..e3eaefa --- /dev/null +++ b/utils/json/nullable/string.go @@ -0,0 +1,12 @@ +package nullable + +// String is a nullable version of a string. +type String *string + +// EmptyString is a zero-length string. +var EmptyString = NewString("") + +// NewString creates a new String with the value of the passed string. +func NewString(s string) String { + return &s +} From b30a1fb7d2c4c595b9c1bee61d17c329cf2298c4 Mon Sep 17 00:00:00 2001 From: mavolin <48887425+mavolin@users.noreply.github.com> Date: Mon, 11 May 2020 05:24:12 +0200 Subject: [PATCH 6/9] Utils: add custom option types --- utils/json/option/custom.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 utils/json/option/custom.go diff --git a/utils/json/option/custom.go b/utils/json/option/custom.go new file mode 100644 index 0000000..7ab04c2 --- /dev/null +++ b/utils/json/option/custom.go @@ -0,0 +1,22 @@ +package option + +import "github.com/diamondburned/arikawa/discord" + +// ================================ Seconds ================================ + +// Seconds is the option type for discord.Seconds. +type Seconds *discord.Seconds + +// ZeroSeconds are 0 Seconds. +var ZeroSeconds = NewSeconds(0) + +// NewString creates a new Seconds with the value of the passed discord.Seconds. +func NewSeconds(s discord.Seconds) Seconds { return &s } + +// ================================ Color ================================ + +// Color is the option type for discord.Color. +type Color *discord.Color + +// NewString creates a new Color with the value of the passed discord.Color. +func NewColor(s discord.Color) Color { return &s } From b17c32187ed68d2d4ddbadf409c07dec33121f21 Mon Sep 17 00:00:00 2001 From: mavolin <48887425+mavolin@users.noreply.github.com> Date: Mon, 11 May 2020 05:24:59 +0200 Subject: [PATCH 7/9] API: implement option and nullable types. --- api/channel.go | 12 +++++++----- api/guild.go | 2 +- api/role.go | 16 +++++++++++++--- api/user.go | 2 +- discord/auditlog.go | 3 ++- utils/json/option/bool.go | 4 +--- utils/json/option/number.go | 34 ++++++++++++++++------------------ utils/json/option/string.go | 4 +--- 8 files changed, 42 insertions(+), 35 deletions(-) diff --git a/api/channel.go b/api/channel.go index e101217..ebf2c36 100644 --- a/api/channel.go +++ b/api/channel.go @@ -27,8 +27,8 @@ type CreateChannelData struct { UserRateLimit discord.Seconds `json:"rate_limit_per_user,omitempty"` - NSFW bool `json:"nsfw"` - Position int `json:"position,omitempty"` + NSFW bool `json:"nsfw,omitempty"` + Position option.Int `json:"position,omitempty"` Permissions []discord.Overwrite `json:"permission_overwrites,omitempty"` CategoryID discord.Snowflake `json:"parent_id,string,omitempty"` @@ -68,9 +68,11 @@ func (c *Client) Channel(channelID discord.Snowflake) (*discord.Channel, error) type ModifyChannelData struct { // All types - Name string `json:"name,omitempty"` - Position option.Int `json:"position,omitempty"` - Permissions []discord.Overwrite `json:"permission_overwrites,omitempty"` + Name string `json:"name,omitempty"` + // Type allows conversions between text and news channels. + Type *discord.ChannelType `json:"type,omitempty"` + Position option.Int `json:"position,omitempty"` + Permissions *[]discord.Overwrite `json:"permission_overwrites,omitempty"` // Text only Topic option.String `json:"topic,omitempty"` diff --git a/api/guild.go b/api/guild.go index 933595f..0d67394 100644 --- a/api/guild.go +++ b/api/guild.go @@ -143,7 +143,7 @@ type ModifyGuildData struct { ExplicitFilter *discord.ExplicitFilter `json:"explicit_content_filter,omitempty"` // nullable AFKChannelID discord.Snowflake `json:"afk_channel_id,string,omitempty"` - AFKTimeout discord.Seconds `json:"afk_timeout,omitempty"` + AFKTimeout option.Seconds `json:"afk_timeout,omitempty"` OwnerID discord.Snowflake `json:"owner_id,omitempty"` diff --git a/api/role.go b/api/role.go index 92383a3..b2c0955 100644 --- a/api/role.go +++ b/api/role.go @@ -3,6 +3,7 @@ package api import ( "github.com/diamondburned/arikawa/discord" "github.com/diamondburned/arikawa/utils/httputil" + "github.com/diamondburned/arikawa/utils/json/option" ) func (c *Client) AddRole(guildID, userID, roleID discord.Snowflake) error { @@ -23,7 +24,7 @@ func (c *Client) Roles(guildID discord.Snowflake) ([]discord.Role, error) { EndpointGuilds+guildID.String()+"/roles") } -type AnyRoleData struct { +type CreateRoleData struct { Name string `json:"name,omitempty"` // "new role" Color discord.Color `json:"color,omitempty"` // 0 Hoist bool `json:"hoist,omitempty"` // false (show role separately) @@ -32,7 +33,7 @@ type AnyRoleData struct { Permissions discord.Permissions `json:"permissions,omitempty"` // @everyone } -func (c *Client) CreateRole(guildID discord.Snowflake, data AnyRoleData) (*discord.Role, error) { +func (c *Client) CreateRole(guildID discord.Snowflake, data CreateRoleData) (*discord.Role, error) { var role *discord.Role return role, c.RequestJSON( &role, "POST", @@ -60,9 +61,18 @@ func (c *Client) MoveRole( ) } +type ModifyRoleData struct { + Name string `json:"name,omitempty"` // "new role" + Color option.Color `json:"color,omitempty"` // 0 + Hoist option.Bool `json:"hoist,omitempty"` // false (show role separately) + + Mentionable option.Bool `json:"mentionable,omitempty"` // false + Permissions discord.Permissions `json:"permissions,omitempty"` // @everyone +} + func (c *Client) ModifyRole( guildID, roleID discord.Snowflake, - data AnyRoleData) (*discord.Role, error) { + data ModifyRoleData) (*discord.Role, error) { var role *discord.Role return role, c.RequestJSON( diff --git a/api/user.go b/api/user.go index 3fb4792..ef8eebb 100644 --- a/api/user.go +++ b/api/user.go @@ -23,7 +23,7 @@ func (c *Client) Me() (*discord.User, error) { type ModifySelfData struct { Username string `json:"username,omitempty"` - Avatar Image `json:"image,omitempty"` + Avatar *Image `json:"image,omitempty"` } func (c *Client) ModifyMe(data ModifySelfData) (*discord.User, error) { diff --git a/discord/auditlog.go b/discord/auditlog.go index 52d67b4..e79d6bf 100644 --- a/discord/auditlog.go +++ b/discord/auditlog.go @@ -1,8 +1,9 @@ package discord import ( - "github.com/diamondburned/arikawa/utils/json" "github.com/pkg/errors" + + "github.com/diamondburned/arikawa/utils/json" ) type AuditLog struct { diff --git a/utils/json/option/bool.go b/utils/json/option/bool.go index 92396f0..8e3482f 100644 --- a/utils/json/option/bool.go +++ b/utils/json/option/bool.go @@ -9,6 +9,4 @@ var ( ) // newBool creates a new Bool with the value of the passed bool. -func newBool(b bool) Bool { - return &b -} +func newBool(b bool) Bool { return &b } diff --git a/utils/json/option/number.go b/utils/json/option/number.go index f92e4f5..f8a5b18 100644 --- a/utils/json/option/number.go +++ b/utils/json/option/number.go @@ -1,25 +1,23 @@ package option -type ( - // Uint is the option type for unsigned integers (uint). - Uint *uint - // Int is the option type for integers (int). - Int *int -) +// ================================ Uint ================================ -var ( - // ZeroUint is a Uint with 0 as value. - ZeroUint = NewUint(0) - // ZeroInt is an Int with 0 as value. - ZeroInt = NewInt(0) -) +// Uint is the option type for unsigned integers (uint). +type Uint *uint + +// ZeroUint is a Uint with 0 as value. +var ZeroUint = NewUint(0) // NewUint creates a new Uint using the value of the passed uint. -func NewUint(u uint) Uint { - return &u -} +func NewUint(u uint) Uint { return &u } + +// ================================ Int ================================ + +// Int is the option type for integers (int). +type Int *int + +// ZeroInt is an Int with 0 as value. +var ZeroInt = NewInt(0) // NewInt creates a new Int using the value of the passed int. -func NewInt(i int) Int { - return &i -} +func NewInt(i int) Int { return &i } diff --git a/utils/json/option/string.go b/utils/json/option/string.go index b31217c..74ca0c6 100644 --- a/utils/json/option/string.go +++ b/utils/json/option/string.go @@ -7,6 +7,4 @@ type String *string var EmptyString = NewString("") // NewString creates a new String with the value of the passed string. -func NewString(s string) String { - return &s -} +func NewString(s string) String { return &s } From 4f4526eb4965fb3b83cf1db6a56ffe02db2cef32 Mon Sep 17 00:00:00 2001 From: mavolin <48887425+mavolin@users.noreply.github.com> Date: Mon, 11 May 2020 13:53:25 +0200 Subject: [PATCH 8/9] API: make CreateChannelData.Type non-pointer --- api/channel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/channel.go b/api/channel.go index ebf2c36..da49e5b 100644 --- a/api/channel.go +++ b/api/channel.go @@ -20,7 +20,7 @@ type CreateChannelData struct { Name string `json:"name"` // 2-100 Topic string `json:"topic,omitempty"` - Type *discord.ChannelType `json:"type,omitempty"` + Type discord.ChannelType `json:"type,omitempty"` VoiceBitrate uint `json:"bitrate,omitempty"` VoiceUserLimit uint `json:"user_limit,omitempty"` From 4a30938db01334a5df03f75263cb908c735f7a9e Mon Sep 17 00:00:00 2001 From: mavolin <48887425+mavolin@users.noreply.github.com> Date: Mon, 11 May 2020 13:54:45 +0200 Subject: [PATCH 9/9] Utils: use package names for package doc file --- utils/json/nullable/{doc.go => nullable.go} | 0 utils/json/option/{doc.go => option.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename utils/json/nullable/{doc.go => nullable.go} (100%) rename utils/json/option/{doc.go => option.go} (100%) diff --git a/utils/json/nullable/doc.go b/utils/json/nullable/nullable.go similarity index 100% rename from utils/json/nullable/doc.go rename to utils/json/nullable/nullable.go diff --git a/utils/json/option/doc.go b/utils/json/option/option.go similarity index 100% rename from utils/json/option/doc.go rename to utils/json/option/option.go