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