// 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 ( "reflect" "strings" "unicode" ) // commonInitialisms are common acronyms that are kept as whole uppercased words. var commonInitialisms *indexOfInitialisms // initialisms is a slice of sorted initialisms var initialisms []string var isInitialism func(string) bool // GoNamePrefixFunc sets an optional rule to prefix go names // which do not start with a letter. // // e.g. to help converting "123" into "{prefix}123" // // The default is to prefix with "X" var GoNamePrefixFunc func(string) string func init() { // Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769 var configuredInitialisms = 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, "IPv4": true, "IPv6": 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, } // a thread-safe index of initialisms commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms) initialisms = commonInitialisms.sorted() // a test function isInitialism = commonInitialisms.isInitialism } const ( //collectionFormatComma = "csv" collectionFormatSpace = "ssv" collectionFormatTab = "tsv" collectionFormatPipe = "pipes" collectionFormatMulti = "multi" ) // JoinByFormat joins a string array by a known format (e.g. swagger's collectionFormat attribute): // 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 collectionFormatSpace: sep = " " case collectionFormatTab: sep = "\t" case collectionFormatPipe: sep = "|" case collectionFormatMulti: 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 collectionFormatSpace: sep = " " case collectionFormatTab: sep = "\t" case collectionFormatPipe: sep = "|" case collectionFormatMulti: 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 byInitialism []string func (s byInitialism) Len() int { return len(s) } func (s byInitialism) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s byInitialism) Less(i, j int) bool { if len(s[i]) != len(s[j]) { return len(s[i]) < len(s[j]) } return strings.Compare(s[i], s[j]) > 0 } // 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 []rune(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 { in := split(name) out := make([]string, 0, len(in)) for _, w := range in { out = append(out, lower(w)) } return strings.Join(out, "_") } // ToCommandName lowercases and underscores a go type name func ToCommandName(name string) string { in := split(name) out := make([]string, 0, len(in)) for _, w := range in { 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 { in := newSplitter(withPostSplitInitialismCheck).split(name) out := make([]string, 0, len(in)) for _, w := range in { if !w.IsInitialism() { out = append(out, lower(w.GetOriginal())) } else { out = append(out, w.GetOriginal()) } } 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 { in := newSplitter(withPostSplitInitialismCheck).split(name) out := make([]string, 0, len(in)) for _, w := range in { original := w.GetOriginal() if !w.IsInitialism() { out = append(out, Camelize(original)) } else { out = append(out, original) } } return strings.Join(out, " ") } // ToJSONName camelcases a name which can be underscored or pascal cased func ToJSONName(name string) string { in := split(name) out := make([]string, 0, len(in)) for i, w := range in { if i == 0 { out = append(out, lower(w)) continue } out = append(out, Camelize(w)) } return strings.Join(out, "") } // ToVarName camelcases a name which can be underscored or pascal cased func ToVarName(name string) string { res := ToGoName(name) if isInitialism(res) { 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 { lexems := newSplitter(withPostSplitInitialismCheck).split(name) result := "" for _, lexem := range lexems { goName := lexem.GetUnsafeGoName() // to support old behavior if lexem.IsInitialism() { goName = upper(goName) } result += goName } if len(result) > 0 { // Only prefix with X when the first character isn't an ascii letter first := []rune(result)[0] if !unicode.IsLetter(first) || (first > unicode.MaxASCII && !unicode.IsUpper(first)) { if GoNamePrefixFunc == nil { return "X" + result } result = GoNamePrefixFunc(name) + result } first = []rune(result)[0] if unicode.IsLetter(first) && !unicode.IsUpper(first) { result = string(append([]rune{unicode.ToUpper(first)}, []rune(result)[1:]...)) } } return result } // ContainsStrings searches a slice of strings for a case-sensitive match func ContainsStrings(coll []string, item string) bool { for _, a := range coll { if a == item { return true } } return false } // 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 commonInitialisms.add(upper(word)) } // sort again initialisms = commonInitialisms.sorted() } // CommandLineOptionsGroup represents a group of user-defined command line options type CommandLineOptionsGroup struct { ShortDescription string LongDescription string Options interface{} }