github.com/sdboyer/gps@v0.16.3/source_cache.go (about)

     1  package gps
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/sdboyer/gps/pkgtree"
     8  )
     9  
    10  // singleSourceCache provides a method set for storing and retrieving data about
    11  // a single source.
    12  type singleSourceCache interface {
    13  	// Store the manifest and lock information for a given revision, as defined by
    14  	// a particular ProjectAnalyzer.
    15  	setManifestAndLock(Revision, ProjectAnalyzer, Manifest, Lock)
    16  
    17  	// Get the manifest and lock information for a given revision, as defined by
    18  	// a particular ProjectAnalyzer.
    19  	getManifestAndLock(Revision, ProjectAnalyzer) (Manifest, Lock, bool)
    20  
    21  	// Store a PackageTree for a given revision.
    22  	setPackageTree(Revision, pkgtree.PackageTree)
    23  
    24  	// Get the PackageTree for a given revision.
    25  	getPackageTree(Revision) (pkgtree.PackageTree, bool)
    26  
    27  	// Indicate to the cache that an individual revision is known to exist.
    28  	markRevisionExists(r Revision)
    29  
    30  	// Store the mappings between a set of PairedVersions' surface versions
    31  	// their corresponding revisions.
    32  	//
    33  	// If flush is true, the existing list of versions will be purged before
    34  	// writing. Revisions will have their pairings purged, but record of the
    35  	// revision existing will be kept, on the assumption that revisions are
    36  	// immutable and permanent.
    37  	storeVersionMap(versionList []PairedVersion, flush bool)
    38  
    39  	// Get the list of unpaired versions corresponding to the given revision.
    40  	getVersionsFor(Revision) ([]UnpairedVersion, bool)
    41  
    42  	// Gets all the version pairs currently known to the cache.
    43  	getAllVersions() []PairedVersion
    44  
    45  	// Get the revision corresponding to the given unpaired version.
    46  	getRevisionFor(UnpairedVersion) (Revision, bool)
    47  
    48  	// Attempt to convert the given Version to a Revision, given information
    49  	// currently present in the cache, and in the Version itself.
    50  	toRevision(v Version) (Revision, bool)
    51  
    52  	// Attempt to convert the given Version to an UnpairedVersion, given
    53  	// information currently present in the cache, or in the Version itself.
    54  	//
    55  	// If the input is a revision and multiple UnpairedVersions are associated
    56  	// with it, whatever happens to be the first is returned.
    57  	toUnpaired(v Version) (UnpairedVersion, bool)
    58  }
    59  
    60  type singleSourceCacheMemory struct {
    61  	mut    sync.RWMutex // protects all maps
    62  	infos  map[ProjectAnalyzer]map[Revision]projectInfo
    63  	ptrees map[Revision]pkgtree.PackageTree
    64  	vMap   map[UnpairedVersion]Revision
    65  	rMap   map[Revision][]UnpairedVersion
    66  }
    67  
    68  func newMemoryCache() singleSourceCache {
    69  	return &singleSourceCacheMemory{
    70  		infos:  make(map[ProjectAnalyzer]map[Revision]projectInfo),
    71  		ptrees: make(map[Revision]pkgtree.PackageTree),
    72  		vMap:   make(map[UnpairedVersion]Revision),
    73  		rMap:   make(map[Revision][]UnpairedVersion),
    74  	}
    75  }
    76  
    77  type projectInfo struct {
    78  	Manifest
    79  	Lock
    80  }
    81  
    82  func (c *singleSourceCacheMemory) setManifestAndLock(r Revision, an ProjectAnalyzer, m Manifest, l Lock) {
    83  	c.mut.Lock()
    84  	inner, has := c.infos[an]
    85  	if !has {
    86  		inner = make(map[Revision]projectInfo)
    87  		c.infos[an] = inner
    88  	}
    89  	inner[r] = projectInfo{Manifest: m, Lock: l}
    90  
    91  	// Ensure there's at least an entry in the rMap so that the rMap always has
    92  	// a complete picture of the revisions we know to exist
    93  	if _, has = c.rMap[r]; !has {
    94  		c.rMap[r] = nil
    95  	}
    96  	c.mut.Unlock()
    97  }
    98  
    99  func (c *singleSourceCacheMemory) getManifestAndLock(r Revision, an ProjectAnalyzer) (Manifest, Lock, bool) {
   100  	c.mut.Lock()
   101  	defer c.mut.Unlock()
   102  
   103  	inner, has := c.infos[an]
   104  	if !has {
   105  		return nil, nil, false
   106  	}
   107  
   108  	pi, has := inner[r]
   109  	if has {
   110  		return pi.Manifest, pi.Lock, true
   111  	}
   112  	return nil, nil, false
   113  }
   114  
   115  func (c *singleSourceCacheMemory) setPackageTree(r Revision, ptree pkgtree.PackageTree) {
   116  	c.mut.Lock()
   117  	c.ptrees[r] = ptree
   118  
   119  	// Ensure there's at least an entry in the rMap so that the rMap always has
   120  	// a complete picture of the revisions we know to exist
   121  	if _, has := c.rMap[r]; !has {
   122  		c.rMap[r] = nil
   123  	}
   124  	c.mut.Unlock()
   125  }
   126  
   127  func (c *singleSourceCacheMemory) getPackageTree(r Revision) (pkgtree.PackageTree, bool) {
   128  	c.mut.Lock()
   129  	ptree, has := c.ptrees[r]
   130  	c.mut.Unlock()
   131  	return ptree, has
   132  }
   133  
   134  func (c *singleSourceCacheMemory) storeVersionMap(versionList []PairedVersion, flush bool) {
   135  	c.mut.Lock()
   136  	if flush {
   137  		// TODO(sdboyer) how do we handle cache consistency here - revs that may
   138  		// be out of date vis-a-vis the ptrees or infos maps?
   139  		for r := range c.rMap {
   140  			c.rMap[r] = nil
   141  		}
   142  
   143  		c.vMap = make(map[UnpairedVersion]Revision)
   144  	}
   145  
   146  	for _, v := range versionList {
   147  		pv := v.(PairedVersion)
   148  		u, r := pv.Unpair(), pv.Underlying()
   149  		c.vMap[u] = r
   150  		c.rMap[r] = append(c.rMap[r], u)
   151  	}
   152  	c.mut.Unlock()
   153  }
   154  
   155  func (c *singleSourceCacheMemory) markRevisionExists(r Revision) {
   156  	c.mut.Lock()
   157  	if _, has := c.rMap[r]; !has {
   158  		c.rMap[r] = nil
   159  	}
   160  	c.mut.Unlock()
   161  }
   162  
   163  func (c *singleSourceCacheMemory) getVersionsFor(r Revision) ([]UnpairedVersion, bool) {
   164  	c.mut.Lock()
   165  	versionList, has := c.rMap[r]
   166  	c.mut.Unlock()
   167  	return versionList, has
   168  }
   169  
   170  func (c *singleSourceCacheMemory) getAllVersions() []PairedVersion {
   171  	vlist := make([]PairedVersion, 0, len(c.vMap))
   172  	for v, r := range c.vMap {
   173  		vlist = append(vlist, v.Is(r))
   174  	}
   175  	return vlist
   176  }
   177  
   178  func (c *singleSourceCacheMemory) getRevisionFor(uv UnpairedVersion) (Revision, bool) {
   179  	c.mut.Lock()
   180  	r, has := c.vMap[uv]
   181  	c.mut.Unlock()
   182  	return r, has
   183  }
   184  
   185  func (c *singleSourceCacheMemory) toRevision(v Version) (Revision, bool) {
   186  	switch t := v.(type) {
   187  	case Revision:
   188  		return t, true
   189  	case PairedVersion:
   190  		return t.Underlying(), true
   191  	case UnpairedVersion:
   192  		c.mut.Lock()
   193  		r, has := c.vMap[t]
   194  		c.mut.Unlock()
   195  		return r, has
   196  	default:
   197  		panic(fmt.Sprintf("Unknown version type %T", v))
   198  	}
   199  }
   200  
   201  func (c *singleSourceCacheMemory) toUnpaired(v Version) (UnpairedVersion, bool) {
   202  	switch t := v.(type) {
   203  	case UnpairedVersion:
   204  		return t, true
   205  	case PairedVersion:
   206  		return t.Unpair(), true
   207  	case Revision:
   208  		c.mut.Lock()
   209  		upv, has := c.rMap[t]
   210  		c.mut.Unlock()
   211  
   212  		if has && len(upv) > 0 {
   213  			return upv[0], true
   214  		}
   215  		return nil, false
   216  	default:
   217  		panic(fmt.Sprintf("unknown version type %T", v))
   218  	}
   219  }