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/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}