select.go

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