Discord: Uint64 typed Snowflakes (#132)

* Use typed Snowflakes if possible

* Discord: make Snowflakes uint64

* Fix errors that emerged because of new typing
This commit is contained in:
Maximilian von Lindern 2020-07-29 22:58:39 +02:00 committed by diamondburned
parent 32789bb6e2
commit 908ef96089
7 changed files with 110 additions and 53 deletions

View File

@ -329,7 +329,7 @@ type AuditLogData struct {
// ActionType is the type of audit log event. // ActionType is the type of audit log event.
ActionType discord.AuditLogEvent `schema:"action_type,omitempty"` ActionType discord.AuditLogEvent `schema:"action_type,omitempty"`
// Before filters the log before a certain entry ID. // Before filters the log before a certain entry ID.
Before discord.Snowflake `schema:"before,omitempty"` Before discord.AuditLogEntryID `schema:"before,omitempty"`
// Limit limits how many entries are returned (default 50, minimum 1, // Limit limits how many entries are returned (default 50, minimum 1,
// maximum 100). // maximum 100).
Limit uint `schema:"limit"` Limit uint `schema:"limit"`

View File

@ -25,8 +25,8 @@ func (m *ChannelMention) Usage() string {
return "#channel" return "#channel"
} }
func (m *ChannelMention) ID() discord.Snowflake { func (m *ChannelMention) ID() discord.ChannelID {
return discord.Snowflake(*m) return discord.ChannelID(*m)
} }
func (m *ChannelMention) Mention() string { func (m *ChannelMention) Mention() string {
@ -45,8 +45,8 @@ func (m *UserMention) Usage() string {
return "@user" return "@user"
} }
func (m *UserMention) ID() discord.Snowflake { func (m *UserMention) ID() discord.UserID {
return discord.Snowflake(*m) return discord.UserID(*m)
} }
func (m *UserMention) Mention() string { func (m *UserMention) Mention() string {
@ -65,8 +65,8 @@ func (m *RoleMention) Usage() string {
return "@role" return "@role"
} }
func (m *RoleMention) ID() discord.Snowflake { func (m *RoleMention) ID() discord.RoleID {
return discord.Snowflake(*m) return discord.RoleID(*m)
} }
func (m *RoleMention) Mention() string { func (m *RoleMention) Mention() string {

View File

@ -2,44 +2,58 @@ package arguments
import ( import (
"testing" "testing"
"github.com/diamondburned/arikawa/discord"
) )
func TestMention(t *testing.T) { func TestChannelMention(t *testing.T) {
var ( test := new(ChannelMention)
c ChannelMention str := "<#123123>"
u UserMention id := 123123
r RoleMention
)
type mention interface { if err := test.Parse(str); err != nil {
Parse(arg string) error t.Fatal("Expected", id, "error:", err)
ID() discord.Snowflake
Mention() string
} }
var tests = []struct { if id := test.ID(); id != id {
mention t.Fatal("Expected", id, "got", id)
str string
id discord.Snowflake
}{
{&c, "<#123123>", 123123},
{&r, "<@&23321>", 23321},
{&u, "<@123123>", 123123},
} }
for _, test := range tests { if mention := test.Mention(); mention != str {
if err := test.Parse(test.str); err != nil { t.Fatal("Expected", str, "got", mention)
t.Fatal("Expected", test.id, "error:", err) }
} }
if id := test.ID(); id != test.id { func TestUserMention(t *testing.T) {
t.Fatal("Expected", test.id, "got", id) test := new(UserMention)
} str := "<@123123>"
id := 123123
if mention := test.Mention(); mention != test.str {
t.Fatal("Expected", test.str, "got", mention) if err := test.Parse(str); err != nil {
} t.Fatal("Expected", id, "error:", err)
}
if id := test.ID(); id != id {
t.Fatal("Expected", id, "got", id)
}
if mention := test.Mention(); mention != str {
t.Fatal("Expected", str, "got", mention)
}
}
func TestRoleMention(t *testing.T) {
test := new(RoleMention)
str := "<@&123123>"
id := 123123
if err := test.Parse(str); err != nil {
t.Fatal("Expected", id, "error:", err)
}
if id := test.ID(); id != id {
t.Fatal("Expected", id, "got", id)
}
if mention := test.Mention(); mention != str {
t.Fatal("Expected", str, "got", mention)
} }
} }

View File

@ -67,7 +67,7 @@ func reflectID(v reflect.Value, thing string) discord.Snowflake {
if chID := reflectID(v.Field(i), thing); chID.IsValid() { if chID := reflectID(v.Field(i), thing); chID.IsValid() {
return chID return chID
} }
case reflect.Int64: case reflect.Uint64:
switch { switch {
case false, case false,
// Contains works with "LastMessageID" and such. // Contains works with "LastMessageID" and such.
@ -75,7 +75,7 @@ func reflectID(v reflect.Value, thing string) discord.Snowflake {
// Special case where the struct name has Channel in it. // Special case where the struct name has Channel in it.
field.Name == "ID" && strings.Contains(t.Name(), thing): field.Name == "ID" && strings.Contains(t.Name(), thing):
return discord.Snowflake(v.Field(i).Int()) return discord.Snowflake(v.Field(i).Uint())
} }
} }
} }

View File

@ -6,24 +6,23 @@ import (
"time" "time"
) )
// DiscordEpoch is the Discord epoch constant in time.Duration (nanoseconds) // Epoch is the Discord epoch constant in time.Duration (nanoseconds)
// since Unix epoch. // since Unix epoch.
const DiscordEpoch = 1420070400000 * time.Millisecond const Epoch = 1420070400000 * time.Millisecond
// DurationSinceDiscordEpoch returns the duration from the Discord epoch to // DurationSinceEpoch returns the duration from the Discord epoch to current.
// current. func DurationSinceEpoch(t time.Time) time.Duration {
func DurationSinceDiscordEpoch(t time.Time) time.Duration { return time.Duration(t.UnixNano()) - Epoch
return time.Duration(t.UnixNano()) - DiscordEpoch
} }
type Snowflake int64 type Snowflake uint64
// NullSnowflake gets encoded into a null. This is used for // NullSnowflake gets encoded into a null. This is used for
// optional and nullable snowflake fields. // optional and nullable snowflake fields.
const NullSnowflake Snowflake = -1 const NullSnowflake = ^Snowflake(0)
func NewSnowflake(t time.Time) Snowflake { func NewSnowflake(t time.Time) Snowflake {
return Snowflake((DurationSinceDiscordEpoch(t) / time.Millisecond) << 22) return Snowflake((DurationSinceEpoch(t) / time.Millisecond) << 22)
} }
func ParseSnowflake(sf string) (Snowflake, error) { func ParseSnowflake(sf string) (Snowflake, error) {
@ -70,7 +69,7 @@ func (s Snowflake) String() string {
// IsValid returns whether or not the snowflake is valid. // IsValid returns whether or not the snowflake is valid.
func (s Snowflake) IsValid() bool { func (s Snowflake) IsValid() bool {
return int64(s) > 0 return !(int64(s) == 0 || s == NullSnowflake)
} }
// IsNull returns whether or not the snowflake is null. // IsNull returns whether or not the snowflake is null.
@ -79,7 +78,7 @@ func (s Snowflake) IsNull() bool {
} }
func (s Snowflake) Time() time.Time { func (s Snowflake) Time() time.Time {
unixnano := ((time.Duration(s) >> 22) * time.Millisecond) + DiscordEpoch unixnano := time.Duration(s>>22)*time.Millisecond + Epoch
return time.Unix(0, int64(unixnano)) return time.Unix(0, int64(unixnano))
} }
@ -97,6 +96,8 @@ func (s Snowflake) Increment() uint16 {
type AppID Snowflake type AppID Snowflake
const NullAppID = AppID(NullSnowflake)
func (s AppID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s AppID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *AppID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *AppID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s AppID) String() string { return Snowflake(s).String() } func (s AppID) String() string { return Snowflake(s).String() }
@ -109,6 +110,8 @@ func (s AppID) Increment() uint16 { return Snowflake(s).Increment()
type AttachmentID Snowflake type AttachmentID Snowflake
const NullAttachmentID = AttachmentID(NullSnowflake)
func (s AttachmentID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s AttachmentID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *AttachmentID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *AttachmentID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s AttachmentID) String() string { return Snowflake(s).String() } func (s AttachmentID) String() string { return Snowflake(s).String() }
@ -121,6 +124,8 @@ func (s AttachmentID) Increment() uint16 { return Snowflake(s).Incre
type AuditLogEntryID Snowflake type AuditLogEntryID Snowflake
const NullAuditLogEntryID = AuditLogEntryID(NullSnowflake)
func (s AuditLogEntryID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s AuditLogEntryID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *AuditLogEntryID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *AuditLogEntryID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s AuditLogEntryID) String() string { return Snowflake(s).String() } func (s AuditLogEntryID) String() string { return Snowflake(s).String() }
@ -133,6 +138,8 @@ func (s AuditLogEntryID) Increment() uint16 { return Snowflake(s).In
type ChannelID Snowflake type ChannelID Snowflake
const NullChannelID = ChannelID(NullSnowflake)
func (s ChannelID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s ChannelID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *ChannelID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *ChannelID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s ChannelID) String() string { return Snowflake(s).String() } func (s ChannelID) String() string { return Snowflake(s).String() }
@ -145,6 +152,8 @@ func (s ChannelID) Increment() uint16 { return Snowflake(s).Incremen
type EmojiID Snowflake type EmojiID Snowflake
const NullEmojiID = EmojiID(NullSnowflake)
func (s EmojiID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s EmojiID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *EmojiID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *EmojiID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s EmojiID) String() string { return Snowflake(s).String() } func (s EmojiID) String() string { return Snowflake(s).String() }
@ -157,6 +166,8 @@ func (s EmojiID) Increment() uint16 { return Snowflake(s).Increment(
type IntegrationID Snowflake type IntegrationID Snowflake
const NullIntegrationID = IntegrationID(NullSnowflake)
func (s IntegrationID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s IntegrationID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *IntegrationID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *IntegrationID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s IntegrationID) String() string { return Snowflake(s).String() } func (s IntegrationID) String() string { return Snowflake(s).String() }
@ -169,6 +180,8 @@ func (s IntegrationID) Increment() uint16 { return Snowflake(s).Incr
type GuildID Snowflake type GuildID Snowflake
const NullGuildID = GuildID(NullSnowflake)
func (s GuildID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s GuildID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *GuildID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *GuildID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s GuildID) String() string { return Snowflake(s).String() } func (s GuildID) String() string { return Snowflake(s).String() }
@ -181,6 +194,8 @@ func (s GuildID) Increment() uint16 { return Snowflake(s).Increment(
type MessageID Snowflake type MessageID Snowflake
const NullMessageID = MessageID(NullSnowflake)
func (s MessageID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s MessageID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *MessageID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *MessageID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s MessageID) String() string { return Snowflake(s).String() } func (s MessageID) String() string { return Snowflake(s).String() }
@ -193,6 +208,8 @@ func (s MessageID) Increment() uint16 { return Snowflake(s).Incremen
type RoleID Snowflake type RoleID Snowflake
const NullRoleID = RoleID(NullSnowflake)
func (s RoleID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s RoleID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *RoleID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *RoleID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s RoleID) String() string { return Snowflake(s).String() } func (s RoleID) String() string { return Snowflake(s).String() }
@ -205,6 +222,8 @@ func (s RoleID) Increment() uint16 { return Snowflake(s).Increment()
type UserID Snowflake type UserID Snowflake
const NullUserID = UserID(NullSnowflake)
func (s UserID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s UserID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *UserID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *UserID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s UserID) String() string { return Snowflake(s).String() } func (s UserID) String() string { return Snowflake(s).String() }
@ -217,6 +236,8 @@ func (s UserID) Increment() uint16 { return Snowflake(s).Increment()
type WebhookID Snowflake type WebhookID Snowflake
const NullWebhookID = WebhookID(NullSnowflake)
func (s WebhookID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } func (s WebhookID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *WebhookID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } func (s *WebhookID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s WebhookID) String() string { return Snowflake(s).String() } func (s WebhookID) String() string { return Snowflake(s).String() }

View File

@ -36,6 +36,28 @@ func TestSnowflake(t *testing.T) {
} }
}) })
t.Run("IsValid", func(t *testing.T) {
t.Run("0", func(t *testing.T) {
if Snowflake(0).IsValid() {
t.Fatal("0 isn't a valid Snowflake")
}
})
t.Run("null", func(t *testing.T) {
if NullSnowflake.IsValid() {
t.Fatal("NullSnowflake isn't a valid Snowflake")
}
})
t.Run("valid", func(t *testing.T) {
var testFlake Snowflake = 123
if !testFlake.IsValid() {
t.Fatal(testFlake, "is a valid Snowflake")
}
})
})
t.Run("new", func(t *testing.T) { t.Run("new", func(t *testing.T) {
if s := NewSnowflake(expect); !s.Time().Equal(expect) { if s := NewSnowflake(expect); !s.Time().Equal(expect) {
t.Fatal("Unexpected new snowflake from expected time:", s) t.Fatal("Unexpected new snowflake from expected time:", s)

View File

@ -138,7 +138,7 @@ func (s *Session) JoinChannelCtx(ctx context.Context, gID discord.GuildID, cID d
s.speaking = false s.speaking = false
// Ensure that if `cID` is zero that it passes null to the update event. // Ensure that if `cID` is zero that it passes null to the update event.
var channelID discord.ChannelID = -1 channelID := discord.NullChannelID
if cID.IsValid() { if cID.IsValid() {
channelID = cID channelID = cID
} }