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