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}