mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-11 15:52:58 +03:00
105 lines
2.6 KiB
Go
105 lines
2.6 KiB
Go
package utils
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
// GenerateRandomAlphanumericString generates a random alphanumeric string of the given length
|
|
func GenerateRandomAlphanumericString(length int) (string, error) {
|
|
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
|
|
if length <= 0 {
|
|
return "", errors.New("length must be a positive integer")
|
|
}
|
|
|
|
// The algorithm below is adapted from https://stackoverflow.com/a/35615565
|
|
const (
|
|
letterIdxBits = 6 // 6 bits to represent a letter index
|
|
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
|
)
|
|
|
|
result := strings.Builder{}
|
|
result.Grow(length)
|
|
// Because we discard a bunch of bytes, we read more in the buffer to minimize the changes of performing additional IO
|
|
bufferSize := int(float64(length) * 1.3)
|
|
randomBytes := make([]byte, bufferSize)
|
|
for i, j := 0, 0; i < length; j++ {
|
|
// Fill the buffer if needed
|
|
if j%bufferSize == 0 {
|
|
_, err := io.ReadFull(rand.Reader, randomBytes)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to generate random bytes: %w", err)
|
|
}
|
|
}
|
|
|
|
// Discard bytes that are outside of the range
|
|
// This allows making sure that we maintain uniform distribution
|
|
idx := int(randomBytes[j%length] & letterIdxMask)
|
|
if idx < len(charset) {
|
|
result.WriteByte(charset[idx])
|
|
i++
|
|
}
|
|
}
|
|
|
|
return result.String(), nil
|
|
}
|
|
|
|
func GetHostnameFromURL(rawURL string) string {
|
|
parsedURL, err := url.Parse(rawURL)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return parsedURL.Hostname()
|
|
}
|
|
|
|
// StringPointer creates a string pointer from a string value
|
|
func StringPointer(s string) *string {
|
|
return &s
|
|
}
|
|
|
|
func CapitalizeFirstLetter(str string) string {
|
|
if str == "" {
|
|
return ""
|
|
}
|
|
|
|
result := strings.Builder{}
|
|
result.Grow(len(str))
|
|
for i, r := range str {
|
|
if i == 0 {
|
|
result.WriteRune(unicode.ToUpper(r))
|
|
} else {
|
|
result.WriteRune(r)
|
|
}
|
|
}
|
|
return result.String()
|
|
}
|
|
|
|
func CamelCaseToSnakeCase(str string) string {
|
|
result := strings.Builder{}
|
|
result.Grow(int(float32(len(str)) * 1.1))
|
|
for i, r := range str {
|
|
if unicode.IsUpper(r) && i > 0 {
|
|
result.WriteByte('_')
|
|
}
|
|
result.WriteRune(unicode.ToLower(r))
|
|
}
|
|
return result.String()
|
|
}
|
|
|
|
var camelCaseToScreamingSnakeCaseRe = regexp.MustCompile(`([a-z0-9])([A-Z])`)
|
|
|
|
func CamelCaseToScreamingSnakeCase(s string) string {
|
|
// Insert underscores before uppercase letters (except the first one)
|
|
snake := camelCaseToScreamingSnakeCaseRe.ReplaceAllString(s, `${1}_${2}`)
|
|
|
|
// Convert to uppercase
|
|
return strings.ToUpper(snake)
|
|
}
|