reflect.go raw

   1  package env
   2  
   3  import (
   4  	"encoding"
   5  	"fmt"
   6  	"reflect"
   7  	"strconv"
   8  	"time"
   9  )
  10  
  11  var (
  12  	durationType     = reflect.TypeOf(new(time.Duration)).Elem()
  13  	unmarshalerIface = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
  14  )
  15  
  16  func typeOf(v reflect.Value, types ...reflect.Type) bool {
  17  	for _, t := range types {
  18  		if t == v.Type() {
  19  			return true
  20  		}
  21  	}
  22  	return false
  23  }
  24  
  25  func kindOf(v reflect.Value, kinds ...reflect.Kind) bool {
  26  	for _, k := range kinds {
  27  		if k == v.Kind() {
  28  			return true
  29  		}
  30  	}
  31  	return false
  32  }
  33  
  34  func implements(v reflect.Value, ifaces ...reflect.Type) bool {
  35  	for _, iface := range ifaces {
  36  		if t := v.Type(); t.Implements(iface) || reflect.PtrTo(t).Implements(iface) {
  37  			return true
  38  		}
  39  	}
  40  	return false
  41  }
  42  
  43  func structPtr(v reflect.Value) bool {
  44  	return v.IsValid() && v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct && !v.IsNil()
  45  }
  46  
  47  func setValue(v reflect.Value, s string) error {
  48  	switch {
  49  	case typeOf(v, durationType):
  50  		return setDuration(v, s)
  51  	case implements(v, unmarshalerIface):
  52  		return setUnmarshaler(v, s)
  53  	case kindOf(v, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64):
  54  		return setInt(v, s)
  55  	case kindOf(v, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64):
  56  		return setUint(v, s)
  57  	case kindOf(v, reflect.Float32, reflect.Float64):
  58  		return setFloat(v, s)
  59  	case kindOf(v, reflect.Bool):
  60  		return setBool(v, s)
  61  	case kindOf(v, reflect.String):
  62  		return setString(v, s)
  63  	default:
  64  		panic(fmt.Sprintf("env: unsupported type `%s`", v.Type()))
  65  	}
  66  }
  67  
  68  func setInt(v reflect.Value, s string) error {
  69  	i, err := strconv.ParseInt(s, 10, v.Type().Bits())
  70  	if err != nil {
  71  		return fmt.Errorf("parsing int: %w", err)
  72  	}
  73  	v.SetInt(i)
  74  	return nil
  75  }
  76  
  77  func setUint(v reflect.Value, s string) error {
  78  	u, err := strconv.ParseUint(s, 10, v.Type().Bits())
  79  	if err != nil {
  80  		return fmt.Errorf("parsing uint: %w", err)
  81  	}
  82  	v.SetUint(u)
  83  	return nil
  84  }
  85  
  86  func setFloat(v reflect.Value, s string) error {
  87  	f, err := strconv.ParseFloat(s, v.Type().Bits())
  88  	if err != nil {
  89  		return fmt.Errorf("parsing float: %w", err)
  90  	}
  91  	v.SetFloat(f)
  92  	return nil
  93  }
  94  
  95  func setBool(v reflect.Value, s string) error {
  96  	b, err := strconv.ParseBool(s)
  97  	if err != nil {
  98  		return fmt.Errorf("parsing bool: %w", err)
  99  	}
 100  	v.SetBool(b)
 101  	return nil
 102  }
 103  
 104  func setString(v reflect.Value, s string) error {
 105  	v.SetString(s)
 106  	return nil
 107  }
 108  
 109  func setDuration(v reflect.Value, s string) error {
 110  	d, err := time.ParseDuration(s)
 111  	if err != nil {
 112  		return fmt.Errorf("parsing duration: %w", err)
 113  	}
 114  	v.Set(reflect.ValueOf(d))
 115  	return nil
 116  }
 117  
 118  func setUnmarshaler(v reflect.Value, s string) error {
 119  	u := v.Addr().Interface().(encoding.TextUnmarshaler)
 120  	if err := u.UnmarshalText([]byte(s)); err != nil {
 121  		return fmt.Errorf("unmarshaling text: %w", err)
 122  	}
 123  	return nil
 124  }
 125  
 126  func setSlice(v reflect.Value, s []string) error {
 127  	slice := reflect.MakeSlice(v.Type(), len(s), cap(s))
 128  	for i := 0; i < slice.Len(); i++ {
 129  		if err := setValue(slice.Index(i), s[i]); err != nil {
 130  			return err
 131  		}
 132  	}
 133  	v.Set(slice)
 134  	return nil
 135  }
 136