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