Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ _testmain.go

# govendor
/vendor/*/

# ide
.idea
30 changes: 28 additions & 2 deletions ksuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/rand"
"database/sql/driver"
"encoding/binary"
"encoding/json"
"fmt"
"io"
"math"
Expand Down Expand Up @@ -38,8 +39,9 @@ const (
)

// KSUIDs are 20 bytes:
// 00-03 byte: uint32 BE UTC timestamp with custom epoch
// 04-19 byte: random "payload"
//
// 00-03 byte: uint32 BE UTC timestamp with custom epoch
// 04-19 byte: random "payload"
type KSUID [byteLength]byte

var (
Expand Down Expand Up @@ -102,6 +104,11 @@ func (i KSUID) Get() interface{} {
return i
}

// Equal compares two KSUIDs for equality. It returns true if the two KSUIDs
func (i KSUID) Equal(k2 KSUID) bool {
return bytes.Equal(i[:], k2[:])
}

// Set satisfies the flag.Value interface, making it possible to use KSUIDs as
// part of of the command line options of a program.
func (i *KSUID) Set(s string) error {
Expand All @@ -116,6 +123,10 @@ func (i KSUID) MarshalBinary() ([]byte, error) {
return i.Bytes(), nil
}

func (i KSUID) MarshalJSON() ([]byte, error) {
return json.Marshal(i.String())
}

func (i *KSUID) UnmarshalText(b []byte) error {
id, err := Parse(string(b))
if err != nil {
Expand All @@ -134,6 +145,21 @@ func (i *KSUID) UnmarshalBinary(b []byte) error {
return nil
}

func (i *KSUID) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}

id, err := Parse(s)
if err != nil {
return fmt.Errorf("invalid KSUID: %w", err)
}

*i = id
return nil
}

// Value converts the KSUID into a SQL driver value which can be used to
// directly use the KSUID as parameter to a SQL query.
func (i KSUID) Value() (driver.Value, error) {
Expand Down
30 changes: 22 additions & 8 deletions ksuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ksuid

import (
"bytes"
"encoding/json"
"flag"
"fmt"
"sort"
Expand Down Expand Up @@ -122,6 +121,20 @@ func TestEncodeAndDecode(t *testing.T) {
}
}

func TestEqual(t *testing.T) {
x := New()
y := x

if !x.Equal(y) {
t.Error(x, "!=", y)
}

z := New()
if x.Equal(z) {
t.Error(x, "==", z)
}
}

func TestMarshalText(t *testing.T) {
var id1 = New()
var id2 KSUID
Expand Down Expand Up @@ -164,12 +177,13 @@ func TestMashalJSON(t *testing.T) {
var id1 = New()
var id2 KSUID

if b, err := json.Marshal(id1); err != nil {
t.Fatal(err)
} else if err := json.Unmarshal(b, &id2); err != nil {
t.Fatal(err)
} else if id1 != id2 {
t.Error(id1, "!=", id2)
b, err := id1.MarshalJSON()
if err != nil {
t.Error("unexpected error on marshal:", err)
}

if err := id2.UnmarshalJSON(b); err != nil {
t.Error("failed to unmarshal JSON:", err)
}
}

Expand Down Expand Up @@ -309,7 +323,7 @@ func TestGetTimestamp(t *testing.T) {
x, _ := NewRandomWithTime(nowTime)
xTime := int64(x.Timestamp())
unix := nowTime.Unix()
if xTime != unix - epochStamp {
if xTime != unix-epochStamp {
t.Fatal(xTime, "!=", unix)
}
}
Expand Down