-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for ULID string format (#80)
More details about format: https://github.com/ulid/spec It uses binary underlying implementation: https://github.com/oklog/ulid Signed-off-by: Ilya Pavlov <[email protected]>
- Loading branch information
Showing
9 changed files
with
657 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package conv | ||
|
||
import "github.com/go-openapi/strfmt" | ||
|
||
// ULID returns a pointer to of the ULID value passed in. | ||
func ULID(v strfmt.ULID) *strfmt.ULID { | ||
return &v | ||
} | ||
|
||
// ULIDValue returns the value of the ULID pointer passed in or | ||
// the default value if the pointer is nil. | ||
func ULIDValue(v *strfmt.ULID) strfmt.ULID { | ||
if v == nil { | ||
return strfmt.ULID{} | ||
} | ||
|
||
return *v | ||
} |
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 |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package conv | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/go-openapi/strfmt" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
const testUlid = string("01EYXZVGBHG26MFTG4JWR4K558") | ||
|
||
func TestULIDValue(t *testing.T) { | ||
assert.Equal(t, strfmt.ULID{}, ULIDValue(nil)) | ||
|
||
value := strfmt.ULID{} | ||
err := value.UnmarshalText([]byte(testUlid)) | ||
assert.NoError(t, err) | ||
assert.Equal(t, value, ULIDValue(&value)) | ||
|
||
ulidRef := ULID(value) | ||
assert.Equal(t, &value, ulidRef) | ||
} |
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
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
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 |
---|---|---|
@@ -0,0 +1,221 @@ | ||
package strfmt | ||
|
||
import ( | ||
cryptorand "crypto/rand" | ||
"database/sql/driver" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"sync" | ||
|
||
"github.com/oklog/ulid" | ||
"go.mongodb.org/mongo-driver/bson" | ||
) | ||
|
||
// ULID represents a ulid string format | ||
// ref: | ||
// https://github.com/ulid/spec | ||
// impl: | ||
// https://github.com/oklog/ulid | ||
// | ||
// swagger:strfmt ulid | ||
type ULID struct { | ||
ulid.ULID | ||
} | ||
|
||
var ( | ||
ulidEntropyPool = sync.Pool{ | ||
New: func() interface{} { | ||
return cryptorand.Reader | ||
}, | ||
} | ||
|
||
ULIDScanDefaultFunc = func(raw interface{}) (ULID, error) { | ||
var u ULID = NewULIDZero() | ||
switch x := raw.(type) { | ||
case nil: | ||
// zerp ulid | ||
return u, nil | ||
case string: | ||
if x == "" { | ||
// zero ulid | ||
return u, nil | ||
} | ||
return u, u.UnmarshalText([]byte(x)) | ||
case []byte: | ||
return u, u.UnmarshalText(x) | ||
} | ||
|
||
return u, fmt.Errorf("cannot sql.Scan() strfmt.ULID from: %#v: %w", raw, ulid.ErrScanValue) | ||
} | ||
|
||
// ULIDScanOverrideFunc allows you to override the Scan method of the ULID type | ||
ULIDScanOverrideFunc = ULIDScanDefaultFunc | ||
|
||
ULIDValueDefaultFunc = func(u ULID) (driver.Value, error) { | ||
return driver.Value(u.String()), nil | ||
} | ||
|
||
// ULIDValueOverrideFunc allows you to override the Value method of the ULID type | ||
ULIDValueOverrideFunc = ULIDValueDefaultFunc | ||
) | ||
|
||
func init() { | ||
// register formats in the default registry: | ||
// - ulid | ||
ulid := ULID{} | ||
Default.Add("ulid", &ulid, IsULID) | ||
} | ||
|
||
// IsULID checks if provided string is ULID format | ||
// Be noticed that this function considers overflowed ULID as non-ulid. | ||
// For more details see https://github.com/ulid/spec | ||
func IsULID(str string) bool { | ||
_, err := ulid.ParseStrict(str) | ||
return err == nil | ||
} | ||
|
||
// ParseULID parses a string that represents an valid ULID | ||
func ParseULID(str string) (ULID, error) { | ||
var u ULID | ||
|
||
return u, u.UnmarshalText([]byte(str)) | ||
} | ||
|
||
// NewULIDZero returns a zero valued ULID type | ||
func NewULIDZero() ULID { | ||
return ULID{} | ||
} | ||
|
||
// NewULID generates new unique ULID value and a error if any | ||
func NewULID() (u ULID, err error) { | ||
entropy := ulidEntropyPool.Get().(io.Reader) | ||
|
||
id, err := ulid.New(ulid.Now(), entropy) | ||
if err != nil { | ||
return u, err | ||
} | ||
ulidEntropyPool.Put(entropy) | ||
|
||
u.ULID = id | ||
return u, nil | ||
} | ||
|
||
// GetULID returns underlying instance of ULID | ||
func (u *ULID) GetULID() interface{} { | ||
return u.ULID | ||
} | ||
|
||
// MarshalText returns this instance into text | ||
func (u ULID) MarshalText() ([]byte, error) { | ||
return u.ULID.MarshalText() | ||
} | ||
|
||
// UnmarshalText hydrates this instance from text | ||
func (u *ULID) UnmarshalText(data []byte) error { // validation is performed later on | ||
return u.ULID.UnmarshalText(data) | ||
} | ||
|
||
// Scan reads a value from a database driver | ||
func (u *ULID) Scan(raw interface{}) error { | ||
ul, err := ULIDScanOverrideFunc(raw) | ||
if err == nil { | ||
*u = ul | ||
} | ||
return err | ||
} | ||
|
||
// Value converts a value to a database driver value | ||
func (u ULID) Value() (driver.Value, error) { | ||
return ULIDValueOverrideFunc(u) | ||
} | ||
|
||
func (u ULID) String() string { | ||
return u.ULID.String() | ||
} | ||
|
||
// MarshalJSON returns the ULID as JSON | ||
func (u ULID) MarshalJSON() ([]byte, error) { | ||
return json.Marshal(u.String()) | ||
} | ||
|
||
// UnmarshalJSON sets the ULID from JSON | ||
func (u *ULID) UnmarshalJSON(data []byte) error { | ||
if string(data) == jsonNull { | ||
return nil | ||
} | ||
var ustr string | ||
if err := json.Unmarshal(data, &ustr); err != nil { | ||
return err | ||
} | ||
id, err := ulid.ParseStrict(ustr) | ||
if err != nil { | ||
return fmt.Errorf("couldn't parse JSON value as ULID: %w", err) | ||
} | ||
u.ULID = id | ||
return nil | ||
} | ||
|
||
// MarshalBSON document from this value | ||
func (u ULID) MarshalBSON() ([]byte, error) { | ||
return bson.Marshal(bson.M{"data": u.String()}) | ||
} | ||
|
||
// UnmarshalBSON document into this value | ||
func (u *ULID) UnmarshalBSON(data []byte) error { | ||
var m bson.M | ||
if err := bson.Unmarshal(data, &m); err != nil { | ||
return err | ||
} | ||
|
||
if ud, ok := m["data"].(string); ok { | ||
id, err := ulid.ParseStrict(ud) | ||
if err != nil { | ||
return fmt.Errorf("couldn't parse bson bytes as ULID: %w", err) | ||
} | ||
u.ULID = id | ||
return nil | ||
} | ||
return errors.New("couldn't unmarshal bson bytes as ULID") | ||
} | ||
|
||
// DeepCopyInto copies the receiver and writes its value into out. | ||
func (u *ULID) DeepCopyInto(out *ULID) { | ||
*out = *u | ||
} | ||
|
||
// DeepCopy copies the receiver into a new ULID. | ||
func (u *ULID) DeepCopy() *ULID { | ||
if u == nil { | ||
return nil | ||
} | ||
out := new(ULID) | ||
u.DeepCopyInto(out) | ||
return out | ||
} | ||
|
||
// GobEncode implements the gob.GobEncoder interface. | ||
func (u ULID) GobEncode() ([]byte, error) { | ||
return u.ULID.MarshalBinary() | ||
} | ||
|
||
// GobDecode implements the gob.GobDecoder interface. | ||
func (u *ULID) GobDecode(data []byte) error { | ||
return u.ULID.UnmarshalBinary(data) | ||
} | ||
|
||
// MarshalBinary implements the encoding.BinaryMarshaler interface. | ||
func (u ULID) MarshalBinary() ([]byte, error) { | ||
return u.ULID.MarshalBinary() | ||
} | ||
|
||
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. | ||
func (u *ULID) UnmarshalBinary(data []byte) error { | ||
return u.ULID.UnmarshalBinary(data) | ||
} | ||
|
||
// Equal checks if two ULID instances are equal by their underlying type | ||
func (u ULID) Equal(other ULID) bool { | ||
return u.ULID == other.ULID | ||
} |
Oops, something went wrong.