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