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  }