ls: Add support to ls dump bug information in specific formats

vince created

This adds an optional flag to the ls command that allows users to specify the format they wish to dump the output as. Currently, supported parameters are 'default', 'plain' and 'json'.

Change summary

commands/ls.go                     | 126 ++++++++++++++++++++++++++++++-
doc/man/git-bug-ls.1               |   4 +
doc/md/git-bug_ls.md               |   1 
misc/bash_completion/git-bug       |   4 +
misc/powershell_completion/git-bug |   2 
misc/zsh_completion/git-bug        |   3 
6 files changed, 134 insertions(+), 6 deletions(-)

Detailed changes

commands/ls.go 🔗

@@ -1,8 +1,10 @@
 package commands
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
+	"time"
 
 	text "github.com/MichaelMure/go-term-text"
 	"github.com/spf13/cobra"
@@ -21,9 +23,10 @@ var (
 	lsNoQuery       []string
 	lsSortBy        string
 	lsSortDirection string
+	lsOutputFormat  string
 )
 
-func runLsBug(cmd *cobra.Command, args []string) error {
+func runLsBug(_ *cobra.Command, args []string) error {
 	backend, err := cache.NewRepoCache(repo)
 	if err != nil {
 		return err
@@ -48,20 +51,125 @@ func runLsBug(cmd *cobra.Command, args []string) error {
 
 	allIds := backend.QueryBugs(q)
 
-	for _, id := range allIds {
+	bugExcerpt := make([]*cache.BugExcerpt, len(allIds))
+	for i, id := range allIds {
 		b, err := backend.ResolveBugExcerpt(id)
 		if err != nil {
 			return err
 		}
+		bugExcerpt[i] = b
+	}
+
+	switch lsOutputFormat {
+	case "plain":
+		return lsPlainFormatter(backend, bugExcerpt)
+	case "json":
+		return lsJsonFormatter(backend, bugExcerpt)
+	case "default":
+		return lsDefaultFormatter(backend, bugExcerpt)
+	default:
+		return fmt.Errorf("unknown format %s", lsOutputFormat)
+	}
+}
 
+type JSONBug struct {
+	Id           string    `json:"id"`
+	HumanId      string    `json:"human_id"`
+	CreationTime time.Time `json:"creation_time"`
+	LastEdited   time.Time `json:"last_edited"`
+
+	Status       string         `json:"status"`
+	Labels       []bug.Label    `json:"labels"`
+	Title        string         `json:"title"`
+	Actors       []JSONIdentity `json:"actors"`
+	Participants []JSONIdentity `json:"participants"`
+	Author       JSONIdentity   `json:"author"`
+
+	Comments int               `json:"comments"`
+	Metadata map[string]string `json:"metadata"`
+}
+
+type JSONIdentity struct {
+	Id      string `json:"id"`
+	HumanId string `json:"human_id"`
+	Name    string `json:"name"`
+	Login   string `json:"login"`
+}
+
+func lsJsonFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) error {
+	for _, b := range bugExcerpts {
+		jsonBug := JSONBug{
+			b.Id.String(),
+			b.Id.Human(),
+			time.Unix(b.CreateUnixTime, 0),
+			time.Unix(b.EditUnixTime, 0),
+			b.Status.String(),
+			b.Labels,
+			b.Title,
+			[]JSONIdentity{},
+			[]JSONIdentity{},
+			JSONIdentity{},
+			b.LenComments,
+			b.CreateMetadata,
+		}
+
+		if b.AuthorId != "" {
+			author, err := backend.ResolveIdentityExcerpt(b.AuthorId)
+			if err != nil {
+				return err
+			}
+
+			jsonBug.Author.Name = author.DisplayName()
+			jsonBug.Author.Login = author.Login
+			jsonBug.Author.Id = author.Id.String()
+			jsonBug.Author.HumanId = author.Id.Human()
+		} else {
+			jsonBug.Author.Name = b.LegacyAuthor.DisplayName()
+			jsonBug.Author.Login = b.LegacyAuthor.Login
+		}
+
+		for _, element := range b.Actors {
+			actor, err := backend.ResolveIdentityExcerpt(element)
+			if err != nil {
+				return err
+			}
+
+			jsonBug.Actors = append(jsonBug.Actors, JSONIdentity{
+				actor.Id.String(),
+				actor.Id.Human(),
+				actor.Name,
+				actor.Login,
+			})
+		}
+
+		for _, element := range b.Participants {
+			participant, err := backend.ResolveIdentityExcerpt(element)
+			if err != nil {
+				return err
+			}
+			jsonBug.Participants = append(jsonBug.Participants, JSONIdentity{
+				participant.Id.String(),
+				participant.Id.Human(),
+				participant.DisplayName(),
+				participant.Login,
+			})
+		}
+
+		jsonObject, _ := json.MarshalIndent(jsonBug, "", "    ")
+		fmt.Printf("%s\n", jsonObject)
+	}
+	return nil
+}
+
+func lsDefaultFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) error {
+	for _, b := range bugExcerpts {
 		var name string
 		if b.AuthorId != "" {
 			author, err := backend.ResolveIdentityExcerpt(b.AuthorId)
 			if err != nil {
-				name = "<missing author data>"
-			} else {
-				name = author.DisplayName()
+				return err
 			}
+			name = author.DisplayName()
 		} else {
 			name = b.LegacyAuthor.DisplayName()
 		}
@@ -92,7 +200,13 @@ func runLsBug(cmd *cobra.Command, args []string) error {
 			comments,
 		)
 	}
+	return nil
+}
 
+func lsPlainFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) error {
+	for _, b := range bugExcerpts {
+		fmt.Printf("[%s] %s\n", b.Status, b.Title)
+	}
 	return nil
 }
 
@@ -177,4 +291,6 @@ func init() {
 		"Sort the results by a characteristic. Valid values are [id,creation,edit]")
 	lsCmd.Flags().StringVarP(&lsSortDirection, "direction", "d", "asc",
 		"Select the sorting direction. Valid values are [asc,desc]")
+	lsCmd.Flags().StringVarP(&lsOutputFormat, "format", "f", "default",
+		"Select the output formatting style. Valid values are [default, plain(text), json]")
 }

doc/man/git-bug-ls.1 🔗

@@ -57,6 +57,10 @@ You can pass an additional query to filter and order the list. This query can be
 \fB\-d\fP, \fB\-\-direction\fP="asc"
 	Select the sorting direction. Valid values are [asc,desc]
 
+.PP
+\fB\-f\fP, \fB\-\-format\fP="default"
+	Select the output formatting style. Valid values are [default, plain(text), json]
+
 .PP
 \fB\-h\fP, \fB\-\-help\fP[=false]
 	help for ls

doc/md/git-bug_ls.md 🔗

@@ -35,6 +35,7 @@ git bug ls --status closed --by creation
   -n, --no strings            Filter by absence of something. Valid values are [label]
   -b, --by string             Sort the results by a characteristic. Valid values are [id,creation,edit] (default "creation")
   -d, --direction string      Select the sorting direction. Valid values are [asc,desc] (default "asc")
+  -f, --format string         Select the output formatting style. Valid values are [default, plain(text), json] (default "default")
   -h, --help                  help for ls
 ```
 

misc/bash_completion/git-bug 🔗

@@ -807,6 +807,10 @@ _git-bug_ls()
     two_word_flags+=("--direction")
     two_word_flags+=("-d")
     local_nonpersistent_flags+=("--direction=")
+    flags+=("--format=")
+    two_word_flags+=("--format")
+    two_word_flags+=("-f")
+    local_nonpersistent_flags+=("--format=")
 
     must_have_one_flag=()
     must_have_one_noun=()

misc/powershell_completion/git-bug 🔗

@@ -159,6 +159,8 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
             [CompletionResult]::new('--by', 'by', [CompletionResultType]::ParameterName, 'Sort the results by a characteristic. Valid values are [id,creation,edit]')
             [CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Select the sorting direction. Valid values are [asc,desc]')
             [CompletionResult]::new('--direction', 'direction', [CompletionResultType]::ParameterName, 'Select the sorting direction. Valid values are [asc,desc]')
+            [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default, plain(text), json]')
+            [CompletionResult]::new('--format', 'format', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default, plain(text), json]')
             break
         }
         'git-bug;ls-id' {

misc/zsh_completion/git-bug 🔗

@@ -303,7 +303,8 @@ function _git-bug_ls {
     '(*-t *--title)'{\*-t,\*--title}'[Filter by title]:' \
     '(*-n *--no)'{\*-n,\*--no}'[Filter by absence of something. Valid values are [label]]:' \
     '(-b --by)'{-b,--by}'[Sort the results by a characteristic. Valid values are [id,creation,edit]]:' \
-    '(-d --direction)'{-d,--direction}'[Select the sorting direction. Valid values are [asc,desc]]:'
+    '(-d --direction)'{-d,--direction}'[Select the sorting direction. Valid values are [asc,desc]]:' \
+    '(-f --format)'{-f,--format}'[Select the output formatting style. Valid values are [default, plain(text), json]]:'
 }
 
 function _git-bug_ls-id {