gitea源码

util.go 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package util
  4. import (
  5. "bytes"
  6. "crypto/rand"
  7. "fmt"
  8. "math/big"
  9. "strconv"
  10. "strings"
  11. "golang.org/x/text/cases"
  12. "golang.org/x/text/language"
  13. )
  14. // IsEmptyString checks if the provided string is empty
  15. func IsEmptyString(s string) bool {
  16. return len(strings.TrimSpace(s)) == 0
  17. }
  18. // NormalizeEOL will convert Windows (CRLF) and Mac (CR) EOLs to UNIX (LF)
  19. func NormalizeEOL(input []byte) []byte {
  20. var right, left, pos int
  21. if right = bytes.IndexByte(input, '\r'); right == -1 {
  22. return input
  23. }
  24. length := len(input)
  25. tmp := make([]byte, length)
  26. // We know that left < length because otherwise right would be -1 from IndexByte.
  27. copy(tmp[pos:pos+right], input[left:left+right])
  28. pos += right
  29. tmp[pos] = '\n'
  30. left += right + 1
  31. pos++
  32. for left < length {
  33. if input[left] == '\n' {
  34. left++
  35. }
  36. right = bytes.IndexByte(input[left:], '\r')
  37. if right == -1 {
  38. copy(tmp[pos:], input[left:])
  39. pos += length - left
  40. break
  41. }
  42. copy(tmp[pos:pos+right], input[left:left+right])
  43. pos += right
  44. tmp[pos] = '\n'
  45. left += right + 1
  46. pos++
  47. }
  48. return tmp[:pos]
  49. }
  50. // CryptoRandomInt returns a crypto random integer between 0 and limit, inclusive
  51. func CryptoRandomInt(limit int64) (int64, error) {
  52. rInt, err := rand.Int(rand.Reader, big.NewInt(limit))
  53. if err != nil {
  54. return 0, err
  55. }
  56. return rInt.Int64(), nil
  57. }
  58. const alphanumericalChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  59. // CryptoRandomString generates a crypto random alphanumerical string, each byte is generated by [0,61] range
  60. func CryptoRandomString(length int64) (string, error) {
  61. buf := make([]byte, length)
  62. limit := int64(len(alphanumericalChars))
  63. for i := range buf {
  64. num, err := CryptoRandomInt(limit)
  65. if err != nil {
  66. return "", err
  67. }
  68. buf[i] = alphanumericalChars[num]
  69. }
  70. return string(buf), nil
  71. }
  72. // CryptoRandomBytes generates `length` crypto bytes
  73. // This differs from CryptoRandomString, as each byte in CryptoRandomString is generated by [0,61] range
  74. // This function generates totally random bytes, each byte is generated by [0,255] range
  75. func CryptoRandomBytes(length int64) ([]byte, error) {
  76. buf := make([]byte, length)
  77. _, err := rand.Read(buf)
  78. return buf, err
  79. }
  80. // ToUpperASCII returns s with all ASCII letters mapped to their upper case.
  81. func ToUpperASCII(s string) string {
  82. b := []byte(s)
  83. for i, c := range b {
  84. if 'a' <= c && c <= 'z' {
  85. b[i] -= 'a' - 'A'
  86. }
  87. }
  88. return string(b)
  89. }
  90. // ToTitleCase returns s with all english words capitalized
  91. func ToTitleCase(s string) string {
  92. // `cases.Title` is not thread-safe, do not use global shared variable for it
  93. return cases.Title(language.English).String(s)
  94. }
  95. // ToTitleCaseNoLower returns s with all english words capitalized without lower-casing
  96. func ToTitleCaseNoLower(s string) string {
  97. // `cases.Title` is not thread-safe, do not use global shared variable for it
  98. return cases.Title(language.English, cases.NoLower).String(s)
  99. }
  100. // ToInt64 transform a given int into int64.
  101. func ToInt64(number any) (int64, error) {
  102. var value int64
  103. switch v := number.(type) {
  104. case int:
  105. value = int64(v)
  106. case int8:
  107. value = int64(v)
  108. case int16:
  109. value = int64(v)
  110. case int32:
  111. value = int64(v)
  112. case int64:
  113. value = v
  114. case uint:
  115. value = int64(v)
  116. case uint8:
  117. value = int64(v)
  118. case uint16:
  119. value = int64(v)
  120. case uint32:
  121. value = int64(v)
  122. case uint64:
  123. value = int64(v)
  124. case float32:
  125. value = int64(v)
  126. case float64:
  127. value = int64(v)
  128. case string:
  129. var err error
  130. if value, err = strconv.ParseInt(v, 10, 64); err != nil {
  131. return 0, err
  132. }
  133. default:
  134. return 0, fmt.Errorf("unable to convert %v to int64", number)
  135. }
  136. return value, nil
  137. }
  138. // ToFloat64 transform a given int into float64.
  139. func ToFloat64(number any) (float64, error) {
  140. var value float64
  141. switch v := number.(type) {
  142. case int:
  143. value = float64(v)
  144. case int8:
  145. value = float64(v)
  146. case int16:
  147. value = float64(v)
  148. case int32:
  149. value = float64(v)
  150. case int64:
  151. value = float64(v)
  152. case uint:
  153. value = float64(v)
  154. case uint8:
  155. value = float64(v)
  156. case uint16:
  157. value = float64(v)
  158. case uint32:
  159. value = float64(v)
  160. case uint64:
  161. value = float64(v)
  162. case float32:
  163. value = float64(v)
  164. case float64:
  165. value = v
  166. case string:
  167. var err error
  168. if value, err = strconv.ParseFloat(v, 64); err != nil {
  169. return 0, err
  170. }
  171. default:
  172. return 0, fmt.Errorf("unable to convert %v to float64", number)
  173. }
  174. return value, nil
  175. }
  176. // ToPointer returns the pointer of a copy of any given value
  177. func ToPointer[T any](val T) *T {
  178. return &val
  179. }
  180. // Iif is an "inline-if", it returns "trueVal" if "condition" is true, otherwise "falseVal"
  181. func Iif[T any](condition bool, trueVal, falseVal T) T {
  182. if condition {
  183. return trueVal
  184. }
  185. return falseVal
  186. }
  187. // IfZero returns "def" if "v" is a zero value, otherwise "v"
  188. func IfZero[T comparable](v, def T) T {
  189. var zero T
  190. if v == zero {
  191. return def
  192. }
  193. return v
  194. }
  195. func IfEmpty[T any](v, def []T) []T {
  196. if len(v) == 0 {
  197. return def
  198. }
  199. return v
  200. }
  201. // OptionalArg helps the "optional argument" in Golang:
  202. //
  203. // func foo(optArg ...int) { return OptionalArg(optArg) }
  204. // calling `foo()` gets zero value 0, calling `foo(100)` gets 100
  205. // func bar(optArg ...int) { return OptionalArg(optArg, 42) }
  206. // calling `bar()` gets default value 42, calling `bar(100)` gets 100
  207. //
  208. // Passing more than 1 item to `optArg` or `defaultValue` is undefined behavior.
  209. // At the moment only the first item is used.
  210. func OptionalArg[T any](optArg []T, defaultValue ...T) (ret T) {
  211. if len(optArg) >= 1 {
  212. return optArg[0]
  213. }
  214. if len(defaultValue) >= 1 {
  215. return defaultValue[0]
  216. }
  217. return ret
  218. }
  219. func ReserveLineBreakForTextarea(input string) string {
  220. // Since the content is from a form which is a textarea, the line endings are \r\n.
  221. // It's a standard behavior of HTML.
  222. // But we want to store them as \n like what GitHub does.
  223. // And users are unlikely to really need to keep the \r.
  224. // Other than this, we should respect the original content, even leading or trailing spaces.
  225. return strings.ReplaceAll(input, "\r\n", "\n")
  226. }