| // Copyright 2015 go-swagger maintainers |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package swag |
| |
| import ( |
| "math" |
| "reflect" |
| "regexp" |
| "sort" |
| "strings" |
| "sync" |
| "unicode" |
| ) |
| |
| // Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769 |
| var commonInitialisms = map[string]bool{ |
| "ACL": true, |
| "API": true, |
| "ASCII": true, |
| "CPU": true, |
| "CSS": true, |
| "DNS": true, |
| "EOF": true, |
| "GUID": true, |
| "HTML": true, |
| "HTTPS": true, |
| "HTTP": true, |
| "ID": true, |
| "IP": true, |
| "JSON": true, |
| "LHS": true, |
| "OAI": true, |
| "QPS": true, |
| "RAM": true, |
| "RHS": true, |
| "RPC": true, |
| "SLA": true, |
| "SMTP": true, |
| "SQL": true, |
| "SSH": true, |
| "TCP": true, |
| "TLS": true, |
| "TTL": true, |
| "UDP": true, |
| "UI": true, |
| "UID": true, |
| "UUID": true, |
| "URI": true, |
| "URL": true, |
| "UTF8": true, |
| "VM": true, |
| "XML": true, |
| "XMPP": true, |
| "XSRF": true, |
| "XSS": true, |
| } |
| var initialisms []string |
| |
| var once sync.Once |
| |
| func sortInitialisms() { |
| for k := range commonInitialisms { |
| initialisms = append(initialisms, k) |
| } |
| sort.Sort(sort.Reverse(byLength(initialisms))) |
| } |
| |
| // JoinByFormat joins a string array by a known format: |
| // ssv: space separated value |
| // tsv: tab separated value |
| // pipes: pipe (|) separated value |
| // csv: comma separated value (default) |
| func JoinByFormat(data []string, format string) []string { |
| if len(data) == 0 { |
| return data |
| } |
| var sep string |
| switch format { |
| case "ssv": |
| sep = " " |
| case "tsv": |
| sep = "\t" |
| case "pipes": |
| sep = "|" |
| case "multi": |
| return data |
| default: |
| sep = "," |
| } |
| return []string{strings.Join(data, sep)} |
| } |
| |
| // SplitByFormat splits a string by a known format: |
| // ssv: space separated value |
| // tsv: tab separated value |
| // pipes: pipe (|) separated value |
| // csv: comma separated value (default) |
| func SplitByFormat(data, format string) []string { |
| if data == "" { |
| return nil |
| } |
| var sep string |
| switch format { |
| case "ssv": |
| sep = " " |
| case "tsv": |
| sep = "\t" |
| case "pipes": |
| sep = "|" |
| case "multi": |
| return nil |
| default: |
| sep = "," |
| } |
| var result []string |
| for _, s := range strings.Split(data, sep) { |
| if ts := strings.TrimSpace(s); ts != "" { |
| result = append(result, ts) |
| } |
| } |
| return result |
| } |
| |
| type byLength []string |
| |
| func (s byLength) Len() int { |
| return len(s) |
| } |
| func (s byLength) Swap(i, j int) { |
| s[i], s[j] = s[j], s[i] |
| } |
| func (s byLength) Less(i, j int) bool { |
| return len(s[i]) < len(s[j]) |
| } |
| |
| // Prepares strings by splitting by caps, spaces, dashes, and underscore |
| func split(str string) (words []string) { |
| repl := strings.NewReplacer( |
| "@", "At ", |
| "&", "And ", |
| "|", "Pipe ", |
| "$", "Dollar ", |
| "!", "Bang ", |
| "-", " ", |
| "_", " ", |
| ) |
| |
| rex1 := regexp.MustCompile(`(\p{Lu})`) |
| rex2 := regexp.MustCompile(`(\pL|\pM|\pN|\p{Pc})+`) |
| |
| str = trim(str) |
| |
| // Convert dash and underscore to spaces |
| str = repl.Replace(str) |
| |
| // Split when uppercase is found (needed for Snake) |
| str = rex1.ReplaceAllString(str, " $1") |
| |
| // check if consecutive single char things make up an initialism |
| once.Do(sortInitialisms) |
| for _, k := range initialisms { |
| str = strings.Replace(str, rex1.ReplaceAllString(k, " $1"), " "+k, -1) |
| } |
| // Get the final list of words |
| words = rex2.FindAllString(str, -1) |
| |
| return |
| } |
| |
| // Removes leading whitespaces |
| func trim(str string) string { |
| return strings.Trim(str, " ") |
| } |
| |
| // Shortcut to strings.ToUpper() |
| func upper(str string) string { |
| return strings.ToUpper(trim(str)) |
| } |
| |
| // Shortcut to strings.ToLower() |
| func lower(str string) string { |
| return strings.ToLower(trim(str)) |
| } |
| |
| // Camelize an uppercased word |
| func Camelize(word string) (camelized string) { |
| for pos, ru := range word { |
| if pos > 0 { |
| camelized += string(unicode.ToLower(ru)) |
| } else { |
| camelized += string(unicode.ToUpper(ru)) |
| } |
| } |
| return |
| } |
| |
| // ToFileName lowercases and underscores a go type name |
| func ToFileName(name string) string { |
| var out []string |
| |
| for _, w := range split(name) { |
| out = append(out, lower(w)) |
| } |
| |
| return strings.Join(out, "_") |
| } |
| |
| // ToCommandName lowercases and underscores a go type name |
| func ToCommandName(name string) string { |
| var out []string |
| for _, w := range split(name) { |
| out = append(out, lower(w)) |
| } |
| return strings.Join(out, "-") |
| } |
| |
| // ToHumanNameLower represents a code name as a human series of words |
| func ToHumanNameLower(name string) string { |
| var out []string |
| for _, w := range split(name) { |
| if !commonInitialisms[upper(w)] { |
| out = append(out, lower(w)) |
| } else { |
| out = append(out, w) |
| } |
| } |
| return strings.Join(out, " ") |
| } |
| |
| // ToHumanNameTitle represents a code name as a human series of words with the first letters titleized |
| func ToHumanNameTitle(name string) string { |
| var out []string |
| for _, w := range split(name) { |
| uw := upper(w) |
| if !commonInitialisms[uw] { |
| out = append(out, upper(w[:1])+lower(w[1:])) |
| } else { |
| out = append(out, w) |
| } |
| } |
| return strings.Join(out, " ") |
| } |
| |
| // ToJSONName camelcases a name which can be underscored or pascal cased |
| func ToJSONName(name string) string { |
| var out []string |
| for i, w := range split(name) { |
| if i == 0 { |
| out = append(out, lower(w)) |
| continue |
| } |
| out = append(out, upper(w[:1])+lower(w[1:])) |
| } |
| return strings.Join(out, "") |
| } |
| |
| // ToVarName camelcases a name which can be underscored or pascal cased |
| func ToVarName(name string) string { |
| res := ToGoName(name) |
| if _, ok := commonInitialisms[res]; ok { |
| return lower(res) |
| } |
| if len(res) <= 1 { |
| return lower(res) |
| } |
| return lower(res[:1]) + res[1:] |
| } |
| |
| // ToGoName translates a swagger name which can be underscored or camel cased to a name that golint likes |
| func ToGoName(name string) string { |
| var out []string |
| for _, w := range split(name) { |
| uw := upper(w) |
| mod := int(math.Min(float64(len(uw)), 2)) |
| if !commonInitialisms[uw] && !commonInitialisms[uw[:len(uw)-mod]] { |
| uw = upper(w[:1]) + lower(w[1:]) |
| } |
| out = append(out, uw) |
| } |
| |
| result := strings.Join(out, "") |
| if len(result) > 0 { |
| ud := upper(result[:1]) |
| ru := []rune(ud) |
| if unicode.IsUpper(ru[0]) { |
| result = ud + result[1:] |
| } else { |
| result = "X" + ud + result[1:] |
| } |
| } |
| return result |
| } |
| |
| // ContainsStringsCI searches a slice of strings for a case-insensitive match |
| func ContainsStringsCI(coll []string, item string) bool { |
| for _, a := range coll { |
| if strings.EqualFold(a, item) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| type zeroable interface { |
| IsZero() bool |
| } |
| |
| // IsZero returns true when the value passed into the function is a zero value. |
| // This allows for safer checking of interface values. |
| func IsZero(data interface{}) bool { |
| // check for things that have an IsZero method instead |
| if vv, ok := data.(zeroable); ok { |
| return vv.IsZero() |
| } |
| // continue with slightly more complex reflection |
| v := reflect.ValueOf(data) |
| switch v.Kind() { |
| case reflect.String: |
| return v.Len() == 0 |
| case reflect.Bool: |
| return !v.Bool() |
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| return v.Int() == 0 |
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
| return v.Uint() == 0 |
| case reflect.Float32, reflect.Float64: |
| return v.Float() == 0 |
| case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: |
| return v.IsNil() |
| case reflect.Struct, reflect.Array: |
| return reflect.DeepEqual(data, reflect.Zero(v.Type()).Interface()) |
| case reflect.Invalid: |
| return true |
| } |
| return false |
| } |
| |
| // AddInitialisms add additional initialisms |
| func AddInitialisms(words ...string) { |
| for _, word := range words { |
| commonInitialisms[upper(word)] = true |
| } |
| } |
| |
| // CommandLineOptionsGroup represents a group of user-defined command line options |
| type CommandLineOptionsGroup struct { |
| ShortDescription string |
| LongDescription string |
| Options interface{} |
| } |