package conf import ( "errors" "net/url" "strconv" "strings" ) var ( ErrCouldNotConvert = errors.New("conf: could not cast one or more values to required type") ) const ( separatorSlice = "," separatorMap = ":" ) type Value string // Map tries to interpret value as "key: value" pairs // separated by comma like in the following string: // "key1: value1, key2: value2, key3: value3". func (v Value) Map() (Map, error) { split := tidySplit(v, separatorSlice) m := make(Map, len(split)) for i := range split { k, v, ok := toKV(split[i], separatorMap) if !ok { return nil, ErrCouldNotConvert } m[k] = Value(v) } return m, nil } // String returns Value as string func (v Value) String() string { return string(v) } // Int converts Value to int. Returned error // will be non nil if convesion failed. func (v Value) Int() (int, error) { return parseValue(v, strconv.Atoi) // Atoi in fact checks int size, no need to use ParseInt } // IntSlice splits Value (separator is ",") and adds // each of resulting values to []int if possible. // Returns nil and non-nil error on first failure to convert. func (v Value) IntSlice() ([]int, error) { return parseSlice(v, strconv.Atoi) } // Float64 converts Value to float64. Returned error // will be non nil if convesion failed. func (v Value) Float64() (float64, error) { return parseValue(v, parseFloat64) } // Float64Slice splits Value (separator is ",") and adds // each of resulting values to []float64 if possible. // Returns nil and non-nil error on first failure to convert. func (v Value) Float64Slice() ([]float64, error) { return parseSlice(v, parseFloat64) } // StringSlice splits Value (separator is ",") and adds // each of resulting values to []string trimming leading and trailing spaces // from each string. func (v Value) StringSlice() []string { return tidySplit(v, separatorSlice) } // Bool tries to interpret 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 (v Value) Bool() (bool, error) { return parseValue(v, strconv.ParseBool) } // URL tries to interpret value as url.URL func (v Value) URL() (*url.URL, error) { return parseValue(v, url.Parse) } func parseSlice[T any, S ~string](s S, f func(string) (T, error)) ([]T, error) { split := tidySplit(s, separatorSlice) list := make([]T, 0, len(split)) for _, str := range split { v, err := parseValue(str, f) if err != nil { return nil, err } list = append(list, v) } return list, nil } func parseValue[T any, S ~string](s S, f func(string) (T, error)) (T, error) { v, err := f(string(s)) if err != nil { return v, errors.Join(ErrCouldNotConvert, err) } return v, err } func tidySplit[S ~string](s S, sep string) []string { splitted := strings.Split(string(s), sep) for i, str := range splitted { splitted[i] = trim(str) } return splitted } func parseFloat64(s string) (float64, error) { return strconv.ParseFloat(s, 64) }