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}