util.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  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. )
  20. // commonInitialisms are common acronyms that are kept as whole uppercased words.
  21. var commonInitialisms *indexOfInitialisms
  22. // initialisms is a slice of sorted initialisms
  23. var initialisms []string
  24. var isInitialism func(string) bool
  25. // GoNamePrefixFunc sets an optional rule to prefix go names
  26. // which do not start with a letter.
  27. //
  28. // e.g. to help convert "123" into "{prefix}123"
  29. //
  30. // The default is to prefix with "X"
  31. var GoNamePrefixFunc func(string) string
  32. func init() {
  33. // Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
  34. var configuredInitialisms = map[string]bool{
  35. "ACL": true,
  36. "API": true,
  37. "ASCII": true,
  38. "CPU": true,
  39. "CSS": true,
  40. "DNS": true,
  41. "EOF": true,
  42. "GUID": true,
  43. "HTML": true,
  44. "HTTPS": true,
  45. "HTTP": true,
  46. "ID": true,
  47. "IP": true,
  48. "IPv4": true,
  49. "IPv6": true,
  50. "JSON": true,
  51. "LHS": true,
  52. "OAI": true,
  53. "QPS": true,
  54. "RAM": true,
  55. "RHS": true,
  56. "RPC": true,
  57. "SLA": true,
  58. "SMTP": true,
  59. "SQL": true,
  60. "SSH": true,
  61. "TCP": true,
  62. "TLS": true,
  63. "TTL": true,
  64. "UDP": true,
  65. "UI": true,
  66. "UID": true,
  67. "UUID": true,
  68. "URI": true,
  69. "URL": true,
  70. "UTF8": true,
  71. "VM": true,
  72. "XML": true,
  73. "XMPP": true,
  74. "XSRF": true,
  75. "XSS": true,
  76. }
  77. // a thread-safe index of initialisms
  78. commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms)
  79. initialisms = commonInitialisms.sorted()
  80. // a test function
  81. isInitialism = commonInitialisms.isInitialism
  82. }
  83. const (
  84. // collectionFormatComma = "csv"
  85. collectionFormatSpace = "ssv"
  86. collectionFormatTab = "tsv"
  87. collectionFormatPipe = "pipes"
  88. collectionFormatMulti = "multi"
  89. )
  90. // JoinByFormat joins a string array by a known format (e.g. swagger's collectionFormat attribute):
  91. //
  92. // ssv: space separated value
  93. // tsv: tab separated value
  94. // pipes: pipe (|) separated value
  95. // csv: comma separated value (default)
  96. func JoinByFormat(data []string, format string) []string {
  97. if len(data) == 0 {
  98. return data
  99. }
  100. var sep string
  101. switch format {
  102. case collectionFormatSpace:
  103. sep = " "
  104. case collectionFormatTab:
  105. sep = "\t"
  106. case collectionFormatPipe:
  107. sep = "|"
  108. case collectionFormatMulti:
  109. return data
  110. default:
  111. sep = ","
  112. }
  113. return []string{strings.Join(data, sep)}
  114. }
  115. // SplitByFormat splits a string by a known format:
  116. //
  117. // ssv: space separated value
  118. // tsv: tab separated value
  119. // pipes: pipe (|) separated value
  120. // csv: comma separated value (default)
  121. func SplitByFormat(data, format string) []string {
  122. if data == "" {
  123. return nil
  124. }
  125. var sep string
  126. switch format {
  127. case collectionFormatSpace:
  128. sep = " "
  129. case collectionFormatTab:
  130. sep = "\t"
  131. case collectionFormatPipe:
  132. sep = "|"
  133. case collectionFormatMulti:
  134. return nil
  135. default:
  136. sep = ","
  137. }
  138. var result []string
  139. for _, s := range strings.Split(data, sep) {
  140. if ts := strings.TrimSpace(s); ts != "" {
  141. result = append(result, ts)
  142. }
  143. }
  144. return result
  145. }
  146. type byInitialism []string
  147. func (s byInitialism) Len() int {
  148. return len(s)
  149. }
  150. func (s byInitialism) Swap(i, j int) {
  151. s[i], s[j] = s[j], s[i]
  152. }
  153. func (s byInitialism) Less(i, j int) bool {
  154. if len(s[i]) != len(s[j]) {
  155. return len(s[i]) < len(s[j])
  156. }
  157. return strings.Compare(s[i], s[j]) > 0
  158. }
  159. // Removes leading whitespaces
  160. func trim(str string) string {
  161. return strings.Trim(str, " ")
  162. }
  163. // Shortcut to strings.ToUpper()
  164. func upper(str string) string {
  165. return strings.ToUpper(trim(str))
  166. }
  167. // Shortcut to strings.ToLower()
  168. func lower(str string) string {
  169. return strings.ToLower(trim(str))
  170. }
  171. // Camelize an uppercased word
  172. func Camelize(word string) (camelized string) {
  173. for pos, ru := range []rune(word) {
  174. if pos > 0 {
  175. camelized += string(unicode.ToLower(ru))
  176. } else {
  177. camelized += string(unicode.ToUpper(ru))
  178. }
  179. }
  180. return
  181. }
  182. // ToFileName lowercases and underscores a go type name
  183. func ToFileName(name string) string {
  184. in := split(name)
  185. out := make([]string, 0, len(in))
  186. for _, w := range in {
  187. out = append(out, lower(w))
  188. }
  189. return strings.Join(out, "_")
  190. }
  191. // ToCommandName lowercases and underscores a go type name
  192. func ToCommandName(name string) string {
  193. in := split(name)
  194. out := make([]string, 0, len(in))
  195. for _, w := range in {
  196. out = append(out, lower(w))
  197. }
  198. return strings.Join(out, "-")
  199. }
  200. // ToHumanNameLower represents a code name as a human series of words
  201. func ToHumanNameLower(name string) string {
  202. in := newSplitter(withPostSplitInitialismCheck).split(name)
  203. out := make([]string, 0, len(in))
  204. for _, w := range in {
  205. if !w.IsInitialism() {
  206. out = append(out, lower(w.GetOriginal()))
  207. } else {
  208. out = append(out, w.GetOriginal())
  209. }
  210. }
  211. return strings.Join(out, " ")
  212. }
  213. // ToHumanNameTitle represents a code name as a human series of words with the first letters titleized
  214. func ToHumanNameTitle(name string) string {
  215. in := newSplitter(withPostSplitInitialismCheck).split(name)
  216. out := make([]string, 0, len(in))
  217. for _, w := range in {
  218. original := w.GetOriginal()
  219. if !w.IsInitialism() {
  220. out = append(out, Camelize(original))
  221. } else {
  222. out = append(out, original)
  223. }
  224. }
  225. return strings.Join(out, " ")
  226. }
  227. // ToJSONName camelcases a name which can be underscored or pascal cased
  228. func ToJSONName(name string) string {
  229. in := split(name)
  230. out := make([]string, 0, len(in))
  231. for i, w := range in {
  232. if i == 0 {
  233. out = append(out, lower(w))
  234. continue
  235. }
  236. out = append(out, Camelize(w))
  237. }
  238. return strings.Join(out, "")
  239. }
  240. // ToVarName camelcases a name which can be underscored or pascal cased
  241. func ToVarName(name string) string {
  242. res := ToGoName(name)
  243. if isInitialism(res) {
  244. return lower(res)
  245. }
  246. if len(res) <= 1 {
  247. return lower(res)
  248. }
  249. return lower(res[:1]) + res[1:]
  250. }
  251. // ToGoName translates a swagger name which can be underscored or camel cased to a name that golint likes
  252. func ToGoName(name string) string {
  253. lexems := newSplitter(withPostSplitInitialismCheck).split(name)
  254. result := ""
  255. for _, lexem := range lexems {
  256. goName := lexem.GetUnsafeGoName()
  257. // to support old behavior
  258. if lexem.IsInitialism() {
  259. goName = upper(goName)
  260. }
  261. result += goName
  262. }
  263. if len(result) > 0 {
  264. // Only prefix with X when the first character isn't an ascii letter
  265. first := []rune(result)[0]
  266. if !unicode.IsLetter(first) || (first > unicode.MaxASCII && !unicode.IsUpper(first)) {
  267. if GoNamePrefixFunc == nil {
  268. return "X" + result
  269. }
  270. result = GoNamePrefixFunc(name) + result
  271. }
  272. first = []rune(result)[0]
  273. if unicode.IsLetter(first) && !unicode.IsUpper(first) {
  274. result = string(append([]rune{unicode.ToUpper(first)}, []rune(result)[1:]...))
  275. }
  276. }
  277. return result
  278. }
  279. // ContainsStrings searches a slice of strings for a case-sensitive match
  280. func ContainsStrings(coll []string, item string) bool {
  281. for _, a := range coll {
  282. if a == item {
  283. return true
  284. }
  285. }
  286. return false
  287. }
  288. // ContainsStringsCI searches a slice of strings for a case-insensitive match
  289. func ContainsStringsCI(coll []string, item string) bool {
  290. for _, a := range coll {
  291. if strings.EqualFold(a, item) {
  292. return true
  293. }
  294. }
  295. return false
  296. }
  297. type zeroable interface {
  298. IsZero() bool
  299. }
  300. // IsZero returns true when the value passed into the function is a zero value.
  301. // This allows for safer checking of interface values.
  302. func IsZero(data interface{}) bool {
  303. // check for things that have an IsZero method instead
  304. if vv, ok := data.(zeroable); ok {
  305. return vv.IsZero()
  306. }
  307. // continue with slightly more complex reflection
  308. v := reflect.ValueOf(data)
  309. switch v.Kind() {
  310. case reflect.String:
  311. return v.Len() == 0
  312. case reflect.Bool:
  313. return !v.Bool()
  314. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  315. return v.Int() == 0
  316. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  317. return v.Uint() == 0
  318. case reflect.Float32, reflect.Float64:
  319. return v.Float() == 0
  320. case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
  321. return v.IsNil()
  322. case reflect.Struct, reflect.Array:
  323. return reflect.DeepEqual(data, reflect.Zero(v.Type()).Interface())
  324. case reflect.Invalid:
  325. return true
  326. }
  327. return false
  328. }
  329. // AddInitialisms add additional initialisms
  330. func AddInitialisms(words ...string) {
  331. for _, word := range words {
  332. // commonInitialisms[upper(word)] = true
  333. commonInitialisms.add(upper(word))
  334. }
  335. // sort again
  336. initialisms = commonInitialisms.sorted()
  337. }
  338. // CommandLineOptionsGroup represents a group of user-defined command line options
  339. type CommandLineOptionsGroup struct {
  340. ShortDescription string
  341. LongDescription string
  342. Options interface{}
  343. }