github.com/podhmo/reflect-shape@v0.4.3/extractor.go (about) 1 package reflectshape 2 3 import ( 4 "fmt" 5 "reflect" 6 "runtime" 7 "sort" 8 "strings" 9 10 "github.com/podhmo/reflect-shape/metadata" 11 ) 12 13 type Extractor struct { 14 Config *Config 15 Lookup *metadata.Lookup 16 17 seen map[ID]*Shape 18 packages map[string]*Package 19 } 20 21 func (e *Extractor) Visited() map[ID]*Shape { 22 return e.seen 23 } 24 25 func (e *Extractor) Extract(ob interface{}) *Shape { 26 // TODO: only handling *T 27 rt := reflect.TypeOf(ob) 28 rv := reflect.ValueOf(ob) 29 return e.extract(rt, rv) 30 } 31 32 func (e *Extractor) extract(rt reflect.Type, rv reflect.Value) *Shape { 33 lv := 0 34 for rt.Kind() == reflect.Pointer { 35 rt = rt.Elem() 36 rv = rv.Elem() 37 lv++ 38 } 39 40 id := ID{rt: rt} 41 if rt.Kind() == reflect.Func { 42 id.pc = rv.Pointer() // distinguish same signature function 43 } 44 45 shape, ok := e.seen[id] 46 if ok { 47 if lv == 0 { 48 return shape 49 } 50 copied := *shape 51 copied.Lv = lv 52 return &copied 53 } 54 55 name := rt.Name() 56 pkgPath := rt.PkgPath() 57 isMethod := false 58 59 if id.pc != 0 { // is function? 60 fullname := runtime.FuncForPC(id.pc).Name() 61 parts := strings.Split(fullname, ".") 62 63 if strings.HasSuffix(fullname, "-fm") { 64 isMethod = true 65 // @@ github.com/podhmo/reflect-shape/neo_test.S0.M-fm 66 // @@ github.com/podhmo/reflect-shape/neo_test.(*S1).M-fm 67 pkgPath = strings.Join(parts[:len(parts)-2], ".") 68 name = fmt.Sprintf("%s.%s", strings.Trim(parts[len(parts)-2], "(*)"), strings.TrimSuffix(parts[len(parts)-1], "-fm")) 69 } else { 70 // @@ github.com/podhmo/reflect-shape/neo_test.F1 71 // @@ github.com/podhmo/reflect-shape/neo_test.S0 72 pkgPath = strings.Join(parts[:len(parts)-1], ".") 73 name = parts[len(parts)-1] 74 } 75 } 76 77 pkg, ok := e.packages[pkgPath] 78 if !ok { 79 parts := strings.Split(pkgPath, "/") // todo fix 80 pkgName := parts[len(parts)-1] 81 pkg = &Package{ 82 Name: pkgName, 83 Path: pkgPath, 84 scope: &Scope{shapes: map[string]*Shape{}}, 85 } 86 e.packages[pkgPath] = pkg 87 } 88 89 shape = &Shape{ 90 Name: name, 91 Kind: rt.Kind(), 92 ID: id, 93 Type: rt, 94 DefaultValue: rv, 95 Number: len(e.seen), 96 IsMethod: isMethod, 97 Package: pkg, 98 e: e, 99 } 100 e.seen[id] = shape 101 pkg.scope.shapes[name] = shape 102 103 if lv == 0 { 104 return shape 105 } 106 copied := *shape 107 copied.Lv = lv 108 return &copied 109 } 110 111 type Package struct { 112 Name string 113 Path string 114 115 scope *Scope 116 } 117 118 func (p *Package) Scope() *Scope { 119 return p.scope 120 } 121 122 type Scope struct { 123 shapes map[string]*Shape 124 } 125 126 func (s *Scope) Names() []string { 127 return s.names(false) 128 } 129 130 func (s *Scope) NamesWithMethod() []string { 131 return s.names(true) 132 } 133 134 func (s *Scope) names(withMethod bool) []string { 135 // anonymous function is not supported yet 136 r := make([]string, 0, len(s.shapes)) 137 for name, s := range s.shapes { 138 if !withMethod && s.IsMethod { 139 continue 140 } 141 r = append(r, name) 142 } 143 sort.Strings(r) 144 return r 145 }