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}