Skip to content

Commit

Permalink
board: CLI tooling
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelMure committed Dec 27, 2022
1 parent 19fa67a commit 1507092
Show file tree
Hide file tree
Showing 39 changed files with 1,243 additions and 29 deletions.
16 changes: 8 additions & 8 deletions cache/board_excerpt.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ type BoardExcerpt struct {
CreateUnixTime int64
EditUnixTime int64

Title string
Description string
ItemCount int
Actors []entity.Id
Title string
Description string
ItemCount int
Participants []entity.Id

CreateMetadata map[string]string
}

func NewBoardExcerpt(b *BoardCache) *BoardExcerpt {
snap := b.Snapshot()

actorsIds := make([]entity.Id, 0, len(snap.Actors))
for _, actor := range snap.Actors {
actorsIds = append(actorsIds, actor.Id())
participantsIds := make([]entity.Id, 0, len(snap.Participants))
for _, participant := range snap.Participants {
participantsIds = append(participantsIds, participant.Id())
}

return &BoardExcerpt{
Expand All @@ -50,7 +50,7 @@ func NewBoardExcerpt(b *BoardCache) *BoardExcerpt {
Title: snap.Title,
Description: snap.Description,
ItemCount: snap.ItemCount(),
Actors: actorsIds,
Participants: participantsIds,
CreateMetadata: b.FirstOp().AllMetadata(),
}
}
Expand Down
178 changes: 178 additions & 0 deletions commands/board/board.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package boardcmd

import (
"encoding/json"
"fmt"
"strings"

text "github.com/MichaelMure/go-term-text"
"github.com/spf13/cobra"

"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/commands/cmdjson"
"github.com/MichaelMure/git-bug/commands/completion"
"github.com/MichaelMure/git-bug/commands/execenv"
"github.com/MichaelMure/git-bug/util/colors"
)

type boardOptions struct {
metadataQuery []string
actorQuery []string
titleQuery []string
outputFormat string
}

func NewBoardCommand() *cobra.Command {
env := execenv.NewEnv()
options := boardOptions{}

cmd := &cobra.Command{
Use: "board",
Short: "List boards",
PreRunE: execenv.LoadBackend(env),
RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error {
return runBoard(env, options, args)
}),
}

flags := cmd.Flags()
flags.SortFlags = false

flags.StringSliceVarP(&options.metadataQuery, "metadata", "m", nil,
"Filter by metadata. Example: github-url=URL")
cmd.RegisterFlagCompletionFunc("author", completion.UserForQuery(env))
flags.StringSliceVarP(&options.actorQuery, "actor", "A", nil,
"Filter by actor")
cmd.RegisterFlagCompletionFunc("actor", completion.UserForQuery(env))
flags.StringSliceVarP(&options.titleQuery, "title", "t", nil,
"Filter by title")
flags.StringVarP(&options.outputFormat, "format", "f", "default",
"Select the output formatting style. Valid values are [default,plain,compact,id,json,org-mode]")
cmd.RegisterFlagCompletionFunc("format",
completion.From([]string{"default", "id", "json"}))

const selectGroup = "select"
cmd.AddGroup(&cobra.Group{ID: selectGroup, Title: "Implicit selection"})

addCmdWithGroup := func(child *cobra.Command, groupID string) {
cmd.AddCommand(child)
child.GroupID = groupID
}

addCmdWithGroup(newBoardDeselectCommand(), selectGroup)
addCmdWithGroup(newBoardSelectCommand(), selectGroup)

cmd.AddCommand(newBoardNewCommand())
cmd.AddCommand(newBoardRmCommand())
// cmd.AddCommand(newBugShowCommand())
cmd.AddCommand(newBoardDescriptionCommand())
cmd.AddCommand(newBoardTitleCommand())
cmd.AddCommand(newBoardAddDraftCommand())

return cmd
}

func runBoard(env *execenv.Env, opts boardOptions, args []string) error {
// TODO: query

allIds := env.Backend.Boards().AllIds()

excerpts := make([]*cache.BoardExcerpt, len(allIds))
for i, id := range allIds {
b, err := env.Backend.Boards().ResolveExcerpt(id)
if err != nil {
return err
}
excerpts[i] = b
}

switch opts.outputFormat {
case "json":
return boardJsonFormatter(env, excerpts)
case "id":
return boardIDFormatter(env, excerpts)
case "default":
return boardDefaultFormatter(env, excerpts)
default:
return fmt.Errorf("unknown format %s", opts.outputFormat)
}
}

func boardIDFormatter(env *execenv.Env, excerpts []*cache.BoardExcerpt) error {
for _, b := range excerpts {
env.Out.Println(b.Id().String())
}

return nil
}

func boardDefaultFormatter(env *execenv.Env, excerpts []*cache.BoardExcerpt) error {
for _, b := range excerpts {
// truncate + pad if needed
titleFmt := text.LeftPadMaxLine(strings.TrimSpace(b.Title), 50, 0)
descFmt := text.LeftPadMaxLine(strings.TrimSpace(b.Description), 50, 0)

var itemFmt string
switch {
case b.ItemCount < 1:
itemFmt = "empty"
case b.ItemCount < 1000:
itemFmt = fmt.Sprintf("%3d 📝", b.ItemCount)
default:
itemFmt = " ∞ 📝"

}

env.Out.Printf("%s\t%s\t%s\t%s\n",
colors.Cyan(b.Id().Human()),
titleFmt,
descFmt,
itemFmt,
)
}
return nil
}

type JSONBoardExcerpt struct {
Id string `json:"id"`
HumanId string `json:"human_id"`
CreateTime cmdjson.Time `json:"create_time"`
EditTime cmdjson.Time `json:"edit_time"`

Title string `json:"title"`
Description string `json:"description"`
Participants []cmdjson.Identity `json:"participants"`

Items int `json:"items"`
Metadata map[string]string `json:"metadata"`
}

func boardJsonFormatter(env *execenv.Env, excerpts []*cache.BoardExcerpt) error {
res := make([]JSONBoardExcerpt, len(excerpts))
for i, b := range excerpts {
jsonBoard := JSONBoardExcerpt{
Id: b.Id().String(),
HumanId: b.Id().Human(),
CreateTime: cmdjson.NewTime(b.CreateTime(), b.CreateLamportTime),
EditTime: cmdjson.NewTime(b.EditTime(), b.EditLamportTime),
Title: b.Title,
Description: b.Description,
Items: b.ItemCount,
Metadata: b.CreateMetadata,
}

jsonBoard.Participants = make([]cmdjson.Identity, len(b.Participants))
for i, element := range b.Participants {
participant, err := env.Backend.Identities().ResolveExcerpt(element)
if err != nil {
return err
}
jsonBoard.Participants[i] = cmdjson.NewIdentityFromExcerpt(participant)
}

res[i] = jsonBoard
}
jsonObject, _ := json.MarshalIndent(res, "", " ")
env.Out.Printf("%s\n", jsonObject)
return nil
}
76 changes: 76 additions & 0 deletions commands/board/board_adddraft.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package boardcmd

import (
"github.com/spf13/cobra"

buginput "github.com/MichaelMure/git-bug/commands/bug/input"
"github.com/MichaelMure/git-bug/commands/execenv"
)

type boardAddDraftOptions struct {
title string
messageFile string
message string
column string
nonInteractive bool
}

func newBoardAddDraftCommand() *cobra.Command {
env := execenv.NewEnv()
options := boardAddDraftOptions{}

cmd := &cobra.Command{
Use: "add-draft [BOARD_ID]",
Short: "Add a draft item to a board",
PreRunE: execenv.LoadBackend(env),
RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error {
return runBoardAddDraft(env, options, args)
}),
ValidArgsFunction: BoardCompletion(env),
}

flags := cmd.Flags()
flags.SortFlags = false

flags.StringVarP(&options.title, "title", "t", "",
"Provide the title to describe the draft item")
flags.StringVarP(&options.message, "message", "m", "",
"Provide the message of the draft item")
flags.StringVarP(&options.messageFile, "file", "F", "",
"Take the message from the given file. Use - to read the message from the standard input")
flags.StringVarP(&options.column, "column", "1",
"The column to add to. Either a column Id or prefix, or ")
flags.BoolVar(&options.nonInteractive, "non-interactive", false, "Do not ask for user input")

return cmd
}

func runBoardAddDraft(env *execenv.Env, opts boardAddDraftOptions, args []string) error {
b, args, err := ResolveSelected(env.Backend, args)
if err != nil {
return err
}

if opts.messageFile != "" && opts.message == "" {
// Note: reuse the bug inputs
opts.title, opts.message, err = buginput.BugCreateFileInput(opts.messageFile)
if err != nil {
return err
}
}

if !opts.nonInteractive && opts.messageFile == "" && (opts.message == "" || opts.title == "") {
opts.title, opts.message, err = buginput.BugCreateEditorInput(env.Backend, opts.title, opts.message)
if err == buginput.ErrEmptyTitle {
env.Out.Println("Empty title, aborting.")
return nil
}
if err != nil {
return err
}
}

b.AddItemDraft()

return nil
}
38 changes: 38 additions & 0 deletions commands/board/board_description.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package boardcmd

import (
"github.com/spf13/cobra"

"github.com/MichaelMure/git-bug/commands/execenv"
)

func newBoardDescriptionCommand() *cobra.Command {
env := execenv.NewEnv()

cmd := &cobra.Command{
Use: "description [BOARD_ID]",
Short: "Display the description of a board",
PreRunE: execenv.LoadBackend(env),
RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error {
return runBoardDescription(env, args)
}),
ValidArgsFunction: BoardCompletion(env),
}

cmd.AddCommand(newBoardDescriptionEditCommand())

return cmd
}

func runBoardDescription(env *execenv.Env, args []string) error {
b, args, err := ResolveSelected(env.Backend, args)
if err != nil {
return err
}

snap := b.Snapshot()

env.Out.Println(snap.Description)

return nil
}

0 comments on commit 1507092

Please sign in to comment.