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/repository"
 15)
 16
 17const selectFile = "select"
 18
 19var ErrNoValidId = errors.New("you must provide a bug id or use the \"select\" command first")
 20
 21// ResolveBug first try to resolve a bug using the first argument of the command
 22// line. If it fails, it fallback to the select mechanism.
 23//
 24// Returns:
 25// - the bug if any
 26// - the new list of command line arguments with the bug prefix removed if it
 27//   has been used
 28// - an error if the process failed
 29func ResolveBug(repo *cache.RepoCache, args []string) (*cache.BugCache, []string, error) {
 30	// At first, try to use the first argument as a bug prefix
 31	if len(args) > 0 {
 32		b, err := repo.ResolveBugPrefix(args[0])
 33
 34		if err == nil {
 35			return b, args[1:], nil
 36		}
 37
 38		if err != bug.ErrBugNotExist {
 39			return nil, nil, err
 40		}
 41	}
 42
 43	// first arg is not a valid bug prefix, we can safely use the preselected bug if any
 44
 45	b, err := selected(repo)
 46
 47	// selected bug is invalid
 48	if err == bug.ErrBugNotExist {
 49		// we clear the selected bug
 50		err = Clear(repo)
 51		if err != nil {
 52			return nil, nil, err
 53		}
 54		return nil, nil, ErrNoValidId
 55	}
 56
 57	// another error when reading the bug
 58	if err != nil {
 59		return nil, nil, err
 60	}
 61
 62	// bug is successfully retrieved
 63	if b != nil {
 64		return b, args, nil
 65	}
 66
 67	// no selected bug and no valid first argument
 68	return nil, nil, ErrNoValidId
 69}
 70
 71// Select will select a bug for future use
 72func Select(repo *cache.RepoCache, id string) error {
 73	selectPath := selectFilePath(repo)
 74
 75	f, err := os.OpenFile(selectPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
 76	if err != nil {
 77		return err
 78	}
 79
 80	_, err = f.WriteString(id)
 81	if err != nil {
 82		return err
 83	}
 84
 85	return f.Close()
 86}
 87
 88// Clear will clear the selected bug, if any
 89func Clear(repo *cache.RepoCache) error {
 90	selectPath := selectFilePath(repo)
 91
 92	return os.Remove(selectPath)
 93}
 94
 95func selected(repo *cache.RepoCache) (*cache.BugCache, error) {
 96	selectPath := selectFilePath(repo)
 97
 98	f, err := os.Open(selectPath)
 99	if err != nil {
100		if os.IsNotExist(err) {
101			return nil, nil
102		} else {
103			return nil, err
104		}
105	}
106
107	buf, err := ioutil.ReadAll(io.LimitReader(f, 100))
108	if err != nil {
109		return nil, err
110	}
111	if len(buf) == 100 {
112		return nil, fmt.Errorf("the select file should be < 100 bytes")
113	}
114
115	id := string(buf)
116	if !bug.IDIsValid(id) {
117		err = os.Remove(selectPath)
118		if err != nil {
119			return nil, errors.Wrap(err, "error while removing invalid select file")
120		}
121
122		return nil, fmt.Errorf("select file in invalid, removing it")
123	}
124
125	b, err := repo.ResolveBug(id)
126	if err != nil {
127		return nil, err
128	}
129
130	err = f.Close()
131	if err != nil {
132		return nil, err
133	}
134
135	return b, nil
136}
137
138func selectFilePath(repo repository.RepoCommon) string {
139	return path.Join(repo.GetPath(), ".git", "git-bug", selectFile)
140}