...

Source file src/github.com/peterbourgon/ff/v3/fftoml/fftoml.go

Documentation: github.com/peterbourgon/ff/v3/fftoml

     1  // Package fftoml provides a TOML config file paser.
     2  package fftoml
     3  
     4  import (
     5  	"fmt"
     6  	"io"
     7  	"strconv"
     8  
     9  	"github.com/pelletier/go-toml"
    10  	"github.com/peterbourgon/ff/v3"
    11  )
    12  
    13  // Parser is a parser for TOML file format. Flags and their values are read
    14  // from the key/value pairs defined in the config file.
    15  func Parser(r io.Reader, set func(name, value string) error) error {
    16  	return New().Parse(r, set)
    17  }
    18  
    19  // ConfigFileParser is a parser for the TOML file format. Flags and their values
    20  // are read from the key/value pairs defined in the config file.
    21  // Nested tables and keys are concatenated with a delimiter to derive the
    22  // relevant flag name.
    23  type ConfigFileParser struct {
    24  	delimiter string
    25  }
    26  
    27  // New constructs and configures a ConfigFileParser using the provided options.
    28  func New(opts ...Option) (c ConfigFileParser) {
    29  	c.delimiter = "."
    30  	for _, opt := range opts {
    31  		opt(&c)
    32  	}
    33  	return c
    34  }
    35  
    36  // Parse parses the provided io.Reader as a TOML file and uses the provided set function
    37  // to set flag names derived from the tables names and their key/value pairs.
    38  func (c ConfigFileParser) Parse(r io.Reader, set func(name, value string) error) error {
    39  	tree, err := toml.LoadReader(r)
    40  	if err != nil {
    41  		return ParseError{Inner: err}
    42  	}
    43  
    44  	return parseTree(tree, "", c.delimiter, set)
    45  }
    46  
    47  // Option is a function which changes the behavior of the TOML config file parser.
    48  type Option func(*ConfigFileParser)
    49  
    50  // WithTableDelimiter is an option which configures a delimiter
    51  // used to prefix table names onto keys when constructing
    52  // their associated flag name.
    53  // The default delimiter is "."
    54  //
    55  // For example, given the following TOML
    56  //
    57  //     [section.subsection]
    58  //     value = 10
    59  //
    60  // Parse will match to a flag with the name `-section.subsection.value` by default.
    61  // If the delimiter is "-", Parse will match to `-section-subsection-value` instead.
    62  func WithTableDelimiter(d string) Option {
    63  	return func(c *ConfigFileParser) {
    64  		c.delimiter = d
    65  	}
    66  }
    67  
    68  func parseTree(tree *toml.Tree, parent, delimiter string, set func(name, value string) error) error {
    69  	for _, key := range tree.Keys() {
    70  		name := key
    71  		if parent != "" {
    72  			name = parent + delimiter + key
    73  		}
    74  		switch t := tree.Get(key).(type) {
    75  		case *toml.Tree:
    76  			if err := parseTree(t, name, delimiter, set); err != nil {
    77  				return err
    78  			}
    79  		case interface{}:
    80  			values, err := valsToStrs(t)
    81  			if err != nil {
    82  				return ParseError{Inner: err}
    83  			}
    84  			for _, value := range values {
    85  				if err = set(name, value); err != nil {
    86  					return err
    87  				}
    88  			}
    89  		}
    90  	}
    91  	return nil
    92  }
    93  
    94  func valsToStrs(val interface{}) ([]string, error) {
    95  	if vals, ok := val.([]interface{}); ok {
    96  		ss := make([]string, len(vals))
    97  		for i := range vals {
    98  			s, err := valToStr(vals[i])
    99  			if err != nil {
   100  				return nil, err
   101  			}
   102  			ss[i] = s
   103  		}
   104  		return ss, nil
   105  	}
   106  	s, err := valToStr(val)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	return []string{s}, nil
   111  
   112  }
   113  
   114  func valToStr(val interface{}) (string, error) {
   115  	switch v := val.(type) {
   116  	case string:
   117  		return v, nil
   118  	case bool:
   119  		return strconv.FormatBool(v), nil
   120  	case uint64:
   121  		return strconv.FormatUint(v, 10), nil
   122  	case int64:
   123  		return strconv.FormatInt(v, 10), nil
   124  	case float64:
   125  		return strconv.FormatFloat(v, 'g', -1, 64), nil
   126  	default:
   127  		return "", ff.StringConversionError{Value: val}
   128  	}
   129  }
   130  
   131  // ParseError wraps all errors originating from the TOML parser.
   132  type ParseError struct {
   133  	Inner error
   134  }
   135  
   136  // Error implenents the error interface.
   137  func (e ParseError) Error() string {
   138  	return fmt.Sprintf("error parsing TOML config: %v", e.Inner)
   139  }
   140  
   141  // Unwrap implements the errors.Wrapper interface, allowing errors.Is and
   142  // errors.As to work with ParseErrors.
   143  func (e ParseError) Unwrap() error {
   144  	return e.Inner
   145  }
   146  

View as plain text