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