aboutsummaryrefslogtreecommitdiffstats
path: root/commands/select/select.go
blob: cf861fcc885ad5b9eb2a28d502af8b9976c98be5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package _select

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"

	"github.com/pkg/errors"

	"github.com/MichaelMure/git-bug/bug"
	"github.com/MichaelMure/git-bug/cache"
	"github.com/MichaelMure/git-bug/entity"
	"github.com/MichaelMure/git-bug/repository"
)

const selectFile = "select"

var ErrNoValidId = errors.New("you must provide a bug id or use the \"select\" command first")

// ResolveBug first try to resolve a bug using the first argument of the command
// line. If it fails, it fallback to the select mechanism.
//
// Returns:
// - the bug if any
// - the new list of command line arguments with the bug prefix removed if it
//   has been used
// - an error if the process failed
func ResolveBug(repo *cache.RepoCache, args []string) (*cache.BugCache, []string, error) {
	// At first, try to use the first argument as a bug prefix
	if len(args) > 0 {
		b, err := repo.ResolveBugPrefix(args[0])

		if err == nil {
			return b, args[1:], nil
		}

		if err != bug.ErrBugNotExist {
			return nil, nil, err
		}
	}

	// first arg is not a valid bug prefix, we can safely use the preselected bug if any

	b, err := selected(repo)

	// selected bug is invalid
	if err == bug.ErrBugNotExist {
		// we clear the selected bug
		err = Clear(repo)
		if err != nil {
			return nil, nil, err
		}
		return nil, nil, ErrNoValidId
	}

	// another error when reading the bug
	if err != nil {
		return nil, nil, err
	}

	// bug is successfully retrieved
	if b != nil {
		return b, args, nil
	}

	// no selected bug and no valid first argument
	return nil, nil, ErrNoValidId
}

// Select will select a bug for future use
func Select(repo *cache.RepoCache, id entity.Id) error {
	selectPath := selectFilePath(repo)

	f, err := os.OpenFile(selectPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}

	_, err = f.WriteString(id.String())
	if err != nil {
		return err
	}

	return f.Close()
}

// Clear will clear the selected bug, if any
func Clear(repo *cache.RepoCache) error {
	selectPath := selectFilePath(repo)

	return os.Remove(selectPath)
}

func selected(repo *cache.RepoCache) (*cache.BugCache, error) {
	selectPath := selectFilePath(repo)

	f, err := os.Open(selectPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		} else {
			return nil, err
		}
	}

	buf, err := ioutil.ReadAll(io.LimitReader(f, 100))
	if err != nil {
		return nil, err
	}
	if len(buf) == 100 {
		return nil, fmt.Errorf("the select file should be < 100 bytes")
	}

	id := entity.Id(buf)
	if err := id.Validate(); err != nil {
		err = os.Remove(selectPath)
		if err != nil {
			return nil, errors.Wrap(err, "error while removing invalid select file")
		}

		return nil, fmt.Errorf("select file in invalid, removing it")
	}

	b, err := repo.ResolveBug(id)
	if err != nil {
		return nil, err
	}

	err = f.Close()
	if err != nil {
		return nil, err
	}

	return b, nil
}

func selectFilePath(repo repository.RepoCommon) string {
	return path.Join(repo.GetPath(), "git-bug", selectFile)
}