cache.go

  1package cache
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	"github.com/MichaelMure/git-bug/bug"
  8	"github.com/MichaelMure/git-bug/repository"
  9)
 10
 11type Cache interface {
 12	RegisterRepository(ref string, repo repository.Repo)
 13	RegisterDefaultRepository(repo repository.Repo)
 14
 15	ResolveRepo(ref string) (CachedRepo, error)
 16	DefaultRepo() (CachedRepo, error)
 17
 18	// Shortcut to resolve on the default repo for convenience
 19	DefaultResolveBug(id string) (CachedBug, error)
 20	DefaultResolveBugPrefix(prefix string) (CachedBug, error)
 21}
 22
 23type CachedRepo interface {
 24	ResolveBug(id string) (CachedBug, error)
 25	ResolveBugPrefix(prefix string) (CachedBug, error)
 26	ClearAllBugs()
 27}
 28
 29type CachedBug interface {
 30	Snapshot() bug.Snapshot
 31	ClearSnapshot()
 32}
 33
 34// Cache ------------------------
 35
 36type DefaultCache struct {
 37	repos map[string]CachedRepo
 38}
 39
 40func NewDefaultCache() Cache {
 41	return &DefaultCache{
 42		repos: make(map[string]CachedRepo),
 43	}
 44}
 45
 46func (c *DefaultCache) RegisterRepository(ref string, repo repository.Repo) {
 47	c.repos[ref] = NewCachedRepo(repo)
 48}
 49
 50func (c *DefaultCache) RegisterDefaultRepository(repo repository.Repo) {
 51	c.repos[""] = NewCachedRepo(repo)
 52}
 53
 54func (c *DefaultCache) DefaultRepo() (CachedRepo, error) {
 55	if len(c.repos) != 1 {
 56		return nil, fmt.Errorf("repository is not unique")
 57	}
 58
 59	for _, r := range c.repos {
 60		return r, nil
 61	}
 62
 63	panic("unreachable")
 64}
 65
 66func (c *DefaultCache) ResolveRepo(ref string) (CachedRepo, error) {
 67	r, ok := c.repos[ref]
 68	if !ok {
 69		return nil, fmt.Errorf("unknown repo")
 70	}
 71	return r, nil
 72}
 73
 74func (c *DefaultCache) DefaultResolveBug(id string) (CachedBug, error) {
 75	repo, err := c.DefaultRepo()
 76
 77	if err != nil {
 78		return nil, err
 79	}
 80
 81	return repo.ResolveBug(id)
 82}
 83
 84func (c *DefaultCache) DefaultResolveBugPrefix(prefix string) (CachedBug, error) {
 85	repo, err := c.DefaultRepo()
 86
 87	if err != nil {
 88		return nil, err
 89	}
 90
 91	return repo.ResolveBugPrefix(prefix)
 92}
 93
 94// Repo ------------------------
 95
 96type CachedRepoImpl struct {
 97	repo repository.Repo
 98	bugs map[string]CachedBug
 99}
100
101func NewCachedRepo(r repository.Repo) CachedRepo {
102	return &CachedRepoImpl{
103		repo: r,
104		bugs: make(map[string]CachedBug),
105	}
106}
107
108func (c CachedRepoImpl) ResolveBug(id string) (CachedBug, error) {
109	cached, ok := c.bugs[id]
110	if ok {
111		return cached, nil
112	}
113
114	b, err := bug.ReadLocalBug(c.repo, id)
115	if err != nil {
116		return nil, err
117	}
118
119	cached = NewCachedBug(b)
120	c.bugs[id] = cached
121
122	return cached, nil
123}
124
125func (c CachedRepoImpl) ResolveBugPrefix(prefix string) (CachedBug, error) {
126	// preallocate but empty
127	matching := make([]string, 0, 5)
128
129	for id := range c.bugs {
130		if strings.HasPrefix(id, prefix) {
131			matching = append(matching, id)
132		}
133	}
134
135	// TODO: should check matching bug in the repo as well
136
137	if len(matching) > 1 {
138		return nil, fmt.Errorf("Multiple matching bug found:\n%s", strings.Join(matching, "\n"))
139	}
140
141	if len(matching) == 1 {
142		b := c.bugs[matching[0]]
143		return b, nil
144	}
145
146	b, err := bug.FindLocalBug(c.repo, prefix)
147
148	if err != nil {
149		return nil, err
150	}
151
152	cached := NewCachedBug(b)
153	c.bugs[b.Id()] = cached
154
155	return cached, nil
156}
157
158func (c CachedRepoImpl) ClearAllBugs() {
159	c.bugs = make(map[string]CachedBug)
160}
161
162// Bug ------------------------
163
164type CachedBugImpl struct {
165	bug  *bug.Bug
166	snap *bug.Snapshot
167}
168
169func NewCachedBug(b *bug.Bug) CachedBug {
170	return &CachedBugImpl{
171		bug: b,
172	}
173}
174
175func (c CachedBugImpl) Snapshot() bug.Snapshot {
176	if c.snap == nil {
177		snap := c.bug.Compile()
178		c.snap = &snap
179	}
180	return *c.snap
181}
182
183func (c CachedBugImpl) ClearSnapshot() {
184	c.snap = nil
185}