123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- // Copyright 2017 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package util
-
- import (
- "bytes"
- "crypto/rand"
- "fmt"
- "math/big"
- "strconv"
- "strings"
-
- "code.gitea.io/gitea/modules/optional"
-
- "golang.org/x/text/cases"
- "golang.org/x/text/language"
- )
-
- // OptionalBoolParse get the corresponding optional.Option[bool] of a string using strconv.ParseBool
- func OptionalBoolParse(s string) optional.Option[bool] {
- v, e := strconv.ParseBool(s)
- if e != nil {
- return optional.None[bool]()
- }
- return optional.Some(v)
- }
-
- // IsEmptyString checks if the provided string is empty
- func IsEmptyString(s string) bool {
- return len(strings.TrimSpace(s)) == 0
- }
-
- // NormalizeEOL will convert Windows (CRLF) and Mac (CR) EOLs to UNIX (LF)
- func NormalizeEOL(input []byte) []byte {
- var right, left, pos int
- if right = bytes.IndexByte(input, '\r'); right == -1 {
- return input
- }
- length := len(input)
- tmp := make([]byte, length)
-
- // We know that left < length because otherwise right would be -1 from IndexByte.
- copy(tmp[pos:pos+right], input[left:left+right])
- pos += right
- tmp[pos] = '\n'
- left += right + 1
- pos++
-
- for left < length {
- if input[left] == '\n' {
- left++
- }
-
- right = bytes.IndexByte(input[left:], '\r')
- if right == -1 {
- copy(tmp[pos:], input[left:])
- pos += length - left
- break
- }
- copy(tmp[pos:pos+right], input[left:left+right])
- pos += right
- tmp[pos] = '\n'
- left += right + 1
- pos++
- }
- return tmp[:pos]
- }
-
- // CryptoRandomInt returns a crypto random integer between 0 and limit, inclusive
- func CryptoRandomInt(limit int64) (int64, error) {
- rInt, err := rand.Int(rand.Reader, big.NewInt(limit))
- if err != nil {
- return 0, err
- }
- return rInt.Int64(), nil
- }
-
- const alphanumericalChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
-
- // CryptoRandomString generates a crypto random alphanumerical string, each byte is generated by [0,61] range
- func CryptoRandomString(length int64) (string, error) {
- buf := make([]byte, length)
- limit := int64(len(alphanumericalChars))
- for i := range buf {
- num, err := CryptoRandomInt(limit)
- if err != nil {
- return "", err
- }
- buf[i] = alphanumericalChars[num]
- }
- return string(buf), nil
- }
-
- // CryptoRandomBytes generates `length` crypto bytes
- // This differs from CryptoRandomString, as each byte in CryptoRandomString is generated by [0,61] range
- // This function generates totally random bytes, each byte is generated by [0,255] range
- func CryptoRandomBytes(length int64) ([]byte, error) {
- buf := make([]byte, length)
- _, err := rand.Read(buf)
- return buf, err
- }
-
- // ToUpperASCII returns s with all ASCII letters mapped to their upper case.
- func ToUpperASCII(s string) string {
- b := []byte(s)
- for i, c := range b {
- if 'a' <= c && c <= 'z' {
- b[i] -= 'a' - 'A'
- }
- }
- return string(b)
- }
-
- // ToTitleCase returns s with all english words capitalized
- func ToTitleCase(s string) string {
- // `cases.Title` is not thread-safe, do not use global shared variable for it
- return cases.Title(language.English).String(s)
- }
-
- // ToTitleCaseNoLower returns s with all english words capitalized without lower-casing
- func ToTitleCaseNoLower(s string) string {
- // `cases.Title` is not thread-safe, do not use global shared variable for it
- return cases.Title(language.English, cases.NoLower).String(s)
- }
-
- // ToInt64 transform a given int into int64.
- func ToInt64(number any) (int64, error) {
- var value int64
- switch v := number.(type) {
- case int:
- value = int64(v)
- case int8:
- value = int64(v)
- case int16:
- value = int64(v)
- case int32:
- value = int64(v)
- case int64:
- value = v
-
- case uint:
- value = int64(v)
- case uint8:
- value = int64(v)
- case uint16:
- value = int64(v)
- case uint32:
- value = int64(v)
- case uint64:
- value = int64(v)
-
- case float32:
- value = int64(v)
- case float64:
- value = int64(v)
-
- case string:
- var err error
- if value, err = strconv.ParseInt(v, 10, 64); err != nil {
- return 0, err
- }
- default:
- return 0, fmt.Errorf("unable to convert %v to int64", number)
- }
- return value, nil
- }
-
- // ToFloat64 transform a given int into float64.
- func ToFloat64(number any) (float64, error) {
- var value float64
- switch v := number.(type) {
- case int:
- value = float64(v)
- case int8:
- value = float64(v)
- case int16:
- value = float64(v)
- case int32:
- value = float64(v)
- case int64:
- value = float64(v)
-
- case uint:
- value = float64(v)
- case uint8:
- value = float64(v)
- case uint16:
- value = float64(v)
- case uint32:
- value = float64(v)
- case uint64:
- value = float64(v)
-
- case float32:
- value = float64(v)
- case float64:
- value = v
-
- case string:
- var err error
- if value, err = strconv.ParseFloat(v, 64); err != nil {
- return 0, err
- }
- default:
- return 0, fmt.Errorf("unable to convert %v to float64", number)
- }
- return value, nil
- }
-
- // ToPointer returns the pointer of a copy of any given value
- func ToPointer[T any](val T) *T {
- return &val
- }
-
- // IfZero returns "def" if "v" is a zero value, otherwise "v"
- func IfZero[T comparable](v, def T) T {
- var zero T
- if v == zero {
- return def
- }
- return v
- }
-
- func ReserveLineBreakForTextarea(input string) string {
- // Since the content is from a form which is a textarea, the line endings are \r\n.
- // It's a standard behavior of HTML.
- // But we want to store them as \n like what GitHub does.
- // And users are unlikely to really need to keep the \r.
- // Other than this, we should respect the original content, even leading or trailing spaces.
- return strings.ReplaceAll(input, "\r\n", "\n")
- }
|