├── docs ├── zap.png ├── zapcore.png ├── examples-cn │ ├── cronsun.png │ ├── cronsun.node.png │ ├── cronsun.node.core.png │ ├── cronsun.node.core.dot │ ├── cronsun.node.dot │ ├── goimportdot_guide.md │ └── cronsun.dot ├── zapcore.dot └── zap.dot ├── core ├── imps_test.go ├── pkg_filter_test.go ├── file_filter.go ├── util.go ├── imps.go └── pkg_filter.go ├── README.md └── goimportdot.go /docs/zap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yqylovy/goimportdot/HEAD/docs/zap.png -------------------------------------------------------------------------------- /docs/zapcore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yqylovy/goimportdot/HEAD/docs/zapcore.png -------------------------------------------------------------------------------- /docs/examples-cn/cronsun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yqylovy/goimportdot/HEAD/docs/examples-cn/cronsun.png -------------------------------------------------------------------------------- /docs/examples-cn/cronsun.node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yqylovy/goimportdot/HEAD/docs/examples-cn/cronsun.node.png -------------------------------------------------------------------------------- /docs/examples-cn/cronsun.node.core.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yqylovy/goimportdot/HEAD/docs/examples-cn/cronsun.node.core.png -------------------------------------------------------------------------------- /core/imps_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestGetImports(t *testing.T) { 10 | Convey("Test GetImports", t, func() { 11 | _, err := GetImports("go.uber.org/zap", 12 | NameContains(true, ".git"), 13 | NameContains(true, "_test.go"), 14 | HasSuffix(false, ".go")) 15 | So(err, ShouldBeNil) 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /core/pkg_filter_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestPkgRegex(t *testing.T) { 10 | Convey("Test PkgRegex", t, func() { 11 | imps := map[string]StrSet{ 12 | "a/b/c": NewStrSet( 13 | "test/subt", 14 | ), 15 | } 16 | filter := PkgWildcardFilter(true, "test*") 17 | imps = filter(imps) 18 | So(imps["a/b/c"], ShouldBeEmpty) 19 | }) 20 | } 21 | 22 | func TestParsePkgWildcardStr(t *testing.T) { 23 | Convey("Test ParsePkgWildcardStr", t, func() { 24 | str := "w:a*,*b;b:c" 25 | fs, err := ParsePkgWildcardStr(str) 26 | So(err, ShouldBeNil) 27 | So(len(fs), ShouldEqual, 2) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /docs/zapcore.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | "go.uber.org/zap/internal/multierror"->"go.uber.org/zap/internal/bufferpool"; 3 | "go.uber.org/zap/internal/bufferpool"->"go.uber.org/zap/buffer"; 4 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/buffer"; 5 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/multierror"; 6 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/exit"; 7 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/bufferpool"; 8 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/color"; 9 | "go.uber.org/zap/internal/exit"; 10 | "go.uber.org/zap/zapcore"; 11 | "go.uber.org/zap/internal/color"; 12 | "go.uber.org/zap/buffer"; 13 | "go.uber.org/zap/internal/multierror"; 14 | "go.uber.org/zap/internal/bufferpool"; 15 | } 16 | -------------------------------------------------------------------------------- /docs/examples-cn/cronsun.node.core.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/db"; 3 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/conf"; 4 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/node/cron"; 5 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/db"; 6 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/event"; 7 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/conf"; 8 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/node/cron"; 9 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun"; 10 | "github.com/shunfei/cronsun/node/cron"; 11 | "github.com/shunfei/cronsun"; 12 | "github.com/shunfei/cronsun/db"; 13 | "github.com/shunfei/cronsun/conf"; 14 | "github.com/shunfei/cronsun/event"; 15 | "github.com/shunfei/cronsun/node"; 16 | } 17 | -------------------------------------------------------------------------------- /core/file_filter.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | type FileFilter struct { 9 | IsBlack bool 10 | Func func(fp string, info os.FileInfo, err error) bool 11 | } 12 | 13 | func NameContains(isblack bool, str string) FileFilter { 14 | return FileFilter{ 15 | IsBlack: isblack, 16 | Func: func(fp string, _ os.FileInfo, _ error) bool { 17 | return strings.Contains(fp, str) 18 | }, 19 | } 20 | } 21 | func HasSuffix(isblack bool, suffixs ...string) FileFilter { 22 | return FileFilter{ 23 | IsBlack: isblack, 24 | Func: func(fp string, _ os.FileInfo, _ error) bool { 25 | for _, sf := range suffixs { 26 | if sf == "" { 27 | continue 28 | } 29 | if sf[0] != '.' { 30 | sf = "." + sf 31 | } 32 | if strings.HasSuffix(fp, sf) { 33 | return true 34 | } 35 | } 36 | return false 37 | }, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoImportDot 2 | 3 | ## What is GoImportDot ? 4 | 5 | GoImportDot is a tiny tool to generate a `dot` file (used for Graphviz) of imports of golang package.It has two purpose. 6 | 7 | * Help people quickly understand how a package organization without going into details of code. 8 | * Help people find out whether a package is too confusing and needs to be refactored. 9 | 10 | ## Quick Start 11 | 12 | ``` 13 | go get -u github.com/yqylovy/goimportdot 14 | goimportdot -pkg=yourpackagename > pkg.dot 15 | dot -Tsvg pkg.dot >pkg.svg 16 | ``` 17 | 18 | ## Example 19 | 20 | Install `go.uber.org/zap` 21 | 22 | ``` 23 | go get go.uber.org/zap 24 | ``` 25 | 26 | Get a graph of `go.uber.org/zap` 27 | 28 | ``` 29 | goimportdot -pkg=go.uber.org/zap > zap.dot 30 | dot -Tpng zap.dot > zap.png 31 | ``` 32 | 33 | ![zap](./docs/zap.png) 34 | 35 | 36 | Only get a graph of zapcore in `go.uber.org/zap` 37 | 38 | ``` 39 | goimportdot -pkg=go.uber.org/zap -root=go.uber.org/zap/zapcore > zapcore.dot 40 | dot -Tpng zapcore.dot > zapcore.png 41 | ``` 42 | 43 | ![zap](./docs/zapcore.png) 44 | -------------------------------------------------------------------------------- /docs/zap.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | "go.uber.org/zap/internal/multierror"->"go.uber.org/zap/internal/bufferpool"; 3 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/bufferpool"; 4 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/exit"; 5 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/color"; 6 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/buffer"; 7 | "go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/multierror"; 8 | "go.uber.org/zap/internal/bufferpool"->"go.uber.org/zap/buffer"; 9 | "go.uber.org/zap/zapgrpc"->"go.uber.org/zap"; 10 | "go.uber.org/zap/zaptest/observer"->"go.uber.org/zap/zapcore"; 11 | "go.uber.org/zap"->"go.uber.org/zap/zapcore"; 12 | "go.uber.org/zap"->"go.uber.org/zap/internal/bufferpool"; 13 | "go.uber.org/zap"->"go.uber.org/zap/internal/multierror"; 14 | "go.uber.org/zap/internal/readme"; 15 | "go.uber.org/zap/buffer"; 16 | "go.uber.org/zap"; 17 | "go.uber.org/zap/zaptest/observer"; 18 | "go.uber.org/zap/benchmarks"; 19 | "go.uber.org/zap/internal/multierror"; 20 | "go.uber.org/zap/zaptest"; 21 | "go.uber.org/zap/internal/exit"; 22 | "go.uber.org/zap/zapgrpc"; 23 | "go.uber.org/zap/internal/bufferpool"; 24 | "go.uber.org/zap/internal/color"; 25 | "go.uber.org/zap/zapcore"; 26 | } 27 | -------------------------------------------------------------------------------- /docs/examples-cn/cronsun.node.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/db"; 3 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/conf"; 4 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/node/cron"; 5 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/log"; 6 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/db"; 7 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/log"; 8 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/event"; 9 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/utils"; 10 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/utils"; 11 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun"; 12 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/conf"; 13 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/log"; 14 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/node/cron"; 15 | "github.com/shunfei/cronsun/node/cron"; 16 | "github.com/shunfei/cronsun/log"; 17 | "github.com/shunfei/cronsun/event"; 18 | "github.com/shunfei/cronsun/utils"; 19 | "github.com/shunfei/cronsun/node"; 20 | "github.com/shunfei/cronsun"; 21 | "github.com/shunfei/cronsun/db"; 22 | "github.com/shunfei/cronsun/conf"; 23 | } 24 | -------------------------------------------------------------------------------- /core/util.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "go/parser" 5 | "go/token" 6 | "path/filepath" 7 | "strings" 8 | ) 9 | 10 | func ParseGoImport(gofile string) (ss StrSet, err error) { 11 | fset := token.NewFileSet() // positions are relative to fset 12 | f, err := parser.ParseFile(fset, gofile, nil, parser.ImportsOnly) 13 | if err != nil { 14 | return 15 | } 16 | ss = NewStrSet() 17 | for _, s := range f.Imports { 18 | ss.Put(strings.Trim(s.Path.Value, `"`)) 19 | } 20 | return 21 | } 22 | func PkgOfFile(gofile string) (pkg string) { 23 | return strings.SplitN(filepath.Dir(gofile), "/src/", 2)[1] 24 | } 25 | 26 | type StrSet map[string]bool 27 | 28 | func NewStrSet(strs ...string) StrSet { 29 | ss := StrSet(make(map[string]bool)) 30 | for _, str := range strs { 31 | ss.Put(str) 32 | } 33 | return ss 34 | } 35 | func (this StrSet) Put(str string) { this[str] = true } 36 | func (this StrSet) Del(str string) { delete(this, str) } 37 | func (this StrSet) Contains(str string) (ok bool) { _, ok = this[str]; return ok } 38 | func (this StrSet) Merge(that StrSet) { 39 | for str := range that { 40 | this[str] = true 41 | } 42 | } 43 | func (this StrSet) Array() []string { 44 | ret := make([]string, 0, len(this)) 45 | for str := range this { 46 | ret = append(ret, str) 47 | } 48 | return ret 49 | } 50 | -------------------------------------------------------------------------------- /docs/examples-cn/goimportdot_guide.md: -------------------------------------------------------------------------------- 1 | # goimportdot : 一个帮你迅速了解 golang 项目结构的工具 2 | 3 | ## 简介 4 | 5 | 很多时候,当我们想熟悉一个 `golang` 项目时,都需要能快速地对代码的整体结构有个宏观了解,初步明白项目是如何组织构成的。在有了大体的概念后,再选择适当的切入点,专注于代码的核心部分进行研究,达到熟悉项目的目的。 `goimportdot` 就是一个根据 `golang` 中 `import` 生成调用关系,再配合 `Graphviz` 生成调用图的工具。 6 | 7 | 8 | ## 安装 9 | 10 | ``` 11 | go get -u github.com/yqylovy/goimportdot 12 | ``` 13 | 14 | 在安装后会在 `$GOPATH/bin` 路径中生成 `goimportdot` 文件。 15 | 16 | --- 17 | 18 | ## 使用示例 19 | 20 | 以 `github.com/shunfei/cronsun` 作为示例,这是一个分布式任务系统,类似于 `crontab`。首先我们把代码下载下来。 21 | 22 | ``` 23 | go get github.com/shunfei/cronsun 24 | ``` 25 | 26 | 使用 `goimportdot` 对项目结构进行解析。再通过 dot 把 解析结构转化为 `png` 图片。 27 | 28 | ``` 29 | goimportdot -pkg=github.com/shunfei/cronsun > cronsun.dot 30 | dot -Tpng cronsun.dot > cronsun.png 31 | ``` 32 | 33 | 打开 `cronsun.png`,图片如下: 34 | 35 | ![cronsun](./cronsun.png) 36 | 37 | 可以看到现在项目整体结构一目了然,存在两个入口 `github.com/shunfei/cronsun/bin/node` 和 `github.com/shunfei/cronsun/bin/web`。 38 | 39 | `cronsun` 还是个轻量级、整洁的项目,可以一目了然。作者在分析更复杂的项目的时候,发现生成的调用图非常复杂,看上去像一团乱麻,难以入手。这时候需要减少信息量,逐步分析。依旧以 `cronsun` 为例。如果我们只想分析 `cronsun` 的 `node` 部分: 40 | 41 | ``` 42 | goimportdot -pkg=github.com/shunfei/cronsun -root=github.com/shunfei/cronsun/node > cronsun.node.dot 43 | dot -Tpng cronsun.node.dot > cronsun.node.png 44 | ``` 45 | 46 | 图片结果: 47 | 48 | ![cronsun.node](./cronsun.node.png) 49 | 50 | 项目中通常会存在一些辅助工具,如 `log` 包,被大量地引用,在分析时可以通过指定黑名单来达到忽略某些包的目的: 51 | 52 | ``` 53 | # 忽略其中的 log、utils 54 | goimportdot -pkg=github.com/shunfei/cronsun -root=github.com/shunfei/cronsun/node -filter=b:*utils,*log > cronsun.node.core.dot 55 | dot -Tpng cronsun.node.core.dot > cronsun.node.core.png 56 | ``` 57 | 58 | 图片结果: 59 | 60 | ![cronsun.node.core](./cronsun.node.core.png) 61 | 62 | 63 | 通过指定 `root` 和 `filter`,可以有效地减少输出,快速地把握核心。 64 | 65 | ## 结语 66 | 67 | `goimportdot`是个刚推出的小工具,还有很多不成熟的地方,欢迎提出建议。 68 | 项目地址: [https://github.com/yqylovy/goimportdot](https://github.com/yqylovy/goimportdot) 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /core/imps.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | func GetImports(pkg string, filters ...FileFilter) (pkgimports map[string]StrSet, err error) { 13 | fullpath := "" 14 | 15 | goPath := os.Getenv("GOPATH") 16 | gopaths := strings.Split(goPath, ":") 17 | for _, gp := range gopaths { 18 | fp := filepath.Join(gp, "src", pkg) 19 | if _, err := os.Stat(fp); err == nil { 20 | fullpath = fp 21 | } 22 | } 23 | if fullpath == "" { 24 | err = fmt.Errorf("Can not find package [%s] in GOPATH [%s]", pkg, goPath) 25 | return 26 | } 27 | pkgimports = make(map[string]StrSet) 28 | filepath.Walk(fullpath, func(fp string, info os.FileInfo, err error) error { 29 | if info.IsDir() { 30 | return nil 31 | } 32 | for _, filter := range filters { 33 | if !filter.IsBlack { 34 | continue 35 | } 36 | if filter.Func(fp, info, err) { 37 | return nil 38 | } 39 | } 40 | for _, filter := range filters { 41 | if filter.IsBlack { 42 | continue 43 | } 44 | if !filter.Func(fp, info, err) { 45 | return nil 46 | } 47 | } 48 | pkg := PkgOfFile(fp) 49 | if _, ok := pkgimports[pkg]; !ok { 50 | pkgimports[pkg] = NewStrSet() 51 | } 52 | ss, err := ParseGoImport(fp) 53 | if err != nil { 54 | // TODO: better err 55 | panic(err) 56 | } 57 | pkgimports[pkg].Merge(ss) 58 | return nil 59 | }) 60 | return 61 | } 62 | 63 | func WriteDot(pkgimports map[string]StrSet, writer io.Writer) (err error) { 64 | nodes := NewStrSet() 65 | edges := [][2]string{} 66 | for pkg, imps := range pkgimports { 67 | nodes.Put(pkg) 68 | for imp := range imps { 69 | nodes.Put(imp) 70 | edges = append(edges, [2]string{pkg, imp}) 71 | } 72 | } 73 | buf := bytes.NewBuffer([]byte{}) 74 | buf.WriteString("digraph G {\n") 75 | for _, edge := range edges { 76 | buf.WriteString(fmt.Sprintf(`"%s"->"%s";`, edge[0], edge[1])) 77 | buf.WriteByte('\n') 78 | } 79 | for pkg, _ := range nodes { 80 | buf.WriteString(fmt.Sprintf(`"%s";`, pkg)) 81 | buf.WriteByte('\n') 82 | } 83 | buf.WriteString("}\n") 84 | _, err = writer.Write(buf.Bytes()) 85 | return 86 | } 87 | -------------------------------------------------------------------------------- /goimportdot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/yqylovy/goimportdot/core" 9 | ) 10 | 11 | func main() { 12 | var ignoreGit = true 13 | var ignoreTest = true 14 | var onlySelfPkg = true 15 | 16 | var packageName = "" 17 | var root = "" 18 | var filters = "" 19 | 20 | var level = -1 21 | 22 | flag.BoolVar(&ignoreGit, "ignoregit", ignoreGit, "ignore files in git") 23 | flag.BoolVar(&ignoreTest, "ignoretest", ignoreTest, "ignore test files") 24 | flag.BoolVar(&onlySelfPkg, "only", onlySelfPkg, "only to draw the input package") 25 | flag.StringVar(&filters, "filter", "", "filter to (ignore/only include) package match wildcard,example: -filter=w:a*,*b;b:c means only include package start with a and ends with b, ignore package named c") 26 | flag.StringVar(&root, "root", root, "only draw package with the graph start from root") 27 | flag.IntVar(&level, "level", level, "show how many level , -1 for all") 28 | flag.StringVar(&packageName, "pkg", packageName, "the package to draw") 29 | flag.Parse() 30 | 31 | if packageName == "" { 32 | fmt.Println("You must specify the packge name with -pkg ") 33 | return 34 | } 35 | 36 | fileFilter := []core.FileFilter{ 37 | core.HasSuffix(false, ".go"), 38 | } 39 | if ignoreGit { 40 | fileFilter = append(fileFilter, core.NameContains(true, ".git")) 41 | } 42 | if ignoreTest { 43 | fileFilter = append(fileFilter, core.NameContains(true, "_test.go")) 44 | } 45 | 46 | pkgAndImports, err := core.GetImports(packageName, fileFilter...) 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | pkgFilters := []core.PkgFilter{} 52 | if onlySelfPkg { 53 | pkgFilters = append(pkgFilters, core.PkgWildcardFilter(false, packageName+"*")) 54 | } 55 | if root != "" { 56 | pkgFilters = append(pkgFilters, core.RootFilter(root)) 57 | } 58 | moreFilters, err := core.ParsePkgWildcardStr(filters) 59 | if err != nil { 60 | fmt.Printf("No right filter [%s], please check!", filters) 61 | return 62 | } 63 | pkgFilters = append(pkgFilters, moreFilters...) 64 | 65 | if level >= 0 { 66 | pkgFilters = append(pkgFilters, core.PkgLevelFilter(level)) 67 | } 68 | 69 | for _, f := range pkgFilters { 70 | pkgAndImports = f(pkgAndImports) 71 | } 72 | core.WriteDot(pkgAndImports, os.Stdout) 73 | } 74 | -------------------------------------------------------------------------------- /docs/examples-cn/cronsun.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun"; 3 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/node/cron"; 4 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/utils"; 5 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/conf"; 6 | "github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/log"; 7 | "github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun"; 8 | "github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun/conf"; 9 | "github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun/event"; 10 | "github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun/log"; 11 | "github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun/web"; 12 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/node/cron"; 13 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/conf"; 14 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/db"; 15 | "github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/log"; 16 | "github.com/shunfei/cronsun/web"->"github.com/shunfei/cronsun/conf"; 17 | "github.com/shunfei/cronsun/web"->"github.com/shunfei/cronsun"; 18 | "github.com/shunfei/cronsun/web"->"github.com/shunfei/cronsun/log"; 19 | "github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun"; 20 | "github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun/conf"; 21 | "github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun/event"; 22 | "github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun/log"; 23 | "github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun/node"; 24 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/log"; 25 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/db"; 26 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/utils"; 27 | "github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/event"; 28 | "github.com/shunfei/cronsun"; 29 | "github.com/shunfei/cronsun/conf"; 30 | "github.com/shunfei/cronsun/log"; 31 | "github.com/shunfei/cronsun/bin/web"; 32 | "github.com/shunfei/cronsun/bin/node"; 33 | "github.com/shunfei/cronsun/db/mid"; 34 | "github.com/shunfei/cronsun/node"; 35 | "github.com/shunfei/cronsun/node/cron"; 36 | "github.com/shunfei/cronsun/utils"; 37 | "github.com/shunfei/cronsun/event"; 38 | "github.com/shunfei/cronsun/web"; 39 | "github.com/shunfei/cronsun/db"; 40 | } 41 | -------------------------------------------------------------------------------- /core/pkg_filter.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "regexp" 5 | 6 | "strings" 7 | ) 8 | 9 | type PkgFilter func(map[string]StrSet) map[string]StrSet 10 | 11 | func RootFilter(root string) PkgFilter { 12 | return func(imps map[string]StrSet) (ret map[string]StrSet) { 13 | ret = make(map[string]StrSet) 14 | cur := []string{root} 15 | for len(cur) > 0 { 16 | newcur := NewStrSet() 17 | for _, pkg := range cur { 18 | if pkgimp, ok := imps[pkg]; ok { 19 | ret[pkg] = pkgimp 20 | newcur.Merge(pkgimp) 21 | } 22 | } 23 | cur = newcur.Array() 24 | } 25 | return ret 26 | } 27 | } 28 | 29 | func PkgWildcardFilter(isBlack bool, pkgs ...string) PkgFilter { 30 | regs := []*regexp.Regexp{} 31 | for _, pkg := range pkgs { 32 | rgp := regexp.MustCompile("^" + strings.Replace(pkg, "*", ".*", -1) + "$") 33 | regs = append(regs, rgp) 34 | } 35 | return func(imps map[string]StrSet) (ret map[string]StrSet) { 36 | ret = make(map[string]StrSet) 37 | BIG: 38 | for pkg, imps := range imps { 39 | for _, rgp := range regs { 40 | if isBlack == rgp.MatchString(pkg) { 41 | continue BIG 42 | } 43 | } 44 | for k := range imps { 45 | for _, rgp := range regs { 46 | if isBlack == rgp.MatchString(k) { 47 | imps.Del(k) 48 | } 49 | } 50 | } 51 | ret[pkg] = imps 52 | } 53 | return ret 54 | } 55 | } 56 | 57 | func ParsePkgWildcardStr(str string) (fs []PkgFilter, err error) { 58 | if str == "" { 59 | return 60 | } 61 | strArr := strings.Split(str, ";") 62 | for _, str := range strArr { 63 | str = strings.TrimSpace(str) 64 | wb_pkgs := strings.SplitN(str, ":", 2) 65 | fs = append(fs, PkgWildcardFilter(wb_pkgs[0] == "b", strings.Split(wb_pkgs[1], ",")...)) 66 | } 67 | return 68 | } 69 | 70 | func PkgLevelFilter(level int) PkgFilter { 71 | return func(imps map[string]StrSet) (ret map[string]StrSet) { 72 | if level < 0 { 73 | return imps 74 | } 75 | // find the root 76 | // which are not pointed to 77 | allTarget := NewStrSet() 78 | for _, targets := range imps { 79 | allTarget.Merge(targets) 80 | } 81 | levelMap := map[string]int{} 82 | for pkg := range imps { 83 | if !allTarget.Contains(pkg) { 84 | levelMap[pkg] = 0 85 | } 86 | } 87 | for i := 0; i < level; i++ { 88 | nextLevel := NewStrSet() 89 | for pkg, pkgLevel := range levelMap { 90 | if pkgLevel != i { 91 | continue 92 | } 93 | for target := range imps[pkg] { 94 | nextLevel.Put(target) 95 | } 96 | } 97 | for next := range nextLevel { 98 | levelMap[next] = i + 1 99 | } 100 | } 101 | 102 | ret = make(map[string]StrSet, len(levelMap)) 103 | for pkg, lvl := range levelMap { 104 | ret[pkg] = imps[pkg] 105 | if lvl == level { 106 | continue 107 | } 108 | } 109 | return ret 110 | } 111 | } 112 | --------------------------------------------------------------------------------