1package cache
  2
  3import (
  4	"fmt"
  5
  6	"github.com/pkg/errors"
  7
  8	"github.com/MichaelMure/git-bug/bug"
  9	"github.com/MichaelMure/git-bug/entity"
 10	"github.com/MichaelMure/git-bug/identity"
 11	"github.com/MichaelMure/git-bug/repository"
 12)
 13
 14func (c *RepoCache) Name() string {
 15	return c.name
 16}
 17
 18// LocalConfig give access to the repository scoped configuration
 19func (c *RepoCache) LocalConfig() repository.Config {
 20	return c.repo.LocalConfig()
 21}
 22
 23// GlobalConfig give access to the global scoped configuration
 24func (c *RepoCache) GlobalConfig() repository.Config {
 25	return c.repo.GlobalConfig()
 26}
 27
 28// AnyConfig give access to a merged local/global configuration
 29func (c *RepoCache) AnyConfig() repository.ConfigRead {
 30	return c.repo.AnyConfig()
 31}
 32
 33func (c *RepoCache) Keyring() repository.Keyring {
 34	return c.repo.Keyring()
 35}
 36
 37// GetPath returns the path to the repo.
 38func (c *RepoCache) GetPath() string {
 39	return c.repo.GetPath()
 40}
 41
 42// GetCoreEditor returns the name of the editor that the user has used to configure git.
 43func (c *RepoCache) GetCoreEditor() (string, error) {
 44	return c.repo.GetCoreEditor()
 45}
 46
 47// GetRemotes returns the configured remotes repositories.
 48func (c *RepoCache) GetRemotes() (map[string]string, error) {
 49	return c.repo.GetRemotes()
 50}
 51
 52// GetUserName returns the name the the user has used to configure git
 53func (c *RepoCache) GetUserName() (string, error) {
 54	return c.repo.GetUserName()
 55}
 56
 57// GetUserEmail returns the email address that the user has used to configure git.
 58func (c *RepoCache) GetUserEmail() (string, error) {
 59	return c.repo.GetUserEmail()
 60}
 61
 62// ReadData will attempt to read arbitrary data from the given hash
 63func (c *RepoCache) ReadData(hash repository.Hash) ([]byte, error) {
 64	return c.repo.ReadData(hash)
 65}
 66
 67// StoreData will store arbitrary data and return the corresponding hash
 68func (c *RepoCache) StoreData(data []byte) (repository.Hash, error) {
 69	return c.repo.StoreData(data)
 70}
 71
 72// Fetch retrieve updates from a remote
 73// This does not change the local bugs or identities state
 74func (c *RepoCache) Fetch(remote string) (string, error) {
 75	stdout1, err := identity.Fetch(c.repo, remote)
 76	if err != nil {
 77		return stdout1, err
 78	}
 79
 80	stdout2, err := bug.Fetch(c.repo, remote)
 81	if err != nil {
 82		return stdout2, err
 83	}
 84
 85	return stdout1 + stdout2, nil
 86}
 87
 88// MergeAll will merge all the available remote bug and identities
 89func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
 90	out := make(chan entity.MergeResult)
 91
 92	// Intercept merge results to update the cache properly
 93	go func() {
 94		defer close(out)
 95
 96		results := identity.MergeAll(c.repo, remote)
 97		for result := range results {
 98			out <- result
 99
100			if result.Err != nil {
101				continue
102			}
103
104			switch result.Status {
105			case entity.MergeStatusNew, entity.MergeStatusUpdated:
106				i := result.Entity.(*identity.Identity)
107				c.muIdentity.Lock()
108				c.identitiesExcerpts[result.Id] = NewIdentityExcerpt(i)
109				c.muIdentity.Unlock()
110			}
111		}
112
113		results = bug.MergeAll(c.repo, remote)
114		for result := range results {
115			out <- result
116
117			if result.Err != nil {
118				continue
119			}
120
121			switch result.Status {
122			case entity.MergeStatusNew, entity.MergeStatusUpdated:
123				b := result.Entity.(*bug.Bug)
124				snap := b.Compile()
125				c.muBug.Lock()
126				c.bugExcerpts[result.Id] = NewBugExcerpt(b, &snap)
127				c.muBug.Unlock()
128			}
129		}
130
131		err := c.write()
132
133		// No easy way out here ..
134		if err != nil {
135			panic(err)
136		}
137	}()
138
139	return out
140}
141
142// Push update a remote with the local changes
143func (c *RepoCache) Push(remote string) (string, error) {
144	stdout1, err := identity.Push(c.repo, remote)
145	if err != nil {
146		return stdout1, err
147	}
148
149	stdout2, err := bug.Push(c.repo, remote)
150	if err != nil {
151		return stdout2, err
152	}
153
154	return stdout1 + stdout2, nil
155}
156
157// Pull will do a Fetch + MergeAll
158// This function will return an error if a merge fail
159func (c *RepoCache) Pull(remote string) error {
160	_, err := c.Fetch(remote)
161	if err != nil {
162		return err
163	}
164
165	for merge := range c.MergeAll(remote) {
166		if merge.Err != nil {
167			return merge.Err
168		}
169		if merge.Status == entity.MergeStatusInvalid {
170			return errors.Errorf("merge failure: %s", merge.Reason)
171		}
172	}
173
174	return nil
175}
176
177func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
178	err := identity.SetUserIdentity(c.repo, i.Identity)
179	if err != nil {
180		return err
181	}
182
183	c.muIdentity.RLock()
184	defer c.muIdentity.RUnlock()
185
186	// Make sure that everything is fine
187	if _, ok := c.identities[i.Id()]; !ok {
188		panic("SetUserIdentity while the identity is not from the cache, something is wrong")
189	}
190
191	c.userIdentityId = i.Id()
192
193	return nil
194}
195
196func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) {
197	if c.userIdentityId != "" {
198		i, ok := c.identities[c.userIdentityId]
199		if ok {
200			return i, nil
201		}
202	}
203
204	c.muIdentity.Lock()
205	defer c.muIdentity.Unlock()
206
207	i, err := identity.GetUserIdentity(c.repo)
208	if err != nil {
209		return nil, err
210	}
211
212	cached := NewIdentityCache(c, i)
213	c.identities[i.Id()] = cached
214	c.userIdentityId = i.Id()
215
216	return cached, nil
217}
218
219func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) {
220	if c.userIdentityId == "" {
221		id, err := identity.GetUserIdentityId(c.repo)
222		if err != nil {
223			return nil, err
224		}
225		c.userIdentityId = id
226	}
227
228	c.muIdentity.RLock()
229	defer c.muIdentity.RUnlock()
230
231	excerpt, ok := c.identitiesExcerpts[c.userIdentityId]
232	if !ok {
233		return nil, fmt.Errorf("cache: missing identity excerpt %v", c.userIdentityId)
234	}
235	return excerpt, nil
236}
237
238func (c *RepoCache) IsUserIdentitySet() (bool, error) {
239	return identity.IsUserIdentitySet(c.repo)
240}