github.com/unionj-cloud/go-doudou/v2@v2.3.5/toolkit/envconfig/usage.go (about) 1 // Copyright (c) 2016 Kelsey Hightower and others. All rights reserved. 2 // Use of this source code is governed by the MIT License that can be found in 3 // the LICENSE file. 4 5 package envconfig 6 7 import ( 8 "encoding" 9 "fmt" 10 "io" 11 "os" 12 "reflect" 13 "strconv" 14 "strings" 15 "text/tabwriter" 16 "text/template" 17 ) 18 19 const ( 20 // DefaultListFormat constant to use to display usage in a list format 21 DefaultListFormat = `This application is configured via the environment. The following environment 22 variables can be used: 23 {{range .}} 24 {{usage_key .}} 25 [description] {{usage_description .}} 26 [type] {{usage_type .}} 27 [default] {{usage_default .}} 28 [required] {{usage_required .}}{{end}} 29 ` 30 // DefaultTableFormat constant to use to display usage in a tabular format 31 DefaultTableFormat = `This application is configured via the environment. The following environment 32 variables can be used: 33 34 KEY TYPE DEFAULT REQUIRED DESCRIPTION 35 {{range .}}{{usage_key .}} {{usage_type .}} {{usage_default .}} {{usage_required .}} {{usage_description .}} 36 {{end}}` 37 ) 38 39 var ( 40 decoderType = reflect.TypeOf((*Decoder)(nil)).Elem() 41 setterType = reflect.TypeOf((*Setter)(nil)).Elem() 42 textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() 43 binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() 44 ) 45 46 func implementsInterface(t reflect.Type) bool { 47 return t.Implements(decoderType) || 48 reflect.PtrTo(t).Implements(decoderType) || 49 t.Implements(setterType) || 50 reflect.PtrTo(t).Implements(setterType) || 51 t.Implements(textUnmarshalerType) || 52 reflect.PtrTo(t).Implements(textUnmarshalerType) || 53 t.Implements(binaryUnmarshalerType) || 54 reflect.PtrTo(t).Implements(binaryUnmarshalerType) 55 } 56 57 // toTypeDescription converts Go types into a human readable description 58 func toTypeDescription(t reflect.Type) string { 59 switch t.Kind() { 60 case reflect.Array, reflect.Slice: 61 if t.Elem().Kind() == reflect.Uint8 { 62 return "String" 63 } 64 return fmt.Sprintf("Comma-separated list of %s", toTypeDescription(t.Elem())) 65 case reflect.Map: 66 return fmt.Sprintf( 67 "Comma-separated list of %s:%s pairs", 68 toTypeDescription(t.Key()), 69 toTypeDescription(t.Elem()), 70 ) 71 case reflect.Ptr: 72 return toTypeDescription(t.Elem()) 73 case reflect.Struct: 74 if implementsInterface(t) && t.Name() != "" { 75 return t.Name() 76 } 77 return "" 78 case reflect.String: 79 name := t.Name() 80 if name != "" && name != "string" { 81 return name 82 } 83 return "String" 84 case reflect.Bool: 85 name := t.Name() 86 if name != "" && name != "bool" { 87 return name 88 } 89 return "True or False" 90 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 91 name := t.Name() 92 if name != "" && !strings.HasPrefix(name, "int") { 93 return name 94 } 95 return "Integer" 96 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 97 name := t.Name() 98 if name != "" && !strings.HasPrefix(name, "uint") { 99 return name 100 } 101 return "Unsigned Integer" 102 case reflect.Float32, reflect.Float64: 103 name := t.Name() 104 if name != "" && !strings.HasPrefix(name, "float") { 105 return name 106 } 107 return "Float" 108 } 109 return fmt.Sprintf("%+v", t) 110 } 111 112 // Usage writes usage information to stdout using the default header and table format 113 func Usage(prefix string, spec interface{}) error { 114 // The default is to output the usage information as a table 115 // Create tabwriter instance to support table output 116 tabs := tabwriter.NewWriter(os.Stdout, 1, 0, 4, ' ', 0) 117 118 err := Usagef(prefix, spec, tabs, DefaultTableFormat) 119 tabs.Flush() 120 return err 121 } 122 123 // Usagef writes usage information to the specified io.Writer using the specified template specification 124 func Usagef(prefix string, spec interface{}, out io.Writer, format string) error { 125 126 // Specify the default usage template functions 127 functions := template.FuncMap{ 128 "usage_key": func(v varInfo) string { return v.Key }, 129 "usage_description": func(v varInfo) string { return v.Tags.Get("desc") }, 130 "usage_type": func(v varInfo) string { return toTypeDescription(v.Field.Type()) }, 131 "usage_default": func(v varInfo) string { return v.Tags.Get("default") }, 132 "usage_required": func(v varInfo) (string, error) { 133 req := v.Tags.Get("required") 134 if req != "" { 135 reqB, err := strconv.ParseBool(req) 136 if err != nil { 137 return "", err 138 } 139 if reqB { 140 req = "true" 141 } 142 } 143 return req, nil 144 }, 145 } 146 147 tmpl, err := template.New("envconfig").Funcs(functions).Parse(format) 148 if err != nil { 149 return err 150 } 151 152 return Usaget(prefix, spec, out, tmpl) 153 } 154 155 // Usaget writes usage information to the specified io.Writer using the specified template 156 func Usaget(prefix string, spec interface{}, out io.Writer, tmpl *template.Template) error { 157 // gather first 158 infos, err := gatherInfo(prefix, spec) 159 if err != nil { 160 return err 161 } 162 163 return tmpl.Execute(out, infos) 164 }