From bd4608b467c5be01d4e0cc64923a032a7d2dd8c5 Mon Sep 17 00:00:00 2001 From: tom twinkle Date: Thu, 25 Aug 2022 11:39:53 +0900 Subject: [PATCH 1/2] feat: add ulid utils --- ulid/go.mod | 5 ++++ ulid/go.sum | 3 ++ ulid/ulid.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++ ulid/ulid_test.go | 62 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+) create mode 100644 ulid/go.mod create mode 100644 ulid/go.sum create mode 100644 ulid/ulid.go create mode 100644 ulid/ulid_test.go diff --git a/ulid/go.mod b/ulid/go.mod new file mode 100644 index 0000000..6cc3639 --- /dev/null +++ b/ulid/go.mod @@ -0,0 +1,5 @@ +module github.com/88labs/go-utils/ulid + +go 1.18 + +require github.com/oklog/ulid/v2 v2.1.0 diff --git a/ulid/go.sum b/ulid/go.sum new file mode 100644 index 0000000..5a427a4 --- /dev/null +++ b/ulid/go.sum @@ -0,0 +1,3 @@ +github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= +github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= diff --git a/ulid/ulid.go b/ulid/ulid.go new file mode 100644 index 0000000..296e545 --- /dev/null +++ b/ulid/ulid.go @@ -0,0 +1,70 @@ +package ulid + +import ( + "crypto/rand" + "errors" + "io" + "sync" + + oklogulid "github.com/oklog/ulid/v2" +) + +type ULID oklogulid.ULID + +var ( + pool = sync.Pool{ + New: func() interface{} { return oklogulid.Monotonic(rand.Reader, 0) }, + } + zeroValueULID oklogulid.ULID + ErrULIDZero = errors.New("ulid is zero") +) + +func New() (ULID, error) { + var entropy = rand.Reader + if e, ok := pool.Get().(io.Reader); ok { + entropy = e + defer pool.Put(e) + } + id, err := oklogulid.New(oklogulid.Now(), entropy) + if err != nil { + return ULID{}, err + } + return ULID(id), nil +} + +func MustNew() ULID { + id, err := New() + if err != nil { + panic(err) + } + return id +} + +func MustParse(s string) ULID { + u := ULID(oklogulid.MustParseStrict(s)) + if u.IsZero() { + panic(ErrULIDZero) + } + return u +} + +func Parse(s string) (ULID, error) { + oklogULID, err := oklogulid.ParseStrict(s) + u := ULID(oklogULID) + if err != nil { + return u, err + } + if u.IsZero() { + return u, ErrULIDZero + } + + return u, nil +} + +func (u ULID) ToString() string { + return oklogulid.ULID(u).String() +} + +func (u ULID) IsZero() bool { + return oklogulid.ULID(u) == zeroValueULID +} diff --git a/ulid/ulid_test.go b/ulid/ulid_test.go new file mode 100644 index 0000000..c603961 --- /dev/null +++ b/ulid/ulid_test.go @@ -0,0 +1,62 @@ +package ulid_test + +import ( + "fmt" + "strings" + "sync" + "testing" + + "github.com/88labs/go-utils/ulid" +) + +func TestParse(t *testing.T) { + tests := []struct { + name string + input string + err string + }{ + {"zero", "00000000000000000000000000", "ulid is zero"}, + {"ok", "0000XSNJG0MQJHBF4QX1EFD6Y3", ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + id, err := ulid.Parse(tt.input) + if len(tt.err) == 0 { + if err != nil { + t.Fatalf("want no err, but has err %v", err) + } + if id.IsZero() { + t.Fatal("ulid is zero") + } + } + + if len(tt.err) > 0 { + if err == nil { + t.Fatalf("want %v, but %v", tt.err, err) + } + if !strings.Contains(err.Error(), tt.err) { + t.Fatalf("want %v, but %v", tt.err, err) + } + } + }) + } +} + +func TestMustNew(t *testing.T) { + t.Run("ulidnew", func(t *testing.T) { + var wg sync.WaitGroup + num := 100000 + wg.Add(num) + for i := 0; i < num; i++ { + go func() { + defer wg.Done() + id := ulid.MustNew() + if id.IsZero() { + panic(fmt.Sprintf("zero: %v", id)) + } + }() + } + wg.Wait() + }) +} From a6dfd98aab3dcd35c8d320525842869fb0a34da6 Mon Sep 17 00:00:00 2001 From: tom twinkle Date: Thu, 25 Aug 2022 12:10:43 +0900 Subject: [PATCH 2/2] Changed function name to satisfy Stringers interface --- ulid/ulid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ulid/ulid.go b/ulid/ulid.go index 296e545..fde6ef1 100644 --- a/ulid/ulid.go +++ b/ulid/ulid.go @@ -61,7 +61,7 @@ func Parse(s string) (ULID, error) { return u, nil } -func (u ULID) ToString() string { +func (u ULID) String() string { return oklogulid.ULID(u).String() }