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