github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/httptransport/transformer/util_param.go (about)

     1  package transformer
     2  
     3  import (
     4  	"context"
     5  	"go/ast"
     6  	"reflect"
     7  	"sync"
     8  
     9  	"github.com/machinefi/w3bstream/pkg/depends/kit/validator"
    10  	vldterr "github.com/machinefi/w3bstream/pkg/depends/kit/validator/errors"
    11  	"github.com/machinefi/w3bstream/pkg/depends/x/contextx"
    12  	"github.com/machinefi/w3bstream/pkg/depends/x/reflectx"
    13  	"github.com/machinefi/w3bstream/pkg/depends/x/typesx"
    14  )
    15  
    16  type Param struct {
    17  	In    string
    18  	Name  string
    19  	Field typesx.StructField
    20  	Type  typesx.Type
    21  	Tags  map[string]reflectx.StructTag
    22  	Loc   []int
    23  }
    24  
    25  func (p *Param) FieldValue(rv reflect.Value) reflect.Value {
    26  	rv = reflectx.Indirect(rv)
    27  	n := len(p.Loc)
    28  	fv := rv
    29  
    30  	for i := 0; i < n; i++ {
    31  		loc := p.Loc[i]
    32  		fv = fv.Field(loc)
    33  
    34  		// last loc should keep ptr value
    35  		if i < n-1 {
    36  			for fv.Kind() == reflect.Ptr {
    37  				// notice the ptr struct ensure only for Ptr Anonymous Field
    38  				if fv.IsNil() {
    39  					fv.Set(reflectx.New(fv.Type()))
    40  				}
    41  				fv = fv.Elem()
    42  			}
    43  		}
    44  	}
    45  
    46  	return fv
    47  }
    48  
    49  func EachParameter(_ context.Context, t typesx.Type, each func(p *Param) bool) {
    50  	var walk func(tpe typesx.Type, parents ...int)
    51  
    52  	walk = func(t typesx.Type, parents ...int) {
    53  		for i := 0; i < t.NumField(); i++ {
    54  			f := t.Field(i)
    55  			if !ast.IsExported(f.Name()) {
    56  				continue
    57  			}
    58  
    59  			loc := append(parents, i)
    60  			flags := reflectx.ParseStructTag(string(f.Tag()))
    61  			display := f.Name()
    62  
    63  			tagIn, hasIn := flags["in"]
    64  			tagName, hasName := flags["name"]
    65  			if hasName {
    66  				if name := tagName.Name(); name == "-" {
    67  					continue // skip name:"-"
    68  				} else {
    69  					if name != "" {
    70  						display = name
    71  					}
    72  				}
    73  			}
    74  
    75  			if f.Anonymous() && (!hasIn && !hasName) {
    76  				ft := f.Type()
    77  				_, ok := typesx.EncodingTextMarshalerTypeReplacer(ft)
    78  				if !ok {
    79  					for ft.Kind() == reflect.Ptr {
    80  						ft = ft.Elem()
    81  					}
    82  					if ft.Kind() == reflect.Struct {
    83  						walk(ft, loc...)
    84  						continue
    85  					}
    86  				}
    87  			}
    88  
    89  			p := &Param{
    90  				In:    tagIn.Name(),
    91  				Name:  display,
    92  				Field: f,
    93  				Type:  f.Type(),
    94  				Tags:  flags,
    95  				Loc:   append([]int{}, loc...),
    96  			}
    97  			if !each(p) {
    98  				break
    99  			}
   100  		}
   101  	}
   102  
   103  	walk(t)
   104  }
   105  
   106  type FlattenParams struct {
   107  	Params []ReqParam
   108  }
   109  
   110  func (FlattenParams) NewValidator(ctx context.Context, typ typesx.Type) (validator.Validator, error) {
   111  	p := &FlattenParams{}
   112  	err := p.CollectParams(ctx, typ)
   113  	return p, err
   114  }
   115  
   116  func (FlattenParams) String() string { return "@flatten" }
   117  
   118  func (ps *FlattenParams) Validate(v interface{}) error {
   119  	rv, ok := v.(reflect.Value)
   120  	if !ok {
   121  		rv = reflect.ValueOf(v)
   122  	}
   123  	errs := vldterr.NewErrorSet()
   124  	rv = reflectx.Indirect(rv)
   125  
   126  	for i := range ps.Params {
   127  		pi := ps.Params[i]
   128  
   129  		fieldValue := pi.FieldValue(rv)
   130  
   131  		if pi.Validator != nil {
   132  			if err := pi.Validator.Validate(fieldValue); err != nil {
   133  				errs.AddErr(err, pi.Name)
   134  			}
   135  		}
   136  	}
   137  
   138  	return errs.Err()
   139  }
   140  
   141  func (ps *FlattenParams) CollectParams(ctx context.Context, typ typesx.Type) error {
   142  	err := EachReqParam(ctx, typesx.DeRef(typ), func(rp *ReqParam) {
   143  		ps.Params = append(ps.Params, *rp)
   144  	})
   145  	return err
   146  }
   147  
   148  type ReqParam struct {
   149  	Param
   150  	Option    Option
   151  	Tsf       Transformer
   152  	Validator validator.Validator
   153  }
   154  
   155  func EachReqParam(ctx context.Context, tpe typesx.Type, each func(rp *ReqParam)) error {
   156  	errs := vldterr.NewErrorSet()
   157  
   158  	EachParameter(ctx, tpe, func(p *Param) bool {
   159  		rp := &ReqParam{}
   160  		rp.Param = *p
   161  
   162  		rp.Option.Name = rp.Name
   163  
   164  		if tag, ok := rp.Tags["name"]; ok {
   165  			rp.Option.Omitempty = tag.HasFlag("omitempty")
   166  		}
   167  		if tag, ok := rp.Tags["mime"]; ok {
   168  			rp.Option.MIME = tag.Name()
   169  		}
   170  		if rp.In == "path" {
   171  			rp.Option.Omitempty = false
   172  		}
   173  
   174  		switch rp.Type.Kind() {
   175  		case reflect.Array, reflect.Slice:
   176  			elem := rp.Type.Elem()
   177  			if !(elem.PkgPath() == "" && elem.Kind() == reflect.Uint8) {
   178  				rp.Option.Explode = true
   179  			}
   180  		}
   181  
   182  		newtsf := func() (Transformer, error) {
   183  			if rp.Option.Explode {
   184  				return NewTransformer(ctx, rp.Type.Elem(), rp.Option)
   185  			}
   186  			return NewTransformer(ctx, rp.Type, rp.Option)
   187  		}
   188  
   189  		tsf, err := newtsf()
   190  		if err != nil {
   191  			errs.AddErr(err, rp.Name)
   192  			return false
   193  		}
   194  		rp.Tsf = tsf
   195  
   196  		// TODO check if current struct or field impled Validator
   197  		paramVldt, err := NewValidator(ctx, rp.Type, rp.Tags, rp.Option.Omitempty, tsf)
   198  		if err != nil {
   199  			errs.AddErr(err, rp.Name)
   200  			return false
   201  		}
   202  		rp.Validator = paramVldt
   203  
   204  		each(rp)
   205  
   206  		return true
   207  	})
   208  
   209  	if errs.Len() == 0 {
   210  		return nil
   211  	}
   212  
   213  	return errs.Err()
   214  }
   215  
   216  type ParamAndValue struct {
   217  	Param
   218  	Value reflect.Value
   219  }
   220  
   221  type GroupedParams = map[string][]Param
   222  
   223  type ckGroupedParamsSet struct{}
   224  
   225  // dftGroupedParamsSet stores typesx.Type => GroupedParams
   226  var dftGroupedParamsSet = &sync.Map{}
   227  
   228  func GroupedParamSetFromContext(ctx context.Context) *sync.Map {
   229  	if m, ok := ctx.Value(ckGroupedParamsSet{}).(*sync.Map); ok {
   230  		return m
   231  	}
   232  	return dftGroupedParamsSet
   233  }
   234  
   235  func WithGroupedParamSet(ctx context.Context, m *sync.Map) context.Context {
   236  	return contextx.WithValue(ctx, ckGroupedParamsSet{}, m)
   237  }
   238  
   239  func CollectGroupedParam(ctx context.Context, tpe typesx.Type) GroupedParams {
   240  	if tpe.Kind() != reflect.Struct {
   241  		return nil
   242  	}
   243  
   244  	m := GroupedParamSetFromContext(ctx)
   245  	if gp, ok := m.Load(tpe); ok {
   246  		return gp.(GroupedParams)
   247  	}
   248  
   249  	gp := GroupedParams{}
   250  
   251  	defer func() {
   252  		m.Store(tpe, gp)
   253  	}()
   254  
   255  	EachParameter(ctx, tpe, func(p *Param) bool {
   256  		gp[p.In] = append(gp[p.In], *p)
   257  		return true
   258  	})
   259  
   260  	return gp
   261  }