show.go

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