func parseSlice(valueType reflect.Type, value string, sep string, layout *string) (*reflect.Value, error) {
	sliceValue := reflect.MakeSlice(valueType, 0, 0)
	if valueType.Elem().Kind() == reflect.Uint8 {
		sliceValue = reflect.ValueOf([]byte(value))
	} else if len(strings.TrimSpace(value)) != 0 {
		values := strings.Split(value, sep)
		sliceValue = reflect.MakeSlice(valueType, len(values), len(values))
		for i, val := range values {
			if err := parseValue(sliceValue.Index(i), val, sep, layout); err != nil {
				return nil, err
			}
		}
	}
	return &sliceValue, nil
}
parseSlice parses value into a slice of given type
package cleanenv
import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"math"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"
	"github.com/BurntSushi/toml"
	"github.com/joho/godotenv"
	"gopkg.in/yaml.v2"
	"olympos.io/encoding/edn"
)
const (
	DefaultSeparator = ","
)
const (
	TagEnv = "env"
	TagEnvLayout = "env-layout"
	TagEnvDefault = "env-default"
	TagEnvSeparator = "env-separator"
	TagEnvDescription = "env-description"
	TagEnvUpd = "env-upd"
	TagEnvRequired = "env-required"
	TagEnvPrefix = "env-prefix"
)
type Setter interface {
	SetValue(string) error
}
type Updater interface {
	Update() error
}
func ReadConfig(path string, cfg interface{}) error {
	err := parseFile(path, cfg)
	if err != nil {
		return err
	}
	return readEnvVars(cfg, false)
}
func ReadEnv(cfg interface{}) error {
	return readEnvVars(cfg, false)
}
func UpdateEnv(cfg interface{}) error {
	return readEnvVars(cfg, true)
}
func parseFile(path string, cfg interface{}) error {
	f, err := os.OpenFile(path, os.O_RDONLY|os.O_SYNC, 0)
	if err != nil {
		return err
	}
	defer f.Close()
	switch ext := strings.ToLower(filepath.Ext(path)); ext {
	case ".yaml", ".yml":
		err = parseYAML(f, cfg)
	case ".json":
		err = parseJSON(f, cfg)
	case ".toml":
		err = parseTOML(f, cfg)
	case ".edn":
		err = parseEDN(f, cfg)
	case ".env":
		err = parseENV(f, cfg)
	default:
		return fmt.Errorf("file format '%s' doesn't supported by the parser", ext)
	}
	if err != nil {
		return fmt.Errorf("config file parsing error: %s", err.Error())
	}
	return nil
}
func parseYAML(r io.Reader, str interface{}) error {
	return yaml.NewDecoder(r).Decode(str)
}
func parseJSON(r io.Reader, str interface{}) error {
	return json.NewDecoder(r).Decode(str)
}
func parseTOML(r io.Reader, str interface{}) error {
	_, err := toml.DecodeReader(r, str)
	return err
}
func parseEDN(r io.Reader, str interface{}) error {
	return edn.NewDecoder(r).Decode(str)
}
func parseENV(r io.Reader, _ interface{}) error {
	vars, err := godotenv.Parse(r)
	if err != nil {
		return err
	}
	for env, val := range vars {
		os.Setenv(env, val)
	}
	return nil
}
type structMeta struct {
	envList     []string
	fieldName   string
	fieldValue  reflect.Value
	defValue    *string
	layout      *string
	separator   string
	description string
	updatable   bool
	required    bool
}
func (sm *structMeta) isFieldValueZero() bool {
	return isZero(sm.fieldValue)
}
func readStructMetadata(cfgRoot interface{}) ([]structMeta, error) {
	type cfgNode struct {
		Val    interface{}
		Prefix string
	}
	cfgStack := []cfgNode{{cfgRoot, ""}}
	metas := make([]structMeta, 0)
	for i := 0; i < len(cfgStack); i++ {
		s := reflect.ValueOf(cfgStack[i].Val)
		sPrefix := cfgStack[i].Prefix
		if s.Kind() == reflect.Ptr {
			s = s.Elem()
		}
		if s.Kind() != reflect.Struct {
			return nil, fmt.Errorf("wrong type %v", s.Kind())
		}
		typeInfo := s.Type()
		for idx := 0; idx < s.NumField(); idx++ {
			fType := typeInfo.Field(idx)
			var (
				defValue  *string
				layout    *string
				separator string
			)
			if fld := s.Field(idx); fld.Kind() == reflect.Struct {
				if fld.Type() != reflect.TypeOf(time.Time{}) {
					prefix, _ := fType.Tag.Lookup(TagEnvPrefix)
					cfgStack = append(cfgStack, cfgNode{fld.Addr().Interface(), sPrefix + prefix})
					continue
				}
				if l, ok := fType.Tag.Lookup(TagEnvLayout); ok {
					layout = &l
				}
			}
			if !s.Field(idx).CanSet() {
				continue
			}
			if def, ok := fType.Tag.Lookup(TagEnvDefault); ok {
				defValue = &def
			}
			if sep, ok := fType.Tag.Lookup(TagEnvSeparator); ok {
				separator = sep
			} else {
				separator = DefaultSeparator
			}
			_, upd := fType.Tag.Lookup(TagEnvUpd)
			_, required := fType.Tag.Lookup(TagEnvRequired)
			envList := make([]string, 0)
			if envs, ok := fType.Tag.Lookup(TagEnv); ok && len(envs) != 0 {
				envList = strings.Split(envs, DefaultSeparator)
				if sPrefix != "" {
					for i := range envList {
						envList[i] = sPrefix + envList[i]
					}
				}
			}
			metas = append(metas, structMeta{
				envList:     envList,
				fieldName:   s.Type().Field(idx).Name,
				fieldValue:  s.Field(idx),
				defValue:    defValue,
				layout:      layout,
				separator:   separator,
				description: fType.Tag.Get(TagEnvDescription),
				updatable:   upd,
				required:    required,
			})
		}
	}
	return metas, nil
}
func readEnvVars(cfg interface{}, update bool) error {
	metaInfo, err := readStructMetadata(cfg)
	if err != nil {
		return err
	}
	if updater, ok := cfg.(Updater); ok {
		if err := updater.Update(); err != nil {
			return err
		}
	}
	for _, meta := range metaInfo {
		if update && !meta.updatable {
			continue
		}
		var rawValue *string
		for _, env := range meta.envList {
			if value, ok := os.LookupEnv(env); ok {
				rawValue = &value
				break
			}
		}
		if rawValue == nil && meta.required && meta.isFieldValueZero() {
			err := fmt.Errorf("field %q is required but the value is not provided",
				meta.fieldName)
			return err
		}
		if rawValue == nil && meta.isFieldValueZero() {
			rawValue = meta.defValue
		}
		if rawValue == nil {
			continue
		}
		if err := parseValue(meta.fieldValue, *rawValue, meta.separator, meta.layout); err != nil {
			return err
		}
	}
	return nil
}
func parseValue(field reflect.Value, value, sep string, layout *string) error {
	if field.CanInterface() {
		if cs, ok := field.Interface().(Setter); ok {
			return cs.SetValue(value)
		} else if csp, ok := field.Addr().Interface().(Setter); ok {
			return csp.SetValue(value)
		}
	}
	valueType := field.Type()
	switch valueType.Kind() {
	case reflect.String:
		field.SetString(value)
	case reflect.Bool:
		b, err := strconv.ParseBool(value)
		if err != nil {
			return err
		}
		field.SetBool(b)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if field.Kind() == reflect.Int64 && valueType.PkgPath() == "time" && valueType.Name() == "Duration" {
			d, err := time.ParseDuration(value)
			if err != nil {
				return err
			}
			field.SetInt(int64(d))
		} else {
			number, err := strconv.ParseInt(value, 0, valueType.Bits())
			if err != nil {
				return err
			}
			field.SetInt(number)
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		number, err := strconv.ParseUint(value, 0, valueType.Bits())
		if err != nil {
			return err
		}
		field.SetUint(number)
	case reflect.Float32, reflect.Float64:
		number, err := strconv.ParseFloat(value, valueType.Bits())
		if err != nil {
			return err
		}
		field.SetFloat(number)
	case reflect.Slice:
		sliceValue, err := parseSlice(valueType, value, sep, layout)
		if err != nil {
			return err
		}
		field.Set(*sliceValue)
	case reflect.Map:
		mapValue, err := parseMap(valueType, value, sep, layout)
		if err != nil {
			return err
		}
		field.Set(*mapValue)
	case reflect.Struct:
		if valueType.PkgPath() == "time" && valueType.Name() == "Time" {
			var l string
			if layout != nil {
				l = *layout
			} else {
				l = time.RFC3339
			}
			val, err := time.Parse(l, value)
			if err != nil {
				return err
			}
			field.Set(reflect.ValueOf(val))
		}
	default:
		return fmt.Errorf("unsupported type %s.%s", valueType.PkgPath(), valueType.Name())
	}
	return nil
}