mirror of https://github.com/docker/cli.git
319 lines
7.3 KiB
Go
319 lines
7.3 KiB
Go
|
// 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"
|
||
|
)
|
||
|
|
||
|
// Taken from https://github.com/golang/lint/blob/1fab560e16097e5b69afb66eb93aab843ef77845/lint.go#L663-L698
|
||
|
var commonInitialisms = map[string]bool{
|
||
|
"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,
|
||
|
"QPS": true,
|
||
|
"RAM": true,
|
||
|
"RHS": true,
|
||
|
"RPC": true,
|
||
|
"SLA": true,
|
||
|
"SMTP": true,
|
||
|
"SSH": true,
|
||
|
"TCP": true,
|
||
|
"TLS": true,
|
||
|
"TTL": true,
|
||
|
"UDP": true,
|
||
|
"UUID": true,
|
||
|
"UID": true,
|
||
|
"UI": true,
|
||
|
"URI": true,
|
||
|
"URL": true,
|
||
|
"UTF8": true,
|
||
|
"VM": true,
|
||
|
"XML": true,
|
||
|
"XSRF": true,
|
||
|
"XSS": true,
|
||
|
}
|
||
|
var initialisms []string
|
||
|
|
||
|
func init() {
|
||
|
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
|
||
|
|
||
|
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))
|
||
|
}
|
||
|
|
||
|
// 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 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)
|
||
|
}
|
||
|
return strings.Join(out, "")
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
}
|
||
|
|
||
|
// CommandLineOptionsGroup represents a group of user-defined command line options
|
||
|
type CommandLineOptionsGroup struct {
|
||
|
ShortDescription string
|
||
|
LongDescription string
|
||
|
Options interface{}
|
||
|
}
|