package conf import ( "errors" "strconv" "strings" ) var ( ErrNotFound = errors.New("conf: key not found") ErrCouldNotConvert = errors.New("conf: could not cast one or more values to required type") ) const valuesSeparator = "," // Setting represents key-value pair read from config file. // It's Value field holds the value of key parsed from the configuration type Setting struct { Name string Value string } // Int converts Setting Value to int. Returned error // will be non nil if convesion failed. func (s Setting) Int() (int, error) { return parseValue(s.Value, strconv.Atoi) } // IntSlice splits Setting Value (separator is ",") and adds // each of resulting values to []int if possible. // If one or more values can not be converted to float64 those will be dropped // and method will return conf.ErrCouldNotConvert. // Check error to be sure that all required values were parsed. func (s Setting) IntSlice() ([]int, error) { return parseSlice(s.Value, strconv.Atoi) } // Float64 converts Setting Value to float64. Returned error // will be non nil if convesion failed. func (s Setting) Float64() (float64, error) { return parseValue(s.Value, parseFloat64) } // Float64Slice splits Setting Value (separator is ",") and adds // each of resulting values to []float64 if possible. // If one or more values can not be converted to float64 those will be dropped // and method will return conf.ErrCouldNotConvert. // Check error to be sure that all required values were parsed. func (s Setting) Float64Slice() ([]float64, error) { return parseSlice(s.Value, parseFloat64) } // String returns option Value as string // This method also implements Stringer interface from fmt module func (s Setting) String() string { return s.Value } // StringSlice splits Setting's Value (separator is ",") and adds // each of resulting values to []string trimming leading and trailing spaces // from each string. func (s Setting) StringSlice() []string { return tidySplit(s.Value) } // Bool tries to interpret Setting's Value as bool // "1", "t", "T", true", "True", "TRUE" yields true // "0", "f", "F, "false", "False", "FALSE" yields false // If nothing matches will return false and conf.ErrCouldNotConvert. func (s Setting) Bool() (bool, error) { return parseValue(s.Value, strconv.ParseBool) } func parseSlice[T any](s string, f func(string) (T, error)) ([]T, error) { split := tidySplit(s) list := make([]T, 0, len(split)) for _, str := range split { v, err := parseValue(str, f) if err != nil { return list, err } list = append(list, v) } return list, nil } func parseValue[T any](s string, f func(string) (T, error)) (T, error) { v, err := f(s) if err != nil { return v, errors.Join(ErrCouldNotConvert, err) } return v, err } func tidySplit(s string) []string { splitted := strings.Split(s, valuesSeparator) for i, str := range splitted { splitted[i] = strings.TrimSpace(str) } return splitted } func parseFloat64(s string) (float64, error) { return strconv.ParseFloat(s, 64) }