-
Notifications
You must be signed in to change notification settings - Fork 0
/
path.go
134 lines (126 loc) · 3.58 KB
/
path.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
package main
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/printer"
"go/token"
"os"
"github.com/emil2k/vend/lib/astutil"
)
// path subcommand updates the import paths in the `cwd` directory that
// contain the `from` import or an import located in its subdirectory to the
// equivalent import in the `to` path.
// Recurses into subdirectories to update import paths based on the `recurse`
// parameter.
func path(ctx *build.Context, cwd, from, to string, recurse bool) error {
process := func(cwdPkg *build.Package, _ error) error {
// Get a list of all imports for the package in the cwd
// directory, to determine which child package also need to be
// updated.
cwdImp, cwdDir := cwdPkg.ImportPath, cwdPkg.Dir
if len(cwdImp) == 0 || len(cwdDir) == 0 {
return fmt.Errorf("no import path or directory for cwd package")
}
// Compile map of import paths to change.
rw := make(map[string]string)
// Get the children of the cwd package their import paths will
// be updated as well.
for _, a := range getImports(cwdPkg, true) {
switch {
case from == a:
rw[from] = to
case isChildPackage(from, a):
cp, err := changeImportPath(from, to, a)
if err != nil {
return err
}
rw[a] = cp
}
}
if len(rw) > 0 {
return rwDir(cwdDir, rw)
}
return nil
}
if recurse {
// Recurse into subdirectory packages.
if err := recursePackages(ctx, cwd, process); err != nil {
return err
}
} else if err := process(getPackage(ctx, cwd, cwd)); err != nil {
return err
}
return nil
}
// rwDir goes through the package in the srcDir and updates import path as
// specified by the rw map, from key to value.
// Returns an error if unable to parse the package or if writing to a file.
func rwDir(srcDir string, rw map[string]string) error {
fs := token.NewFileSet()
mode := parser.AllErrors | parser.ParseComments
pkgs, err := parser.ParseDir(fs, srcDir, nil, mode)
if err != nil {
return err
}
for _, pkg := range pkgs {
for _, f := range pkg.Files {
if err := rwFile(fs, f, rw); err != nil {
return err
}
}
}
return nil
}
// rwFile rewrites the import paths inside a file based on the rw map from keys
// to values.
// Returns an error if writing or closing the file fails.
func rwFile(fs *token.FileSet, f *ast.File, rw map[string]string) error {
for op, np := range rw {
if err := rwImport(fs, f, op, np); err != nil {
return err
}
}
return nil
}
// printerConfig configures the AST pretty printing, it should use space for
// alignment and tabs to indent to mirror gofmt.
var printerConfig = &printer.Config{
Mode: printer.UseSpaces | printer.TabIndent,
Tabwidth: 8,
Indent: 0,
}
// rwImport rewrites the import path from the old path, op, to the new path, np,
// inside a file and then writes the changes to it.
// If the old path is not used in a file nothing and an nil error is returned.
// Returns an error if writing or closing the file fails.
func rwImport(fs *token.FileSet, f *ast.File, op, np string) (err error) {
if rw := astutil.RewriteImport(fs, f, op, np); !rw {
return nil
}
// Open up the file and write the changes to it.
if tf := fs.File(f.Pos()); tf != nil {
var wf *os.File
wf, err = os.OpenFile(tf.Name(), os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return err
}
// Properly catch a close error
defer func() {
if cerr := wf.Close(); err == nil {
err = cerr
}
}()
// Print properly
if err = printerConfig.Fprint(wf, fs, f); err != nil {
return err
}
// Output
if opt.verbose {
printBold(fmt.Sprintf("%s => %s", op, np))
fmt.Println(tf.Name())
}
}
return nil
}