init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
package uuidv7
import (
"crypto/rand"
"encoding/hex"
"errors"
"net/url"
"strings"
"time"
)
// New returns a UUIDv7 (RFC 9562) as a 16-byte array.
func New() ([16]byte, error) {
var out [16]byte
var r [10]byte
if _, err := rand.Read(r[:]); err != nil {
return out, err
}
// 48-bit Unix epoch milliseconds
ms := uint64(time.Now().UnixNano() / int64(time.Millisecond))
out[0] = byte(ms >> 40)
out[1] = byte(ms >> 32)
out[2] = byte(ms >> 24)
out[3] = byte(ms >> 16)
out[4] = byte(ms >> 8)
out[5] = byte(ms)
// Version (0111) in high 4 bits of byte 6
out[6] = (r[0] & 0x0f) | 0x70
out[7] = r[1]
// Variant (10) in high 2 bits of byte 8
out[8] = (r[2] & 0x3f) | 0x80
out[9] = r[3]
out[10] = r[4]
out[11] = r[5]
out[12] = r[6]
out[13] = r[7]
out[14] = r[8]
out[15] = r[9]
return out, nil
}
// MustNew panics if UUID generation fails.
func MustNew() [16]byte {
u, err := New()
if err != nil {
panic(err)
}
return u
}
// Bytes returns a new UUIDv7 as []byte for BINARY(16) columns.
func Bytes() ([]byte, error) {
u, err := New()
if err != nil {
return nil, err
}
b := make([]byte, 16)
copy(b, u[:])
return b, nil
}
// MustBytes panics if UUID generation fails.
func MustBytes() []byte {
u := MustNew()
b := make([]byte, 16)
copy(b, u[:])
return b
}
// String formats a UUID as a canonical string (8-4-4-4-12).
func String(u [16]byte) (string, error) {
if len(u) != 16 {
return "", errors.New("invalid uuid length")
}
dst := make([]byte, 36)
hex.Encode(dst[0:8], u[0:4])
dst[8] = '-'
hex.Encode(dst[9:13], u[4:6])
dst[13] = '-'
hex.Encode(dst[14:18], u[6:8])
dst[18] = '-'
hex.Encode(dst[19:23], u[8:10])
dst[23] = '-'
hex.Encode(dst[24:36], u[10:16])
return string(dst), nil
}
// BytesToString formats a UUID stored in []byte as a canonical string.
func BytesToString(b []byte) (string, error) {
if len(b) != 16 {
return "", errors.New("invalid uuid length")
}
var u [16]byte
copy(u[:], b)
return String(u)
}
// ParseString parses a canonical UUID string into []byte (16 bytes).
func ParseString(s string) ([]byte, error) {
if len(s) != 36 {
return nil, errors.New("invalid uuid string length")
}
buf := make([]byte, 16)
_, err := hex.Decode(buf[0:4], []byte(s[0:8]))
if err != nil {
return nil, err
}
_, err = hex.Decode(buf[4:6], []byte(s[9:13]))
if err != nil {
return nil, err
}
_, err = hex.Decode(buf[6:8], []byte(s[14:18]))
if err != nil {
return nil, err
}
_, err = hex.Decode(buf[8:10], []byte(s[19:23]))
if err != nil {
return nil, err
}
_, err = hex.Decode(buf[10:16], []byte(s[24:36]))
if err != nil {
return nil, err
}
return buf, nil
}
// ParsePathLikeString parses a UUID from a path-like value. This is useful for
// URL params that may arrive escaped, wrapped in a full URL, or with a trailing slash.
func ParsePathLikeString(raw string) ([]byte, error) {
for _, candidate := range candidateUUIDStrings(raw) {
if parsed, err := ParseString(candidate); err == nil {
return parsed, nil
}
}
return nil, errors.New("invalid uuid string")
}
func candidateUUIDStrings(raw string) []string {
raw = strings.TrimSpace(raw)
if raw == "" {
return nil
}
candidates := make([]string, 0, 6)
seen := make(map[string]struct{}, 6)
push := func(value string) {
value = strings.TrimSpace(value)
if value == "" {
return
}
if _, ok := seen[value]; ok {
return
}
seen[value] = struct{}{}
candidates = append(candidates, value)
}
push(raw)
if decoded, err := url.PathUnescape(raw); err == nil && decoded != raw {
push(decoded)
}
if decoded, err := url.QueryUnescape(raw); err == nil && decoded != raw {
push(decoded)
}
for i := 0; i < len(candidates); i++ {
value := candidates[i]
value = strings.TrimSuffix(value, "/")
push(value)
if idx := strings.IndexAny(value, "?#"); idx >= 0 {
push(value[:idx])
}
if idx := strings.LastIndex(value, "/"); idx >= 0 && idx+1 < len(value) {
push(value[idx+1:])
}
if idx := strings.LastIndex(value, "="); idx >= 0 && idx+1 < len(value) {
push(value[idx+1:])
}
for _, part := range strings.Split(value, "/") {
push(part)
}
for _, part := range strings.FieldsFunc(value, func(r rune) bool {
switch r {
case '/', '?', '#', '&', '=':
return true
default:
return false
}
}) {
push(part)
}
}
return candidates
}