util.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // Copyright 2015 go-swagger maintainers
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package swag
  15. import (
  16. "reflect"
  17. "strings"
  18. "unicode"
  19. "unicode/utf8"
  20. )
  21. // GoNamePrefixFunc sets an optional rule to prefix go names
  22. // which do not start with a letter.
  23. //
  24. // The prefix function is assumed to return a string that starts with an upper case letter.
  25. //
  26. // e.g. to help convert "123" into "{prefix}123"
  27. //
  28. // The default is to prefix with "X"
  29. var GoNamePrefixFunc func(string) string
  30. func prefixFunc(name, in string) string {
  31. if GoNamePrefixFunc == nil {
  32. return "X" + in
  33. }
  34. return GoNamePrefixFunc(name) + in
  35. }
  36. const (
  37. // collectionFormatComma = "csv"
  38. collectionFormatSpace = "ssv"
  39. collectionFormatTab = "tsv"
  40. collectionFormatPipe = "pipes"
  41. collectionFormatMulti = "multi"
  42. )
  43. // JoinByFormat joins a string array by a known format (e.g. swagger's collectionFormat attribute):
  44. //
  45. // ssv: space separated value
  46. // tsv: tab separated value
  47. // pipes: pipe (|) separated value
  48. // csv: comma separated value (default)
  49. func JoinByFormat(data []string, format string) []string {
  50. if len(data) == 0 {
  51. return data
  52. }
  53. var sep string
  54. switch format {
  55. case collectionFormatSpace:
  56. sep = " "
  57. case collectionFormatTab:
  58. sep = "\t"
  59. case collectionFormatPipe:
  60. sep = "|"
  61. case collectionFormatMulti:
  62. return data
  63. default:
  64. sep = ","
  65. }
  66. return []string{strings.Join(data, sep)}
  67. }
  68. // SplitByFormat splits a string by a known format:
  69. //
  70. // ssv: space separated value
  71. // tsv: tab separated value
  72. // pipes: pipe (|) separated value
  73. // csv: comma separated value (default)
  74. func SplitByFormat(data, format string) []string {
  75. if data == "" {
  76. return nil
  77. }
  78. var sep string
  79. switch format {
  80. case collectionFormatSpace:
  81. sep = " "
  82. case collectionFormatTab:
  83. sep = "\t"
  84. case collectionFormatPipe:
  85. sep = "|"
  86. case collectionFormatMulti:
  87. return nil
  88. default:
  89. sep = ","
  90. }
  91. var result []string
  92. for _, s := range strings.Split(data, sep) {
  93. if ts := strings.TrimSpace(s); ts != "" {
  94. result = append(result, ts)
  95. }
  96. }
  97. return result
  98. }
  99. // Removes leading whitespaces
  100. func trim(str string) string {
  101. return strings.TrimSpace(str)
  102. }
  103. // Shortcut to strings.ToUpper()
  104. func upper(str string) string {
  105. return strings.ToUpper(trim(str))
  106. }
  107. // Shortcut to strings.ToLower()
  108. func lower(str string) string {
  109. return strings.ToLower(trim(str))
  110. }
  111. // Camelize an uppercased word
  112. func Camelize(word string) string {
  113. camelized := poolOfBuffers.BorrowBuffer(len(word))
  114. defer func() {
  115. poolOfBuffers.RedeemBuffer(camelized)
  116. }()
  117. for pos, ru := range []rune(word) {
  118. if pos > 0 {
  119. camelized.WriteRune(unicode.ToLower(ru))
  120. } else {
  121. camelized.WriteRune(unicode.ToUpper(ru))
  122. }
  123. }
  124. return camelized.String()
  125. }
  126. // ToFileName lowercases and underscores a go type name
  127. func ToFileName(name string) string {
  128. in := split(name)
  129. out := make([]string, 0, len(in))
  130. for _, w := range in {
  131. out = append(out, lower(w))
  132. }
  133. return strings.Join(out, "_")
  134. }
  135. // ToCommandName lowercases and underscores a go type name
  136. func ToCommandName(name string) string {
  137. in := split(name)
  138. out := make([]string, 0, len(in))
  139. for _, w := range in {
  140. out = append(out, lower(w))
  141. }
  142. return strings.Join(out, "-")
  143. }
  144. // ToHumanNameLower represents a code name as a human series of words
  145. func ToHumanNameLower(name string) string {
  146. s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck)
  147. in := s.split(name)
  148. poolOfSplitters.RedeemSplitter(s)
  149. out := make([]string, 0, len(*in))
  150. for _, w := range *in {
  151. if !w.IsInitialism() {
  152. out = append(out, lower(w.GetOriginal()))
  153. } else {
  154. out = append(out, trim(w.GetOriginal()))
  155. }
  156. }
  157. poolOfLexems.RedeemLexems(in)
  158. return strings.Join(out, " ")
  159. }
  160. // ToHumanNameTitle represents a code name as a human series of words with the first letters titleized
  161. func ToHumanNameTitle(name string) string {
  162. s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck)
  163. in := s.split(name)
  164. poolOfSplitters.RedeemSplitter(s)
  165. out := make([]string, 0, len(*in))
  166. for _, w := range *in {
  167. original := trim(w.GetOriginal())
  168. if !w.IsInitialism() {
  169. out = append(out, Camelize(original))
  170. } else {
  171. out = append(out, original)
  172. }
  173. }
  174. poolOfLexems.RedeemLexems(in)
  175. return strings.Join(out, " ")
  176. }
  177. // ToJSONName camelcases a name which can be underscored or pascal cased
  178. func ToJSONName(name string) string {
  179. in := split(name)
  180. out := make([]string, 0, len(in))
  181. for i, w := range in {
  182. if i == 0 {
  183. out = append(out, lower(w))
  184. continue
  185. }
  186. out = append(out, Camelize(trim(w)))
  187. }
  188. return strings.Join(out, "")
  189. }
  190. // ToVarName camelcases a name which can be underscored or pascal cased
  191. func ToVarName(name string) string {
  192. res := ToGoName(name)
  193. if isInitialism(res) {
  194. return lower(res)
  195. }
  196. if len(res) <= 1 {
  197. return lower(res)
  198. }
  199. return lower(res[:1]) + res[1:]
  200. }
  201. // ToGoName translates a swagger name which can be underscored or camel cased to a name that golint likes
  202. func ToGoName(name string) string {
  203. s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck)
  204. lexems := s.split(name)
  205. poolOfSplitters.RedeemSplitter(s)
  206. defer func() {
  207. poolOfLexems.RedeemLexems(lexems)
  208. }()
  209. lexemes := *lexems
  210. if len(lexemes) == 0 {
  211. return ""
  212. }
  213. result := poolOfBuffers.BorrowBuffer(len(name))
  214. defer func() {
  215. poolOfBuffers.RedeemBuffer(result)
  216. }()
  217. // check if not starting with a letter, upper case
  218. firstPart := lexemes[0].GetUnsafeGoName()
  219. if lexemes[0].IsInitialism() {
  220. firstPart = upper(firstPart)
  221. }
  222. if c := firstPart[0]; c < utf8.RuneSelf {
  223. // ASCII
  224. switch {
  225. case 'A' <= c && c <= 'Z':
  226. result.WriteString(firstPart)
  227. case 'a' <= c && c <= 'z':
  228. result.WriteByte(c - 'a' + 'A')
  229. result.WriteString(firstPart[1:])
  230. default:
  231. result.WriteString(prefixFunc(name, firstPart))
  232. // NOTE: no longer check if prefixFunc returns a string that starts with uppercase:
  233. // assume this is always the case
  234. }
  235. } else {
  236. // unicode
  237. firstRune, _ := utf8.DecodeRuneInString(firstPart)
  238. switch {
  239. case !unicode.IsLetter(firstRune):
  240. result.WriteString(prefixFunc(name, firstPart))
  241. case !unicode.IsUpper(firstRune):
  242. result.WriteString(prefixFunc(name, firstPart))
  243. /*
  244. result.WriteRune(unicode.ToUpper(firstRune))
  245. result.WriteString(firstPart[offset:])
  246. */
  247. default:
  248. result.WriteString(firstPart)
  249. }
  250. }
  251. for _, lexem := range lexemes[1:] {
  252. goName := lexem.GetUnsafeGoName()
  253. // to support old behavior
  254. if lexem.IsInitialism() {
  255. goName = upper(goName)
  256. }
  257. result.WriteString(goName)
  258. }
  259. return result.String()
  260. }
  261. // ContainsStrings searches a slice of strings for a case-sensitive match
  262. func ContainsStrings(coll []string, item string) bool {
  263. for _, a := range coll {
  264. if a == item {
  265. return true
  266. }
  267. }
  268. return false
  269. }
  270. // ContainsStringsCI searches a slice of strings for a case-insensitive match
  271. func ContainsStringsCI(coll []string, item string) bool {
  272. for _, a := range coll {
  273. if strings.EqualFold(a, item) {
  274. return true
  275. }
  276. }
  277. return false
  278. }
  279. type zeroable interface {
  280. IsZero() bool
  281. }
  282. // IsZero returns true when the value passed into the function is a zero value.
  283. // This allows for safer checking of interface values.
  284. func IsZero(data interface{}) bool {
  285. v := reflect.ValueOf(data)
  286. // check for nil data
  287. switch v.Kind() { //nolint:exhaustive
  288. case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
  289. if v.IsNil() {
  290. return true
  291. }
  292. }
  293. // check for things that have an IsZero method instead
  294. if vv, ok := data.(zeroable); ok {
  295. return vv.IsZero()
  296. }
  297. // continue with slightly more complex reflection
  298. switch v.Kind() { //nolint:exhaustive
  299. case reflect.String:
  300. return v.Len() == 0
  301. case reflect.Bool:
  302. return !v.Bool()
  303. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  304. return v.Int() == 0
  305. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  306. return v.Uint() == 0
  307. case reflect.Float32, reflect.Float64:
  308. return v.Float() == 0
  309. case reflect.Struct, reflect.Array:
  310. return reflect.DeepEqual(data, reflect.Zero(v.Type()).Interface())
  311. case reflect.Invalid:
  312. return true
  313. default:
  314. return false
  315. }
  316. }
  317. // CommandLineOptionsGroup represents a group of user-defined command line options
  318. type CommandLineOptionsGroup struct {
  319. ShortDescription string
  320. LongDescription string
  321. Options interface{}
  322. }