1package commands
2
3import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "strings"
8
9 "github.com/spf13/cobra"
10
11 _select "github.com/MichaelMure/git-bug/commands/select"
12 "github.com/MichaelMure/git-bug/entities/bug"
13 "github.com/MichaelMure/git-bug/util/colors"
14)
15
16type showOptions struct {
17 fields string
18 format string
19}
20
21func newShowCommand() *cobra.Command {
22 env := newEnv()
23 options := showOptions{}
24
25 cmd := &cobra.Command{
26 Use: "show [ID]",
27 Short: "Display the details of a bug.",
28 PreRunE: loadBackend(env),
29 RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error {
30 return runShow(env, options, args)
31 }),
32 ValidArgsFunction: completeBug(env),
33 }
34
35 flags := cmd.Flags()
36 flags.SortFlags = false
37
38 fields := []string{"author", "authorEmail", "createTime", "lastEdit", "humanId",
39 "id", "labels", "shortId", "status", "title", "actors", "participants"}
40 flags.StringVarP(&options.fields, "field", "", "",
41 "Select field to display. Valid values are ["+strings.Join(fields, ",")+"]")
42 cmd.RegisterFlagCompletionFunc("by", completeFrom(fields))
43 flags.StringVarP(&options.format, "format", "f", "default",
44 "Select the output formatting style. Valid values are [default,json,org-mode]")
45
46 return cmd
47}
48
49func runShow(env *Env, opts showOptions, args []string) error {
50 b, args, err := _select.ResolveBug(env.backend, args)
51 if err != nil {
52 return err
53 }
54
55 snap := b.Snapshot()
56
57 if len(snap.Comments) == 0 {
58 return errors.New("invalid bug: no comment")
59 }
60
61 if opts.fields != "" {
62 switch opts.fields {
63 case "author":
64 env.out.Printf("%s\n", snap.Author.DisplayName())
65 case "authorEmail":
66 env.out.Printf("%s\n", snap.Author.Email())
67 case "createTime":
68 env.out.Printf("%s\n", snap.CreateTime.String())
69 case "lastEdit":
70 env.out.Printf("%s\n", snap.EditTime().String())
71 case "humanId":
72 env.out.Printf("%s\n", snap.Id().Human())
73 case "id":
74 env.out.Printf("%s\n", snap.Id())
75 case "labels":
76 for _, l := range snap.Labels {
77 env.out.Printf("%s\n", l.String())
78 }
79 case "actors":
80 for _, a := range snap.Actors {
81 env.out.Printf("%s\n", a.DisplayName())
82 }
83 case "participants":
84 for _, p := range snap.Participants {
85 env.out.Printf("%s\n", p.DisplayName())
86 }
87 case "shortId":
88 env.out.Printf("%s\n", snap.Id().Human())
89 case "status":
90 env.out.Printf("%s\n", snap.Status)
91 case "title":
92 env.out.Printf("%s\n", snap.Title)
93 default:
94 return fmt.Errorf("\nUnsupported field: %s\n", opts.fields)
95 }
96
97 return nil
98 }
99
100 switch opts.format {
101 case "org-mode":
102 return showOrgModeFormatter(env, snap)
103 case "json":
104 return showJsonFormatter(env, snap)
105 case "default":
106 return showDefaultFormatter(env, snap)
107 default:
108 return fmt.Errorf("unknown format %s", opts.format)
109 }
110}
111
112func showDefaultFormatter(env *Env, snapshot *bug.Snapshot) error {
113 // Header
114 env.out.Printf("%s [%s] %s\n\n",
115 colors.Cyan(snapshot.Id().Human()),
116 colors.Yellow(snapshot.Status),
117 snapshot.Title,
118 )
119
120 env.out.Printf("%s opened this issue %s\n",
121 colors.Magenta(snapshot.Author.DisplayName()),
122 snapshot.CreateTime.String(),
123 )
124
125 env.out.Printf("This was last edited at %s\n\n",
126 snapshot.EditTime().String(),
127 )
128
129 // Labels
130 var labels = make([]string, len(snapshot.Labels))
131 for i := range snapshot.Labels {
132 labels[i] = string(snapshot.Labels[i])
133 }
134
135 env.out.Printf("labels: %s\n",
136 strings.Join(labels, ", "),
137 )
138
139 // Actors
140 var actors = make([]string, len(snapshot.Actors))
141 for i := range snapshot.Actors {
142 actors[i] = snapshot.Actors[i].DisplayName()
143 }
144
145 env.out.Printf("actors: %s\n",
146 strings.Join(actors, ", "),
147 )
148
149 // Participants
150 var participants = make([]string, len(snapshot.Participants))
151 for i := range snapshot.Participants {
152 participants[i] = snapshot.Participants[i].DisplayName()
153 }
154
155 env.out.Printf("participants: %s\n\n",
156 strings.Join(participants, ", "),
157 )
158
159 // Comments
160 indent := " "
161
162 for i, comment := range snapshot.Comments {
163 var message string
164 env.out.Printf("%s%s #%d %s <%s>\n\n",
165 indent,
166 comment.Id().Human(),
167 i,
168 comment.Author.DisplayName(),
169 comment.Author.Email(),
170 )
171
172 if comment.Message == "" {
173 message = colors.BlackBold(colors.WhiteBg("No description provided."))
174 } else {
175 message = comment.Message
176 }
177
178 env.out.Printf("%s%s\n\n\n",
179 indent,
180 message,
181 )
182 }
183
184 return nil
185}
186
187type JSONBugSnapshot struct {
188 Id string `json:"id"`
189 HumanId string `json:"human_id"`
190 CreateTime JSONTime `json:"create_time"`
191 EditTime JSONTime `json:"edit_time"`
192 Status string `json:"status"`
193 Labels []bug.Label `json:"labels"`
194 Title string `json:"title"`
195 Author JSONIdentity `json:"author"`
196 Actors []JSONIdentity `json:"actors"`
197 Participants []JSONIdentity `json:"participants"`
198 Comments []JSONComment `json:"comments"`
199}
200
201type JSONComment struct {
202 Id string `json:"id"`
203 HumanId string `json:"human_id"`
204 Author JSONIdentity `json:"author"`
205 Message string `json:"message"`
206}
207
208func NewJSONComment(comment bug.Comment) JSONComment {
209 return JSONComment{
210 Id: comment.Id().String(),
211 HumanId: comment.Id().Human(),
212 Author: NewJSONIdentity(comment.Author),
213 Message: comment.Message,
214 }
215}
216
217func showJsonFormatter(env *Env, snapshot *bug.Snapshot) error {
218 jsonBug := JSONBugSnapshot{
219 Id: snapshot.Id().String(),
220 HumanId: snapshot.Id().Human(),
221 CreateTime: NewJSONTime(snapshot.CreateTime, 0),
222 EditTime: NewJSONTime(snapshot.EditTime(), 0),
223 Status: snapshot.Status.String(),
224 Labels: snapshot.Labels,
225 Title: snapshot.Title,
226 Author: NewJSONIdentity(snapshot.Author),
227 }
228
229 jsonBug.Actors = make([]JSONIdentity, len(snapshot.Actors))
230 for i, element := range snapshot.Actors {
231 jsonBug.Actors[i] = NewJSONIdentity(element)
232 }
233
234 jsonBug.Participants = make([]JSONIdentity, len(snapshot.Participants))
235 for i, element := range snapshot.Participants {
236 jsonBug.Participants[i] = NewJSONIdentity(element)
237 }
238
239 jsonBug.Comments = make([]JSONComment, len(snapshot.Comments))
240 for i, comment := range snapshot.Comments {
241 jsonBug.Comments[i] = NewJSONComment(comment)
242 }
243
244 jsonObject, _ := json.MarshalIndent(jsonBug, "", " ")
245 env.out.Printf("%s\n", jsonObject)
246
247 return nil
248}
249
250func showOrgModeFormatter(env *Env, snapshot *bug.Snapshot) error {
251 // Header
252 env.out.Printf("%s [%s] %s\n",
253 snapshot.Id().Human(),
254 snapshot.Status,
255 snapshot.Title,
256 )
257
258 env.out.Printf("* Author: %s\n",
259 snapshot.Author.DisplayName(),
260 )
261
262 env.out.Printf("* Creation Time: %s\n",
263 snapshot.CreateTime.String(),
264 )
265
266 env.out.Printf("* Last Edit: %s\n",
267 snapshot.EditTime().String(),
268 )
269
270 // Labels
271 var labels = make([]string, len(snapshot.Labels))
272 for i, label := range snapshot.Labels {
273 labels[i] = string(label)
274 }
275
276 env.out.Printf("* Labels:\n")
277 if len(labels) > 0 {
278 env.out.Printf("** %s\n",
279 strings.Join(labels, "\n** "),
280 )
281 }
282
283 // Actors
284 var actors = make([]string, len(snapshot.Actors))
285 for i, actor := range snapshot.Actors {
286 actors[i] = fmt.Sprintf("%s %s",
287 actor.Id().Human(),
288 actor.DisplayName(),
289 )
290 }
291
292 env.out.Printf("* Actors:\n** %s\n",
293 strings.Join(actors, "\n** "),
294 )
295
296 // Participants
297 var participants = make([]string, len(snapshot.Participants))
298 for i, participant := range snapshot.Participants {
299 participants[i] = fmt.Sprintf("%s %s",
300 participant.Id().Human(),
301 participant.DisplayName(),
302 )
303 }
304
305 env.out.Printf("* Participants:\n** %s\n",
306 strings.Join(participants, "\n** "),
307 )
308
309 env.out.Printf("* Comments:\n")
310
311 for i, comment := range snapshot.Comments {
312 var message string
313 env.out.Printf("** #%d %s\n",
314 i, comment.Author.DisplayName())
315
316 if comment.Message == "" {
317 message = "No description provided."
318 } else {
319 message = strings.ReplaceAll(comment.Message, "\n", "\n: ")
320 }
321
322 env.out.Printf(": %s\n", message)
323 }
324
325 return nil
326}