-
Notifications
You must be signed in to change notification settings - Fork 16
/
pkgreflect.go
193 lines (166 loc) · 4.45 KB
/
pkgreflect.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/*
Problem: Go reflection does not support enumerating types, variables and functions of packages.
pkgreflect generates a file named pkgreflect.go in every parsed package directory.
This file contains the following maps of exported names to reflection types/values:
var Types = map[string]reflect.Type{ ... }
var Functions = map[string]reflect.Value{ ... }
var Variables = map[string]reflect.Value{ ... }
Command line usage:
pkgreflect --help
pkgreflect [-notypes][-nofuncs][-novars][-unexported][-norecurs][-gofile=filename.go] [DIR_NAME]
If -norecurs is not set, then pkgreflect traverses recursively into sub-directories.
If no DIR_NAME is given, then the current directory is used as root.
*/
package main
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
)
var (
notypes bool
nofuncs bool
novars bool
noconsts bool
unexported bool
norecurs bool
stdout bool
gofile string
notests bool
)
func main() {
flag.BoolVar(¬ypes, "notypes", false, "Don't list package types")
flag.BoolVar(&nofuncs, "nofuncs", false, "Don't list package functions")
flag.BoolVar(&novars, "novars", false, "Don't list package variables")
flag.BoolVar(&noconsts, "noconsts", false, "Don't list package consts")
flag.BoolVar(&unexported, "unexported", false, "Also list unexported names")
flag.BoolVar(&norecurs, "norecurs", false, "Don't parse sub-directories resursively")
flag.StringVar(&gofile, "gofile", "pkgreflect.go", "Name of the generated .go file")
flag.BoolVar(&stdout, "stdout", false, "Write to stdout.")
flag.BoolVar(¬ests, "notests", false, "Don't list test related code")
flag.Parse()
if len(flag.Args()) > 0 {
for _, dir := range flag.Args() {
parseDir(dir)
}
} else {
parseDir(".")
}
}
func parseDir(dir string) {
dirFile, err := os.Open(dir)
if err != nil {
panic(err)
}
defer dirFile.Close()
info, err := dirFile.Stat()
if err != nil {
panic(err)
}
if !info.IsDir() {
panic("Path is not a directory: " + dir)
}
pkgs, err := parser.ParseDir(token.NewFileSet(), dir, filter, 0)
if err != nil {
panic(err)
}
for _, pkg := range pkgs {
var buf bytes.Buffer
fmt.Fprintln(&buf, "// Code generated by github.com/ungerik/pkgreflect DO NOT EDIT.\n")
fmt.Fprintln(&buf, "package", pkg.Name)
fmt.Fprintln(&buf, "")
fmt.Fprintln(&buf, `import "reflect"`)
fmt.Fprintln(&buf, "")
// Types
if !notypes {
fmt.Fprintln(&buf, "var Types = map[string]reflect.Type{")
print(&buf, pkg, ast.Typ, "\t\"%s\": reflect.TypeOf((*%s)(nil)).Elem(),\n")
fmt.Fprintln(&buf, "}")
fmt.Fprintln(&buf, "")
}
// Functions
if !nofuncs {
fmt.Fprintln(&buf, "var Functions = map[string]reflect.Value{")
print(&buf, pkg, ast.Fun, "\t\"%s\": reflect.ValueOf(%s),\n")
fmt.Fprintln(&buf, "}")
fmt.Fprintln(&buf, "")
}
if !novars {
// Addresses of variables
fmt.Fprintln(&buf, "var Variables = map[string]reflect.Value{")
print(&buf, pkg, ast.Var, "\t\"%s\": reflect.ValueOf(&%s),\n")
fmt.Fprintln(&buf, "}")
fmt.Fprintln(&buf, "")
}
if !noconsts {
// Addresses of consts
fmt.Fprintln(&buf, "var Consts = map[string]reflect.Value{")
print(&buf, pkg, ast.Con, "\t\"%s\": reflect.ValueOf(%s),\n")
fmt.Fprintln(&buf, "}")
fmt.Fprintln(&buf, "")
}
if stdout {
io.Copy(os.Stdout, &buf)
} else {
filename := filepath.Join(dir, gofile)
newFileData := buf.Bytes()
oldFileData, _ := ioutil.ReadFile(filename)
if !bytes.Equal(newFileData, oldFileData) {
err = ioutil.WriteFile(filename, newFileData, 0660)
if err != nil {
panic(err)
}
}
}
}
if !norecurs {
dirs, err := dirFile.Readdir(-1)
if err != nil {
panic(err)
}
for _, info := range dirs {
if info.IsDir() {
parseDir(filepath.Join(dir, info.Name()))
}
}
}
}
func print(w io.Writer, pkg *ast.Package, kind ast.ObjKind, format string) {
names := []string{}
for _, f := range pkg.Files {
for name, object := range f.Scope.Objects {
if object.Kind == kind && (unexported || ast.IsExported(name)) {
names = append(names, name)
}
}
}
sort.Strings(names)
for _, name := range names {
fmt.Fprintf(w, format, name, name)
}
}
func filter(info os.FileInfo) bool {
name := info.Name()
if info.IsDir() {
return false
}
if name == gofile {
return false
}
if filepath.Ext(name) != ".go" {
return false
}
if strings.HasSuffix(name, "_test.go") && notests {
return false
}
return true
}