github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/conf/env/env_decoder.go (about)

     1  package env
     2  
     3  import (
     4  	"go/ast"
     5  	"reflect"
     6  
     7  	"github.com/machinefi/w3bstream/pkg/depends/base/types"
     8  	"github.com/machinefi/w3bstream/pkg/depends/x/reflectx"
     9  	"github.com/machinefi/w3bstream/pkg/depends/x/textx"
    10  )
    11  
    12  type Decoder struct{ vars *Vars }
    13  
    14  func NewDecoder(vs *Vars) *Decoder { return &Decoder{vars: vs} }
    15  
    16  func (d *Decoder) Decode(v interface{}) error {
    17  	w := NewPathWalker()
    18  
    19  	rv, ok := v.(reflect.Value)
    20  	if !ok {
    21  		rv = reflect.ValueOf(v)
    22  	}
    23  	return d.scan(w, rv)
    24  }
    25  
    26  // scan scan and set value
    27  func (d *Decoder) scan(w *PathWalker, rv reflect.Value) error {
    28  	kind := rv.Kind()
    29  
    30  	if kind != reflect.Ptr && rv.CanAddr() {
    31  		if v, ok := rv.Addr().Interface().(types.DefaultSetter); ok {
    32  			v.SetDefault()
    33  		}
    34  	}
    35  
    36  	switch kind {
    37  	case reflect.Ptr:
    38  		if rv.IsNil() {
    39  			rv.Set(reflectx.New(rv.Type()))
    40  		}
    41  		return d.scan(w, rv.Elem())
    42  	case reflect.Func, reflect.Interface, reflect.Chan, reflect.Map:
    43  		// skip
    44  	default:
    45  		rt := rv.Type()
    46  		if rt.Implements(types.RTypeTextUnmarshaler) ||
    47  			reflect.PtrTo(rt).Implements(types.RTypeTextUnmarshaler) {
    48  			if v := d.vars.Get(w.String()); v != nil && v.Value != "" {
    49  				return textx.UnmarshalText(rv, []byte(v.Value))
    50  			}
    51  			return nil
    52  		}
    53  		switch kind {
    54  		case reflect.Array, reflect.Slice:
    55  			size := d.vars.Len(w.String())
    56  			if kind == reflect.Slice && rv.IsNil() && size > 0 {
    57  				rv.Set(reflect.MakeSlice(rv.Type(), size, size))
    58  			}
    59  			for i := 0; i < rv.Len(); i++ {
    60  				w.Enter(i)
    61  				if err := d.scan(w, rv.Index(i)); err != nil {
    62  					return err
    63  				}
    64  				w.Exit()
    65  			}
    66  		case reflect.Struct:
    67  			for i := 0; i < rv.NumField(); i++ {
    68  				var flags map[string]bool
    69  				ft := rt.Field(i)
    70  				name := ft.Name
    71  				if !ast.IsExported(name) {
    72  					continue
    73  				}
    74  				if tag, ok := ft.Tag.Lookup("env"); ok {
    75  					key, _flags := reflectx.TagValueAndFlags(tag)
    76  					if key == "-" {
    77  						continue
    78  					}
    79  					if key != "" {
    80  						name = key
    81  					}
    82  					flags = _flags
    83  				}
    84  				inline := flags == nil && ft.Anonymous &&
    85  					reflectx.DeRef(ft.Type).Kind() == reflect.Struct
    86  				if !inline {
    87  					w.Enter(name)
    88  				}
    89  				if err := d.scan(w, rv.Field(i)); err != nil {
    90  					return err
    91  				}
    92  				if !inline {
    93  					w.Exit()
    94  				}
    95  			}
    96  		default:
    97  			if v := d.vars.Get(w.String()); v != nil && v.Value != "" {
    98  				return textx.UnmarshalText(rv, []byte(v.Value))
    99  			}
   100  		}
   101  	}
   102  	return nil
   103  }