Skip to content

Commit

Permalink
feat: add Snowflake
Browse files Browse the repository at this point in the history
  • Loading branch information
DasPoet committed Aug 26, 2021
1 parent 14bbb10 commit d086766
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 0 deletions.
93 changes: 93 additions & 0 deletions snowflake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package snowflake

import (
"bytes"
"encoding/json"
"strconv"
"time"
)

// Snowflake represents a Discord snowflake.
// See https://discord.com/developers/docs/reference
// for more information.
type Snowflake int64

// Nil represents an empty Snowflake.
var Nil = (*Snowflake)(nil)

// Zero represents the zero value for a Snowflake.
var Zero = Snowflake(0)

// discordEpoch is the time of the first second of 2015, in milliseconds.
const discordEpoch = 1420070400000

// Parse constructs a Snowflake from s.
func Parse(s string) (Snowflake, error) {
if s == "" {
return Zero, nil
}
parsed, err := strconv.ParseInt(s, 10, 0)
return Snowflake(parsed), err
}

// FromTimestamp generates a Snowflake for the given time.
func FromTimestamp(time time.Time) Snowflake {
return Snowflake(time.UnixMilli()-discordEpoch) << 22
}

// Timestamp returns the number of milliseconds since discordEpoch.
func (s Snowflake) Timestamp() int64 {
return (int64(s) >> 22) + discordEpoch
}

// WorkerID returns the Snowflake's worker id.
func (s Snowflake) WorkerID() int64 {
return (int64(s) & 0x3E0000) >> 17
}

// ProcessID returns the Snowflake's process id.
func (s Snowflake) ProcessID() int64 {
return (int64(s) & 0x1F000) >> 12
}

// Increment returns the number of id's that have
// been generated on the Snowflake's process.
func (s Snowflake) Increment() int64 {
return int64(s) & 0xFFF
}

// UnmarshalJSON unmarshals data into s.
func (s *Snowflake) UnmarshalJSON(data []byte) error {
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()

var raw json.Number
if err := dec.Decode(&raw); err != nil {
typeErr, ok := err.(*json.UnmarshalTypeError)
if ok {
return UnmarshalTypeError{
value: typeErr.Value,
structName: typeErr.Struct,
field: typeErr.Field,
typ: "int or string",
}
}
return err
}

parsed, err := raw.Int64()
if err == nil {
*s = Snowflake(parsed)
}
return err
}

// MarshalJSON returns the JSON representation of s.
func (s Snowflake) MarshalJSON() ([]byte, error) {
return []byte(s.String()), nil
}

// String returns the string representation of s.
func (s Snowflake) String() string {
return strconv.FormatInt(int64(s), 10)
}
20 changes: 20 additions & 0 deletions unmarshal_type_err.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package snowflake

import "fmt"

// UnmarshalTypeError is like json.UnmarshalTypeError, but
// uses a string to represent the Go value it could not be
// assigned to, instead of a reflect.Type.
type UnmarshalTypeError struct {
value string
structName string
field string
typ string
}

func (err UnmarshalTypeError) Error() string {
if err.structName != "" || err.field != "" {
return fmt.Sprintf("json: cannot unmarshal %s into Go struct field %s\".\"%s of type %s", err.value, err.structName, err.field, err.typ)
}
return fmt.Sprintf("json: cannot unmarshal %s into Go value of type %s", err.value, err.typ)
}

0 comments on commit d086766

Please sign in to comment.