bug_show.go

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