github.com/sdboyer/gps@v0.16.3/pkgtree/reachmap.go (about) 1 package pkgtree 2 3 import ( 4 "sort" 5 "strings" 6 7 "github.com/sdboyer/gps/internal" 8 ) 9 10 // ReachMap maps a set of import paths (keys) to the sets of transitively 11 // reachable tree-internal packages, and all the tree-external packages 12 // reachable through those internal packages. 13 // 14 // See PackageTree.ToReachMap() for more information. 15 type ReachMap map[string]struct { 16 Internal, External []string 17 } 18 19 // FlattenAll flattens a reachmap into a sorted, deduplicated list of all the 20 // external imports named by its contained packages. 21 // 22 // If stdlib is false, then stdlib imports are excluded from the result. 23 func (rm ReachMap) FlattenAll(stdlib bool) []string { 24 return rm.flatten(func(pkg string) bool { return true }, stdlib) 25 } 26 27 // Flatten flattens a reachmap into a sorted, deduplicated list of all the 28 // external imports named by its contained packages, but excludes imports coming 29 // from packages with disallowed patterns in their names: any path element with 30 // a leading dot, a leading underscore, with the name "testdata". 31 // 32 // If stdlib is false, then stdlib imports are excluded from the result. 33 func (rm ReachMap) Flatten(stdlib bool) []string { 34 f := func(pkg string) bool { 35 // Eliminate import paths with any elements having leading dots, leading 36 // underscores, or testdata. If these are internally reachable (which is 37 // a no-no, but possible), any external imports will have already been 38 // pulled up through ExternalReach. The key here is that we don't want 39 // to treat such packages as themselves being sources. 40 for _, elem := range strings.Split(pkg, "/") { 41 if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { 42 return false 43 } 44 } 45 return true 46 } 47 48 return rm.flatten(f, stdlib) 49 } 50 51 func (rm ReachMap) flatten(filter func(string) bool, stdlib bool) []string { 52 exm := make(map[string]struct{}) 53 for pkg, ie := range rm { 54 if filter(pkg) { 55 for _, ex := range ie.External { 56 if !stdlib && internal.IsStdLib(ex) { 57 continue 58 } 59 exm[ex] = struct{}{} 60 } 61 } 62 } 63 64 if len(exm) == 0 { 65 return []string{} 66 } 67 68 ex := make([]string, 0, len(exm)) 69 for p := range exm { 70 ex = append(ex, p) 71 } 72 73 sort.Strings(ex) 74 return ex 75 }