select.go

  1package _select
  2
  3import (
  4	"fmt"
  5	"io"
  6	"io/ioutil"
  7	"os"
  8	"path"
  9
 10	"github.com/pkg/errors"
 11
 12	"github.com/MichaelMure/git-bug/bug"
 13	"github.com/MichaelMure/git-bug/cache"
 14	"github.com/MichaelMure/git-bug/entity"
 15	"github.com/MichaelMure/git-bug/repository"
 16)
 17
 18const selectFile = "select"
 19
 20var ErrNoValidId = errors.New("you must provide a bug id or use the \"select\" command first")
 21
 22// ResolveBug first try to resolve a bug using the first argument of the command
 23// line. If it fails, it fallback to the select mechanism.
 24//
 25// Returns:
 26// - the bug if any
 27// - the new list of command line arguments with the bug prefix removed if it
 28//   has been used
 29// - an error if the process failed
 30func ResolveBug(repo *cache.RepoCache, args []string) (*cache.BugCache, []string, error) {
 31	// At first, try to use the first argument as a bug prefix
 32	if len(args) > 0 {
 33		b, err := repo.ResolveBugPrefix(args[0])
 34
 35		if err == nil {
 36			return b, args[1:], nil
 37		}
 38
 39		if err != bug.ErrBugNotExist {
 40			return nil, nil, err
 41		}
 42	}
 43
 44	// first arg is not a valid bug prefix, we can safely use the preselected bug if any
 45
 46	b, err := selected(repo)
 47
 48	// selected bug is invalid
 49	if err == bug.ErrBugNotExist {
 50		// we clear the selected bug
 51		err = Clear(repo)
 52		if err != nil {
 53			return nil, nil, err
 54		}
 55		return nil, nil, ErrNoValidId
 56	}
 57
 58	// another error when reading the bug
 59	if err != nil {
 60		return nil, nil, err
 61	}
 62
 63	// bug is successfully retrieved
 64	if b != nil {
 65		return b, args, nil
 66	}
 67
 68	// no selected bug and no valid first argument
 69	return nil, nil, ErrNoValidId
 70}
 71
 72func ResolveComment(repo *cache.RepoCache, fullId string) (*cache.BugCache, entity.Id, error) {
 73	bugId, _ := bug.SplitCommentId(fullId)
 74	b, _, err := ResolveBug(repo, []string{bugId})
 75	if err != nil {
 76		return nil, entity.UnsetId, err
 77	}
 78
 79	matching := make([]entity.Id, 0, 5)
 80
 81	for _, comment := range b.Snapshot().Comments {
 82		if comment.Id().HasPrefix(fullId) {
 83			matching = append(matching, comment.Id())
 84		}
 85	}
 86
 87	if len(matching) > 1 {
 88		return nil, entity.UnsetId, entity.NewErrMultipleMatch("comment", matching)
 89	} else if len(matching) == 0 {
 90		return nil, entity.UnsetId, errors.New("comment doesn't exist")
 91	}
 92
 93	return b, matching[0], nil
 94}
 95
 96// Select will select a bug for future use
 97func Select(repo *cache.RepoCache, id entity.Id) error {
 98	selectPath := selectFilePath(repo)
 99
100	f, err := os.OpenFile(selectPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
101	if err != nil {
102		return err
103	}
104
105	_, err = f.WriteString(id.String())
106	if err != nil {
107		return err
108	}
109
110	return f.Close()
111}
112
113// Clear will clear the selected bug, if any
114func Clear(repo *cache.RepoCache) error {
115	selectPath := selectFilePath(repo)
116
117	return os.Remove(selectPath)
118}
119
120func selected(repo *cache.RepoCache) (*cache.BugCache, error) {
121	selectPath := selectFilePath(repo)
122
123	f, err := os.Open(selectPath)
124	if err != nil {
125		if os.IsNotExist(err) {
126			return nil, nil
127		} else {
128			return nil, err
129		}
130	}
131
132	buf, err := ioutil.ReadAll(io.LimitReader(f, 100))
133	if err != nil {
134		return nil, err
135	}
136	if len(buf) == 100 {
137		return nil, fmt.Errorf("the select file should be < 100 bytes")
138	}
139
140	id := entity.Id(buf)
141	if err := id.Validate(); err != nil {
142		err = os.Remove(selectPath)
143		if err != nil {
144			return nil, errors.Wrap(err, "error while removing invalid select file")
145		}
146
147		return nil, fmt.Errorf("select file in invalid, removing it")
148	}
149
150	b, err := repo.ResolveBug(id)
151	if err != nil {
152		return nil, err
153	}
154
155	err = f.Close()
156	if err != nil {
157		return nil, err
158	}
159
160	return b, nil
161}
162
163func selectFilePath(repo repository.RepoCommon) string {
164	return path.Join(repo.GetPath(), "git-bug", selectFile)
165}