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 }