...

Source file src/github.com/kelseyhightower/envconfig/usage.go

Documentation: github.com/kelseyhightower/envconfig

     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 specifed 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  }
   165  

View as plain text