1package commands
2
3import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "github.com/MichaelMure/git-bug/bug"
8 "github.com/MichaelMure/git-bug/cache"
9 _select "github.com/MichaelMure/git-bug/commands/select"
10 "github.com/MichaelMure/git-bug/util/colors"
11 "github.com/MichaelMure/git-bug/util/interrupt"
12 "github.com/spf13/cobra"
13 "strings"
14 "time"
15)
16
17var (
18 showFieldsQuery string
19 showOutputFormat string
20)
21
22func runShowBug(_ *cobra.Command, args []string) error {
23 backend, err := cache.NewRepoCache(repo)
24 if err != nil {
25 return err
26 }
27 defer backend.Close()
28 interrupt.RegisterCleaner(backend.Close)
29
30 b, args, err := _select.ResolveBug(backend, args)
31 if err != nil {
32 return err
33 }
34
35 snapshot := b.Snapshot()
36
37 if len(snapshot.Comments) == 0 {
38 return errors.New("invalid bug: no comment")
39 }
40
41 if showFieldsQuery != "" {
42 switch showFieldsQuery {
43 case "author":
44 fmt.Printf("%s\n", snapshot.Author.DisplayName())
45 case "authorEmail":
46 fmt.Printf("%s\n", snapshot.Author.Email())
47 case "createTime":
48 fmt.Printf("%s\n", snapshot.CreatedAt.String())
49 case "lastEdit":
50 fmt.Printf("%s\n", snapshot.LastEditTime().String())
51 case "humanId":
52 fmt.Printf("%s\n", snapshot.Id().Human())
53 case "id":
54 fmt.Printf("%s\n", snapshot.Id())
55 case "labels":
56 for _, l := range snapshot.Labels {
57 fmt.Printf("%s\n", l.String())
58 }
59 case "actors":
60 for _, a := range snapshot.Actors {
61 fmt.Printf("%s\n", a.DisplayName())
62 }
63 case "participants":
64 for _, p := range snapshot.Participants {
65 fmt.Printf("%s\n", p.DisplayName())
66 }
67 case "shortId":
68 fmt.Printf("%s\n", snapshot.Id().Human())
69 case "status":
70 fmt.Printf("%s\n", snapshot.Status)
71 case "title":
72 fmt.Printf("%s\n", snapshot.Title)
73 default:
74 return fmt.Errorf("\nUnsupported field: %s\n", showFieldsQuery)
75 }
76
77 return nil
78 }
79
80 switch showOutputFormat {
81 case "org-mode":
82 return showOrgmodeFormatter(snapshot)
83 case "json":
84 return showJsonFormatter(snapshot)
85 case "plain":
86 return showPlainFormatter(snapshot)
87 case "default":
88 return showDefaultFormatter(snapshot)
89 default:
90 return fmt.Errorf("unknown format %s", showOutputFormat)
91 }
92}
93
94func showDefaultFormatter(snapshot *bug.Snapshot) error {
95 // Header
96 fmt.Printf("%s [%s] %s\n\n",
97 colors.Cyan(snapshot.Id().Human()),
98 colors.Yellow(snapshot.Status),
99 snapshot.Title,
100 )
101
102 fmt.Printf("%s opened this issue %s\n",
103 colors.Magenta(snapshot.Author.DisplayName()),
104 snapshot.CreatedAt.String(),
105 )
106
107 fmt.Printf("This was last edited at %s\n\n",
108 snapshot.LastEditTime().String(),
109 )
110
111 // Labels
112 var labels = make([]string, len(snapshot.Labels))
113 for i := range snapshot.Labels {
114 labels[i] = string(snapshot.Labels[i])
115 }
116
117 fmt.Printf("labels: %s\n",
118 strings.Join(labels, ", "),
119 )
120
121 // Actors
122 var actors = make([]string, len(snapshot.Actors))
123 for i := range snapshot.Actors {
124 actors[i] = snapshot.Actors[i].DisplayName()
125 }
126
127 fmt.Printf("actors: %s\n",
128 strings.Join(actors, ", "),
129 )
130
131 // Participants
132 var participants = make([]string, len(snapshot.Participants))
133 for i := range snapshot.Participants {
134 participants[i] = snapshot.Participants[i].DisplayName()
135 }
136
137 fmt.Printf("participants: %s\n\n",
138 strings.Join(participants, ", "),
139 )
140
141 // Comments
142 indent := " "
143
144 for i, comment := range snapshot.Comments {
145 var message string
146 fmt.Printf("%s#%d %s <%s>\n\n",
147 indent,
148 i,
149 comment.Author.DisplayName(),
150 comment.Author.Email(),
151 )
152
153 if comment.Message == "" {
154 message = colors.GreyBold("No description provided.")
155 } else {
156 message = comment.Message
157 }
158
159 fmt.Printf("%s%s\n\n\n",
160 indent,
161 message,
162 )
163 }
164
165 return nil
166}
167
168func showPlainFormatter(snapshot *bug.Snapshot) error {
169 // Header
170 fmt.Printf("%s [%s] %s\n",
171 snapshot.Id().Human(),
172 snapshot.Status,
173 snapshot.Title,
174 )
175
176 fmt.Printf("author: %s\n",
177 snapshot.Author.DisplayName(),
178 )
179
180 fmt.Printf("creation time: %s\n",
181 snapshot.CreatedAt.String(),
182 )
183
184 fmt.Printf("last edit: %s\n",
185 snapshot.LastEditTime().String(),
186 )
187
188 // Labels
189 var labels = make([]string, len(snapshot.Labels))
190 for i := range snapshot.Labels {
191 labels[i] = string(snapshot.Labels[i])
192 }
193
194 fmt.Printf("labels: %s\n",
195 strings.Join(labels, ", "),
196 )
197
198 // Actors
199 var actors = make([]string, len(snapshot.Actors))
200 for i := range snapshot.Actors {
201 actors[i] = snapshot.Actors[i].DisplayName()
202 }
203
204 fmt.Printf("actors: %s\n",
205 strings.Join(actors, ", "),
206 )
207
208 // Participants
209 var participants = make([]string, len(snapshot.Participants))
210 for i := range snapshot.Participants {
211 participants[i] = snapshot.Participants[i].DisplayName()
212 }
213
214 fmt.Printf("participants: %s\n",
215 strings.Join(participants, ", "),
216 )
217
218 // Comments
219 indent := " "
220
221 for i, comment := range snapshot.Comments {
222 var message string
223 fmt.Printf("%s#%d %s <%s>\n",
224 indent,
225 i,
226 comment.Author.DisplayName(),
227 comment.Author.Email(),
228 )
229
230 if comment.Message == "" {
231 message = "No description provided."
232 } else {
233 message = comment.Message
234 }
235
236 fmt.Printf("%s%s\n",
237 indent,
238 strings.ReplaceAll(message, "\n", fmt.Sprintf("\n%s", indent)),
239 )
240 }
241
242 return nil
243}
244
245type JSONBugSnapshot struct {
246 Id string `json:"id"`
247 HumanId string `json:"human_id"`
248 CreationTime time.Time `json:"creation_time"`
249 LastEdited time.Time `json:"last_edited"`
250
251 Status string `json:"status"`
252 Labels []bug.Label `json:"labels"`
253 Title string `json:"title"`
254 Author JSONIdentity `json:"author"`
255 Actors []JSONIdentity `json:"actors"`
256 Participants []JSONIdentity `json:"participants"`
257
258 Comments []JSONComment `json:"comments"`
259}
260
261type JSONComment struct {
262 Id int `json:"id"`
263 AuthorName string `json:"author_name"`
264 AuthorLogin string `json:"author_login"`
265 Message string `json:"message"`
266}
267
268func showJsonFormatter(snapshot *bug.Snapshot) error {
269 jsonBug := JSONBugSnapshot{
270 snapshot.Id().String(),
271 snapshot.Id().Human(),
272 snapshot.CreatedAt,
273 snapshot.LastEditTime(),
274 snapshot.Status.String(),
275 snapshot.Labels,
276 snapshot.Title,
277 JSONIdentity{},
278 []JSONIdentity{},
279 []JSONIdentity{},
280 []JSONComment{},
281 }
282
283 author, err := NewJSONIdentity(snapshot.Author)
284 if err != nil {
285 return err
286 }
287 jsonBug.Author = author
288
289 for _, element := range snapshot.Actors {
290 actor, err := NewJSONIdentity(element)
291 if err != nil {
292 return err
293 }
294 jsonBug.Actors = append(jsonBug.Actors, actor)
295 }
296
297 for _, element := range snapshot.Participants {
298 participant, err := NewJSONIdentity(element)
299 if err != nil {
300 return err
301 }
302 jsonBug.Participants = append(jsonBug.Participants, participant)
303 }
304
305 for i, comment := range snapshot.Comments {
306 var message string
307 if comment.Message == "" {
308 message = "No description provided."
309 } else {
310 message = comment.Message
311 }
312 jsonBug.Comments = append(jsonBug.Comments, JSONComment{
313 i,
314 comment.Author.Name(),
315 comment.Author.Login(),
316 message,
317 })
318 }
319
320 jsonObject, _ := json.MarshalIndent(jsonBug, "", " ")
321 fmt.Printf("%s\n", jsonObject)
322
323 return nil
324}
325
326func showOrgmodeFormatter(snapshot *bug.Snapshot) error {
327 // Header
328 fmt.Printf("%s [%s] %s\n",
329 snapshot.Id().Human(),
330 snapshot.Status,
331 snapshot.Title,
332 )
333
334 fmt.Printf("* Author: %s\n",
335 snapshot.Author.DisplayName(),
336 )
337
338 fmt.Printf("* Creation Time: %s\n",
339 snapshot.CreatedAt.String(),
340 )
341
342 fmt.Printf("* Last Edit: %s\n",
343 snapshot.LastEditTime().String(),
344 )
345
346 // Labels
347 var labels = make([]string, len(snapshot.Labels))
348 for i, label := range snapshot.Labels {
349 labels[i] = string(label)
350 }
351
352 fmt.Printf("* Labels:\n")
353 if len(labels) > 0 {
354 fmt.Printf("** %s\n",
355 strings.Join(labels, "\n** "),
356 )
357 }
358
359 // Actors
360 var actors = make([]string, len(snapshot.Actors))
361 for i, actor := range snapshot.Actors {
362 actors[i] = fmt.Sprintf("%s %s",
363 actor.Id().Human(),
364 actor.DisplayName(),
365 )
366 }
367
368 fmt.Printf("* Actors:\n** %s\n",
369 strings.Join(actors, "\n** "),
370 )
371
372 // Participants
373 var participants = make([]string, len(snapshot.Participants))
374 for i, participant := range snapshot.Participants {
375 participants[i] = fmt.Sprintf("%s %s",
376 participant.Id().Human(),
377 participant.DisplayName(),
378 )
379 }
380
381 fmt.Printf("* Participants:\n** %s\n",
382 strings.Join(participants, "\n** "),
383 )
384
385 fmt.Printf("* Comments:\n")
386
387 for i, comment := range snapshot.Comments {
388 var message string
389 fmt.Printf("** #%d %s\n",
390 i,
391 comment.Author.DisplayName(),
392 )
393
394 if comment.Message == "" {
395 message = "No description provided."
396 } else {
397 message = strings.ReplaceAll(comment.Message, "\n", "\n: ")
398 }
399
400 fmt.Printf(": %s\n",
401 message,
402 )
403 }
404
405 return nil
406}
407
408var showCmd = &cobra.Command{
409 Use: "show [<id>]",
410 Short: "Display the details of a bug.",
411 PreRunE: loadRepo,
412 RunE: runShowBug,
413}
414
415func init() {
416 RootCmd.AddCommand(showCmd)
417 showCmd.Flags().StringVarP(&showFieldsQuery, "field", "", "",
418 "Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants]")
419 showCmd.Flags().StringVarP(&showOutputFormat, "format", "f", "default",
420 "Select the output formatting style. Valid values are [default,plain,json,org-mode]")
421}