-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from disgoorg/refactor/uint64-snowflakes
update to unit64 from strings and v2
- Loading branch information
Showing
3 changed files
with
115 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
module github.com/disgoorg/snowflake | ||
module github.com/disgoorg/snowflake/v2 | ||
|
||
go 1.18 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,81 +1,124 @@ | ||
package snowflake | ||
|
||
import ( | ||
"fmt" | ||
"bytes" | ||
"os" | ||
"strconv" | ||
"time" | ||
) | ||
|
||
var Epoch int64 = 1420070400000 | ||
// Epoch is the discord epoch in milliseconds. | ||
const Epoch = 1420070400000 | ||
|
||
// NewSnowflake returns a new Snowflake based on the given time.Time | ||
//goland:noinspection GoUnusedExportedFunction | ||
func NewSnowflake(timestamp time.Time) Snowflake { | ||
return Snowflake(strconv.FormatInt(((timestamp.UnixNano()/1_000_000)-Epoch)<<22, 10)) | ||
// Parse parses a string into a snowflake ID. | ||
// returns ID(0) if the string is "null" | ||
func Parse(str string) (ID, error) { | ||
if str == "null" { | ||
return 0, nil | ||
} | ||
id, err := strconv.ParseUint(str, 10, 64) | ||
if err != nil { | ||
return 0, err | ||
} | ||
return ID(id), nil | ||
} | ||
|
||
// ParseString parses a fmt.Stringer into a Snowflake | ||
//goland:noinspection GoUnusedExportedFunction | ||
func ParseString(str fmt.Stringer) Snowflake { | ||
return Snowflake(str.String()) | ||
// MustParse parses a string into a snowflake ID and panics on error. | ||
// returns ID(0) if the string is "null" | ||
func MustParse(str string) ID { | ||
id, err := Parse(str) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return id | ||
} | ||
|
||
// ParseInt64 parses an int64 into a Snowflake | ||
//goland:noinspection GoUnusedExportedFunction | ||
func ParseInt64(i int64) Snowflake { | ||
return Snowflake(strconv.FormatInt(i, 10)) | ||
// GetEnv returns the value of the environment variable named by the key and parses it as a snowflake. | ||
// returns ID(0) if the environment variable is not set. | ||
func GetEnv(key string) ID { | ||
snowflake, _ := LookupEnv(key) | ||
return snowflake | ||
} | ||
|
||
// ParseUInt64 parses an uint64 into a Snowflake | ||
//goland:noinspection GoUnusedExportedFunction | ||
func ParseUInt64(i uint64) Snowflake { | ||
return Snowflake(strconv.FormatUint(i, 10)) | ||
// LookupEnv returns the value of the environment variable named by the key and parses it as a snowflake. | ||
// returns false if the environment variable is not set. | ||
func LookupEnv(key string) (ID, bool) { | ||
env, found := os.LookupEnv(key) | ||
if !found { | ||
return 0, false | ||
} | ||
snowflake, _ := Parse(env) | ||
return snowflake, true | ||
} | ||
|
||
// GetSnowflakeEnv returns a new Snowflake from an environment variable | ||
//goland:noinspection GoUnusedExportedFunction | ||
func GetSnowflakeEnv(key string) Snowflake { | ||
return Snowflake(os.Getenv(key)) | ||
// New creates a new snowflake ID from the provided timestamp with worker id and sequence 0. | ||
func New(timestamp time.Time) ID { | ||
return ID((timestamp.UnixMilli() - Epoch) << 22) | ||
} | ||
|
||
// Snowflake is a general utility type around discord's IDs | ||
type Snowflake string | ||
// ID represents a unique snowflake ID. | ||
type ID uint64 | ||
|
||
// String returns the string representation of the Snowflake | ||
func (s Snowflake) String() string { | ||
return string(s) | ||
// MarshalJSON marshals the snowflake ID into a JSON string. | ||
func (id ID) MarshalJSON() ([]byte, error) { | ||
return []byte(strconv.Quote(strconv.FormatUint(uint64(id), 10))), nil | ||
} | ||
|
||
// Int64 returns the int64 representation of the Snowflake | ||
func (s Snowflake) Int64() int64 { | ||
snowflake, err := strconv.ParseInt(s.String(), 10, 64) | ||
// UnmarshalJSON unmarshals the snowflake ID from a JSON string. | ||
func (id *ID) UnmarshalJSON(data []byte) error { | ||
if bytes.Equal(data, []byte("null")) { | ||
return nil | ||
} | ||
snowflake, err := strconv.Unquote(string(data)) | ||
if err != nil { | ||
panic(err.Error()) | ||
return err | ||
} | ||
return snowflake | ||
i, err := strconv.ParseUint(snowflake, 10, 64) | ||
if err != nil { | ||
return err | ||
} | ||
*id = ID(i) | ||
return nil | ||
} | ||
|
||
// Deconstruct returns DeconstructedSnowflake (https://discord.com/developers/docs/reference#snowflakes-snowflake-id-format-structure-left-to-right) | ||
func (s Snowflake) Deconstruct() DeconstructedSnowflake { | ||
snowflake := s.Int64() | ||
return DeconstructedSnowflake{ | ||
Time: time.Unix(0, ((snowflake>>22)+Epoch)*1_000_000), | ||
WorkerID: (snowflake & 0x3E0000) >> 17, | ||
ProcessID: (snowflake & 0x1F000) >> 12, | ||
Increment: snowflake & 0xFFF, | ||
} | ||
// String returns a string representation of the snowflake ID. | ||
func (id ID) String() string { | ||
return strconv.FormatUint(uint64(id), 10) | ||
} | ||
|
||
// Time returns the time.Time the snowflake was created. | ||
func (id ID) Time() time.Time { | ||
return time.UnixMilli(int64(id>>22 + Epoch)) | ||
} | ||
|
||
// Time returns the time.Time when the snowflake was created | ||
func (s Snowflake) Time() time.Time { | ||
return s.Deconstruct().Time | ||
// WorkerID returns the id of the worker the snowflake was created on. | ||
func (id ID) WorkerID() uint8 { | ||
return uint8(id & 0x3E0000 >> 17) | ||
} | ||
|
||
func (id ID) ProcessID() uint8 { | ||
return uint8(id & 0x3E0000 >> 12) | ||
} | ||
|
||
// Sequence returns the sequence of the snowflake. | ||
func (id ID) Sequence() uint16 { | ||
return uint16(id & 0xFFF) | ||
} | ||
|
||
// Deconstruct returns DeconstructedID (https://discord.com/developers/docs/reference#snowflakes-snowflake-id-format-structure-left-to-right). | ||
func (id ID) Deconstruct() DeconstructedSnowflake { | ||
return DeconstructedSnowflake{ | ||
Time: id.Time(), | ||
WorkerID: id.WorkerID(), | ||
ProcessID: id.ProcessID(), | ||
Sequence: id.Sequence(), | ||
} | ||
} | ||
|
||
// DeconstructedSnowflake contains the properties used by Discord for each CommandID | ||
// DeconstructedSnowflake contains the properties used by Discord for each snowflake ID. | ||
type DeconstructedSnowflake struct { | ||
Time time.Time | ||
WorkerID int64 | ||
ProcessID int64 | ||
Increment int64 | ||
WorkerID uint8 | ||
ProcessID uint8 | ||
Sequence uint16 | ||
} |