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
 72// Select will select a bug for future use
 73func Select(repo *cache.RepoCache, id entity.Id) error {
 74	selectPath := selectFilePath(repo)
 75
 76	f, err := os.OpenFile(selectPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
 77	if err != nil {
 78		return err
 79	}
 80
 81	_, err = f.WriteString(id.String())
 82	if err != nil {
 83		return err
 84	}
 85
 86	return f.Close()
 87}
 88
 89// Clear will clear the selected bug, if any
 90func Clear(repo *cache.RepoCache) error {
 91	selectPath := selectFilePath(repo)
 92
 93	return os.Remove(selectPath)
 94}
 95
 96func selected(repo *cache.RepoCache) (*cache.BugCache, error) {
 97	selectPath := selectFilePath(repo)
 98
 99	f, err := os.Open(selectPath)
100	if err != nil {
101		if os.IsNotExist(err) {
102			return nil, nil
103		} else {
104			return nil, err
105		}
106	}
107
108	buf, err := ioutil.ReadAll(io.LimitReader(f, 100))
109	if err != nil {
110		return nil, err
111	}
112	if len(buf) == 100 {
113		return nil, fmt.Errorf("the select file should be < 100 bytes")
114	}
115
116	id := entity.Id(buf)
117	if err := id.Validate(); err != nil {
118		err = os.Remove(selectPath)
119		if err != nil {
120			return nil, errors.Wrap(err, "error while removing invalid select file")
121		}
122
123		return nil, fmt.Errorf("select file in invalid, removing it")
124	}
125
126	b, err := repo.ResolveBug(id)
127	if err != nil {
128		return nil, err
129	}
130
131	err = f.Close()
132	if err != nil {
133		return nil, err
134	}
135
136	return b, nil
137}
138
139func selectFilePath(repo repository.RepoCommon) string {
140	return path.Join(repo.GetPath(), ".git", "git-bug", selectFile)
141}