github.com/podhmo/reflect-shape@v0.4.3/shape.go (about) 1 package reflectshape 2 3 import ( 4 "context" 5 "fmt" 6 "go/token" 7 "log" 8 "reflect" 9 "regexp" 10 11 "github.com/podhmo/reflect-shape/metadata" 12 ) 13 14 type ID struct { 15 rt reflect.Type 16 pc uintptr 17 } 18 19 type Shape struct { 20 Name string 21 Kind reflect.Kind 22 IsMethod bool 23 24 ID ID 25 Type reflect.Type 26 DefaultValue reflect.Value 27 28 Number int // If all shapes are from the same extractor, this value can be used as ID 29 Lv int // pointer level. v is 0, *v is 1. 30 Package *Package 31 e *Extractor 32 } 33 34 func (s *Shape) Equal(another *Shape) bool { 35 return s.ID == another.ID 36 } 37 38 func (s *Shape) FullName() string { 39 return fmt.Sprintf("%s.%s", s.Package.Path, s.Name) 40 } 41 42 func (s *Shape) String() string { 43 return fmt.Sprintf("&Shape#%d{Name: %q, Kind: %v, Type: %v, Package: %v}", s.Number, s.Name, s.Kind, s.Type, s.Package.Name) 44 } 45 46 func (s *Shape) Struct() *Struct { 47 if s.Kind != reflect.Struct { 48 panic(fmt.Sprintf("shape %v is not Struct kind, %s", s, s.Kind)) 49 } 50 lookup := s.e.Lookup 51 if lookup == nil || s.Name == "" { 52 return &Struct{Shape: s} 53 } 54 55 metadata, err := lookup.LookupFromTypeForReflectType(s.Type) 56 if err != nil { 57 log.Printf("MustStruct(): %+v", err) 58 return &Struct{Shape: s} 59 } 60 return &Struct{Shape: s, metadata: metadata} 61 } 62 63 func (s *Shape) Interface() *Interface { 64 if s.Kind != reflect.Interface { 65 panic(fmt.Sprintf("shape %v is not Interface kind, %s", s, s.Kind)) 66 } 67 lookup := s.e.Lookup 68 if lookup == nil || s.Name == "" { 69 return &Interface{Shape: s} 70 } 71 72 metadata, err := lookup.LookupFromTypeForReflectType(s.Type) 73 if err != nil { 74 log.Printf("MustInterface(): %+v", err) 75 return &Interface{Shape: s} 76 } 77 return &Interface{Shape: s, metadata: metadata} 78 } 79 80 var anonymousFuncNameRegex = regexp.MustCompile(`func\d+$`) 81 82 func (s *Shape) Func() *Func { 83 if s.Kind != reflect.Func && s.ID.pc == 0 { 84 panic(fmt.Sprintf("shape %v is not func kind, %s", s, s.Kind)) 85 } 86 lookup := s.e.Lookup 87 if lookup == nil || s.Name == "" || (s.Package.Path == "" && anonymousFuncNameRegex.MatchString(s.Name)) { 88 return &Func{Shape: s} 89 } 90 91 metadata, err := lookup.LookupFromFuncForPC(s.ID.pc) 92 if err != nil { 93 log.Printf("MustFunc(): %+v", err) 94 return &Func{Shape: s} 95 } 96 return &Func{Shape: s, metadata: metadata} 97 } 98 99 func (s *Shape) Named() *Named { 100 // TODO: check 101 lookup := s.e.Lookup 102 if lookup == nil || s.Name == "" { 103 return &Named{Shape: s} 104 } 105 106 metadata, err := lookup.LookupFromTypeForReflectType(s.Type) 107 if err != nil { 108 log.Printf("MustType(): %+v", err) 109 return &Named{Shape: s} 110 } 111 return &Named{Shape: s, metadata: metadata} 112 } 113 114 type Named struct { 115 Shape *Shape 116 metadata *metadata.Type 117 } 118 119 func (t *Named) Name() string { 120 return t.Shape.Name 121 } 122 123 func (t *Named) Pos() token.Pos { 124 return t.metadata.Raw.Pos 125 } 126 127 func (t *Named) Doc() string { 128 if t.metadata == nil { 129 return "" 130 } 131 return t.metadata.Doc() 132 } 133 134 func (t *Named) String() string { 135 doc := t.Doc() 136 tsize := t.Shape.e.Config.DocTruncationSize 137 if len(doc) > tsize { 138 doc = doc[:tsize] + "..." 139 } 140 return fmt.Sprintf("&Type{Name: %q, kind: %s, type: %v, Doc: %q}", t.Name(), t.Shape.Kind, t.Shape.Type, doc) 141 } 142 143 type Struct struct { 144 Shape *Shape 145 metadata *metadata.Type 146 } 147 148 func (s *Struct) Name() string { 149 return s.Shape.Name 150 } 151 152 func (s *Struct) Pos() token.Pos { 153 return s.metadata.Raw.Pos 154 } 155 156 func (s *Struct) Doc() string { 157 if s.metadata == nil { 158 return "" 159 } 160 return s.metadata.Doc() 161 } 162 163 func (s *Struct) Fields() FieldList { 164 typ := s.Shape.Type 165 var comments map[string]string 166 if s.metadata != nil { 167 comments = s.metadata.FieldComments() 168 } else { 169 comments = map[string]string{} 170 } 171 172 r := make([]*Field, typ.NumField()) 173 for i := 0; i < typ.NumField(); i++ { 174 f := typ.Field(i) 175 rt := f.Type 176 rv := rzero(f.Type) 177 shape := s.Shape.e.extract(rt, rv) 178 r[i] = &Field{StructField: f, Shape: shape, Doc: comments[f.Name]} 179 } 180 return FieldList(r) 181 } 182 183 func (s *Struct) String() string { 184 doc := s.Doc() 185 tsize := s.Shape.e.Config.DocTruncationSize 186 if len(doc) > tsize { 187 doc = doc[:tsize] + "..." 188 } 189 190 fields := s.Fields() 191 fieldNames := make([]string, len(fields)) 192 for i, f := range fields { 193 fieldNames[i] = f.Name 194 } 195 return fmt.Sprintf("&Struct{Name: %q, Fields: %v, Doc: %q}", s.Name(), fieldNames, doc) 196 } 197 198 type FieldList []*Field 199 200 func (fl FieldList) Len() int { 201 return len(fl) 202 } 203 204 func (fl FieldList) String() string { 205 parts := make([]string, len(fl)) 206 for i, v := range fl { 207 parts[i] = fmt.Sprintf("%+v,", v) 208 } 209 return fmt.Sprintf("%+v", parts) 210 } 211 212 type Field struct { 213 reflect.StructField 214 Shape *Shape 215 Doc string 216 } 217 218 func (f *Field) String() string { 219 doc := f.Doc 220 tsize := f.Shape.e.Config.DocTruncationSize 221 if len(doc) > tsize { 222 doc = doc[:tsize] + "..." 223 } 224 return fmt.Sprintf("&Field{Name: %q, type: %v, Doc:%q}", f.Name, f.Shape.Type, doc) 225 } 226 227 type Interface struct { 228 Shape *Shape 229 metadata *metadata.Type 230 } 231 232 func (iface *Interface) Name() string { 233 return iface.Shape.Name 234 } 235 236 func (iface *Interface) Pos() token.Pos { 237 return iface.metadata.Raw.Pos 238 } 239 240 func (iface *Interface) Doc() string { 241 if iface.metadata == nil { 242 return "" 243 } 244 return iface.metadata.Doc() 245 } 246 247 func (iface *Interface) Methods() VarList { 248 typ := iface.Shape.Type 249 var comments map[string]string 250 if iface.metadata != nil { 251 comments = iface.metadata.FieldComments() 252 } else { 253 comments = map[string]string{} 254 } 255 256 r := make([]*Var, typ.NumMethod()) 257 for i := 0; i < typ.NumMethod(); i++ { 258 f := typ.Method(i) 259 rt := f.Type 260 rv := rzero(f.Type) 261 shape := iface.Shape.e.extract(rt, rv) 262 r[i] = &Var{Name: f.Name, Shape: shape, Doc: comments[f.Name]} 263 } 264 return r 265 } 266 267 func (iface *Interface) String() string { 268 doc := iface.Doc() 269 tsize := iface.Shape.e.Config.DocTruncationSize 270 if len(doc) > tsize { 271 doc = doc[:tsize] + "..." 272 } 273 274 methods := iface.Methods() 275 methodNames := make([]string, len(methods)) 276 for i, m := range methods { 277 methodNames[i] = m.Name 278 } 279 return fmt.Sprintf("&Interface{Name: %q, Methods: %v, Doc: %q}", iface.Name(), methodNames, doc) 280 } 281 282 type Func struct { 283 Shape *Shape 284 metadata *metadata.Func 285 } 286 287 func (f *Func) Name() string { 288 return f.Shape.Name 289 } 290 291 func (f *Func) Pos() token.Pos { 292 return f.metadata.Raw.Pos 293 } 294 295 func (f *Func) IsMethod() bool { 296 return f.Shape.IsMethod 297 } 298 func (f *Func) IsVariadic() bool { 299 return f.Shape.Type.IsVariadic() 300 } 301 302 func (f *Func) Args() VarList { 303 typ := f.Shape.Type 304 var args []metadata.Var 305 if f.metadata != nil { 306 args = f.metadata.Args() 307 } else { 308 args = make([]metadata.Var, typ.NumIn()) 309 } 310 311 r := make([]*Var, typ.NumIn()) 312 needFillNames := f.Shape.e.Config.FillArgNames 313 for i := 0; i < typ.NumIn(); i++ { 314 rt := typ.In(i) 315 rv := rzero(rt) 316 shape := f.Shape.e.extract(rt, rv) 317 p := args[i] 318 name := p.Name 319 if name == "" && needFillNames { 320 switch { 321 case rcontextType == rt: 322 name = "ctx" 323 default: 324 name = fmt.Sprintf("arg%d", i) 325 } 326 } 327 r[i] = &Var{Name: name, Shape: shape, Doc: p.Doc} 328 } 329 return VarList(r) 330 } 331 332 func (f *Func) Returns() VarList { 333 typ := f.Shape.Type 334 var args []metadata.Var 335 if f.metadata != nil { 336 args = f.metadata.Returns() 337 } else { 338 args = make([]metadata.Var, typ.NumOut()) 339 } 340 341 needFillNames := f.Shape.e.Config.FillReturnNames 342 errUsed := false 343 r := make([]*Var, typ.NumOut()) 344 for i := 0; i < typ.NumOut(); i++ { 345 rt := typ.Out(i) 346 rv := rzero(rt) 347 shape := f.Shape.e.extract(rt, rv) 348 p := args[i] 349 name := p.Name 350 if name == "" && needFillNames { 351 switch { 352 case rerrType == rt && errUsed: 353 name = fmt.Sprintf("err%d", i) 354 case rerrType == rt: 355 name = "err" 356 errUsed = true 357 default: 358 name = fmt.Sprintf("ret%d", i) 359 } 360 } 361 r[i] = &Var{Name: name, Shape: shape, Doc: p.Doc} 362 } 363 return VarList(r) 364 } 365 366 func (f *Func) Doc() string { 367 if f.metadata == nil { 368 return "" 369 } 370 return f.metadata.Doc() 371 } 372 func (f *Func) Recv() string { 373 if f.metadata == nil { 374 if f.Shape.IsMethod { 375 return "i" 376 } 377 return "" 378 } 379 return f.metadata.Recv 380 } 381 382 func (f *Func) String() string { 383 doc := f.Doc() 384 tsize := f.Shape.e.Config.DocTruncationSize 385 if len(doc) > tsize { 386 doc = doc[:tsize] + "..." 387 } 388 389 args := f.Args() 390 argNames := make([]string, len(args)) 391 for i, m := range args { 392 argNames[i] = m.Name 393 } 394 395 returns := f.Returns() 396 returnNames := make([]string, len(returns)) 397 for i, m := range returns { 398 returnNames[i] = m.Name 399 } 400 return fmt.Sprintf("&Func{Name: %q, Args: %v, Returns: %v, Doc: %q}", f.Name(), argNames, returnNames, doc) 401 } 402 403 type VarList []*Var 404 405 func (vl VarList) Len() int { 406 return len(vl) 407 } 408 409 func (vl VarList) String() string { 410 parts := make([]string, len(vl)) 411 for i, v := range vl { 412 parts[i] = fmt.Sprintf("%+v,", v) 413 } 414 return fmt.Sprintf("%+v", parts) 415 } 416 417 type Var struct { 418 Name string 419 Shape *Shape 420 Doc string 421 } 422 423 func (v *Var) String() string { 424 doc := v.Doc 425 tsize := v.Shape.e.Config.DocTruncationSize 426 if len(doc) > tsize { 427 doc = doc[:tsize] + "..." 428 } 429 return fmt.Sprintf("&Var{Name: %q, type: %v, Doc: %q}", v.Name, v.Shape.Type, doc) 430 } 431 432 func rzero(rt reflect.Type) reflect.Value { 433 // TODO: fixme 434 return reflect.New(rt).Elem() 435 } 436 437 var ( 438 rcontextType = reflect.TypeOf(func(context.Context) {}).In(0) 439 rerrType = reflect.TypeOf(func(error) {}).In(0) 440 )