142 lines
3.1 KiB
Go
142 lines
3.1 KiB
Go
package models
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"database/sql/driver"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/oklog/ulid/v2"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/schema"
|
|
)
|
|
|
|
// ULID is a custom type that wraps oklog/ulid/v2.ULID and implements
|
|
// GORM's Scanner and Valuer interfaces for database operations
|
|
type ULID struct {
|
|
ulid.ULID
|
|
}
|
|
|
|
// NewULID creates a new ULID from a ulid.ULID
|
|
func NewULID(u ulid.ULID) ULID {
|
|
return ULID{ULID: u}
|
|
}
|
|
|
|
// ParseULID parses a string into a ULID
|
|
func ParseULID(s string) (ULID, error) {
|
|
u, err := ulid.Parse(s)
|
|
if err != nil {
|
|
return ULID{}, err
|
|
}
|
|
return ULID{ULID: u}, nil
|
|
}
|
|
|
|
// MustParseULID parses a string into a ULID and panics on error
|
|
func MustParseULID(s string) ULID {
|
|
u, err := ParseULID(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return u
|
|
}
|
|
|
|
// GenerateULID creates a new ULID with the current timestamp
|
|
func GenerateULID() ULID {
|
|
return ULID{ULID: ulid.MustNew(ulid.Timestamp(time.Now()), rand.Reader)}
|
|
}
|
|
|
|
// GenerateULIDWithTime creates a new ULID with the specified timestamp
|
|
func GenerateULIDWithTime(t time.Time) ULID {
|
|
return ULID{ULID: ulid.MustNew(ulid.Timestamp(t), rand.Reader)}
|
|
}
|
|
|
|
// Scan implements the sql.Scanner interface for reading from database
|
|
func (u *ULID) Scan(value interface{}) error {
|
|
if value == nil {
|
|
*u = ULID{}
|
|
return nil
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case string:
|
|
parsed, err := ulid.Parse(v)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot parse ULID from string: %w", err)
|
|
}
|
|
u.ULID = parsed
|
|
return nil
|
|
case []byte:
|
|
parsed, err := ulid.Parse(string(v))
|
|
if err != nil {
|
|
return fmt.Errorf("cannot parse ULID from bytes: %w", err)
|
|
}
|
|
u.ULID = parsed
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("cannot scan %T into ULID", value)
|
|
}
|
|
}
|
|
|
|
// Value implements the driver.Valuer interface for writing to database
|
|
func (u ULID) Value() (driver.Value, error) {
|
|
if u.ULID == (ulid.ULID{}) {
|
|
return nil, nil
|
|
}
|
|
return u.ULID.String(), nil
|
|
}
|
|
|
|
// GormDataType returns the data type for GORM
|
|
func (ULID) GormDataType() string {
|
|
return "char(26)"
|
|
}
|
|
|
|
// GormDBDataType returns the database-specific data type for GORM
|
|
func (ULID) GormDBDataType(db *gorm.DB, field *schema.Field) string {
|
|
switch db.Dialector.Name() {
|
|
case "postgres":
|
|
return "char(26)"
|
|
case "mysql":
|
|
return "char(26)"
|
|
case "sqlite":
|
|
return "text"
|
|
default:
|
|
return "char(26)"
|
|
}
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler
|
|
func (u ULID) MarshalJSON() ([]byte, error) {
|
|
return []byte(`"` + u.ULID.String() + `"`), nil
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler
|
|
func (u *ULID) UnmarshalJSON(data []byte) error {
|
|
if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
|
|
return fmt.Errorf("invalid JSON string for ULID")
|
|
}
|
|
|
|
str := string(data[1 : len(data)-1])
|
|
if str == "" {
|
|
*u = ULID{}
|
|
return nil
|
|
}
|
|
|
|
parsed, err := ulid.Parse(str)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot parse ULID from JSON: %w", err)
|
|
}
|
|
|
|
u.ULID = parsed
|
|
return nil
|
|
}
|
|
|
|
// String returns the string representation of the ULID
|
|
func (u ULID) String() string {
|
|
return u.ULID.String()
|
|
}
|
|
|
|
// IsZero returns true if the ULID is zero value
|
|
func (u ULID) IsZero() bool {
|
|
return u.ULID == ulid.ULID{}
|
|
}
|