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  )