func setFieldVal(structField reflect.Value, name string, v string) error {
	attempted, err := setUnmarshFieldVal(structField, name, v)
	if attempted {
		return err
	}
	switch structField.Kind() {
	case reflect.String:
		structField.SetString(v)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if structField.Type() == reflect.TypeOf(time.Duration(0)) {
			dur, err := time.ParseDuration(v)
			if err != nil {
				return InvalidVariableError{name, v, err}
			}
			structField.SetInt(int64(dur))
		} else {
			vInt, err := strconv.Atoi(v)
			if err != nil {
				return InvalidVariableError{name, v, err}
			}
			structField.SetInt(int64(vInt))
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		vUint, err := strconv.ParseUint(v, 10, 64)
		if err != nil {
			return InvalidVariableError{name, v, err}
		}
		structField.SetUint(uint64(vUint))
	case reflect.Float32, reflect.Float64:
		vFloat, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return InvalidVariableError{name, v, err}
		}
		structField.SetFloat(vFloat)
	case reflect.Bool:
		vBool, err := strconv.ParseBool(v)
		if err != nil {
			return InvalidVariableError{name, v, err}
		}
		structField.SetBool(vBool)
	default:
		return InvalidFieldError{
			Name:    name,
			Message: fmt.Sprintf("Unsupported struct field type: %s", structField.Type().String()),
		}
	}
	return nil
}
setFieldVal first converts v to the type of structField, then uses reflection
to set the field to the converted value.
package envvar
import (
	"encoding"
	"fmt"
	"reflect"
	"strconv"
	"syscall"
	"time"
)
func Parse(v interface{}) error {
	return ParseWithConfig(v, Config{Getenv: syscall.Getenv})
}
type Config struct {
	Getenv func(key string) (value string, found bool)
}
type GetenvFn func(key string) (value string, found bool)
func ParseWithConfig(v interface{}, config Config) error {
	typ := reflect.TypeOf(v)
	if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Struct {
		return InvalidArgumentError{fmt.Sprintf("Error in Parse: type must be a pointer to a struct. Got: %T", v)}
	}
	structType := typ.Elem()
	val := reflect.ValueOf(v)
	if val.IsNil() {
		return InvalidArgumentError{"Error in Parse: argument cannot be nil"}
	}
	structVal := val.Elem()
	if config.Getenv == nil {
		config.Getenv = syscall.Getenv
	}
	ss := structStack{"", structType, structVal, &config}
	return ss.parseStruct()
}
type structStack struct {
	envPrefix  string         
	structType reflect.Type   
	structVal  reflect.Value  
	config     *Config        
}
func (ss structStack) push(
	envPrefix string,
	structType reflect.Type,
	structVal reflect.Value,
) structStack {
	return structStack{
		envPrefix:  ss.envPrefix + envPrefix,
		structType: structType,
		structVal:  structVal,
		config:     ss.config,
	}
}
func (ss structStack) parseStruct() error {
	errors := []error{}
	for i := 0; i < ss.structType.NumField(); i++ {
		field := ss.structType.Field(i)
		fieldVal := ss.structVal.Field(i)
		if err := ss.parseField(field, fieldVal); err != nil {
			if suberrors, ok := err.(ErrorList); ok {
				errors = append(errors, suberrors.Errors...)
			} else {
				errors = append(errors, err)
			}
		}
	}
	if len(errors) > 0 {
		return ErrorList{errors}
	}
	return nil
}
func (ss structStack) parseField(field reflect.StructField, fieldVal reflect.Value) error {
	varName := field.Name
	customName := field.Tag.Get("envvar")
	if customName == "-" {
		return nil
	}
	if customName != "" {
		varName = customName
	}
	if success, _ := cleverMaybeTextUnmarshaler(fieldVal); !success {
		if fieldVal.Type().Kind() == reflect.Struct {
			newSS := ss.push(customName, field.Type, fieldVal)
			if err := foundDefaultTagError(field); err != nil {
				return err
			}
			return newSS.parseStruct()
		} else if fieldVal.Type().Kind() == reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct {
			if fieldVal.IsNil() {
				fieldVal.Set(reflect.New(field.Type.Elem()))
			}
			if err := foundDefaultTagError(field); err != nil {
				return err
			}
			newSS := ss.push(customName, field.Type.Elem(), fieldVal.Elem())
			return newSS.parseStruct()
		}
	}
	var varVal string
	defaultVal, foundDefault := field.Tag.Lookup("default")
	derivedVarName := ss.envPrefix + varName
	envVal, foundEnv := ss.config.Getenv(derivedVarName)
	if foundEnv {
		varVal = envVal
	} else {
		if foundDefault {
			varVal = defaultVal
		} else {
			return UnsetVariableError{VarName: derivedVarName}
		}
	}
	return setFieldVal(fieldVal, derivedVarName, varVal)
}
func foundDefaultTagError(field reflect.StructField) error {
	if _, foundDefault := field.Tag.Lookup("default"); foundDefault {
		return InvalidFieldError{
			Name:    field.Name,
			Message: "default tag is not supported for nested structs.",
		}
	}
	return nil
}
func maybeTextUnmarshaler(val reflect.Value) (bool, encoding.TextUnmarshaler) {
	if val.CanInterface() {
		casted, ok := val.Interface().(encoding.TextUnmarshaler)
		if !ok {
			return false, nil
		}
		return true, casted
	}
	return false, nil
}
func cleverMaybeTextUnmarshaler(structField reflect.Value) (bool, encoding.TextUnmarshaler) {
	if success, m := maybeTextUnmarshaler(structField); success {
		return true, m
	}
	if structField.CanAddr() {
		if success, m := maybeTextUnmarshaler(structField.Addr()); success {
			return true, m
		}
	}
	return false, nil
}
func setUnmarshFieldVal(structField reflect.Value, name string, v string) (bool, error) {
	if success, m := cleverMaybeTextUnmarshaler(structField); success {
		err := m.UnmarshalText([]byte(v))
		if err != nil {
			return true, InvalidVariableError{name, v, err}
		}
		return true, nil
	}
	return false, nil
}