Add tea issues --fields
, allow printing labels #312
|
@ -5,6 +5,11 @@
|
|||
package flags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/tea/modules/utils"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
@ -91,3 +96,30 @@ var IssuePRFlags = append([]cli.Flag{
|
|||
&PaginationPageFlag,
|
||||
&PaginationLimitFlag,
|
||||
}, AllDefaultFlags...)
|
||||
|
||||
// FieldsFlag generates a flag selecting printable fields.
|
||||
// To retrieve the value, use GetFields()
|
||||
func FieldsFlag(availableFields, defaultFields []string) *cli.StringFlag {
|
||||
return &cli.StringFlag{
|
||||
Name: "fields",
|
||||
Aliases: []string{"f"},
|
||||
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
|
||||
%s
|
||||
`, strings.Join(availableFields, ",")),
|
||||
Value: strings.Join(defaultFields, ","),
|
||||
}
|
||||
}
|
||||
|
||||
// GetFields parses the values provided in a fields flag, and
|
||||
// optionally validates against valid values.
|
||||
func GetFields(ctx *cli.Context, validFields []string) ([]string, error) {
|
||||
selection := strings.Split(ctx.String("fields"), ",")
|
||||
if validFields != nil {
|
||||
for _, field := range selection {
|
||||
if !utils.Contains(validFields, field) {
|
||||
return nil, fmt.Errorf("Invalid field '%s'", field)
|
||||
}
|
||||
}
|
||||
}
|
||||
return selection, nil
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"code.gitea.io/tea/cmd/flags"
|
||||
"code.gitea.io/tea/cmd/issues"
|
||||
"code.gitea.io/tea/modules/context"
|
||||
"code.gitea.io/tea/modules/print"
|
||||
|
@ -29,7 +28,7 @@ var CmdIssues = cli.Command{
|
|||
&issues.CmdIssuesReopen,
|
||||
&issues.CmdIssuesClose,
|
||||
},
|
||||
Flags: flags.IssuePRFlags,
|
||||
Flags: issues.CmdIssuesList.Flags,
|
||||
}
|
||||
|
||||
func runIssues(ctx *cli.Context) error {
|
||||
|
|
|
@ -20,7 +20,11 @@ var CmdIssuesList = cli.Command{
|
|||
Usage: "List issues of the repository",
|
||||
Description: `List issues of the repository`,
|
||||
Action: RunIssuesList,
|
||||
Flags: flags.IssuePRFlags,
|
||||
Flags: append([]cli.Flag{
|
||||
flags.FieldsFlag(print.IssueFields, []string{
|
||||
"index", "title", "state", "author", "milestone", "labels",
|
||||
}),
|
||||
}, flags.IssuePRFlags...),
|
||||
}
|
||||
|
||||
// RunIssuesList list issues
|
||||
|
@ -48,6 +52,11 @@ func RunIssuesList(cmd *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
print.IssuesList(issues, ctx.Output)
|
||||
fields, err := flags.GetFields(cmd, print.IssueFields)
|
||||
noerw marked this conversation as resolved
Outdated
6543
commented
Outdated
Review
```diff
- fields, _ := flags.GetFields(cmd, nil)
+ fields, err := flags.GetFields(cmd, print.IssueFields)
+ if err != nil {
+ return err
+ }
```
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
print.IssuesPullsList(issues, ctx.Output, fields)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"code.gitea.io/tea/cmd/flags"
|
||||
"code.gitea.io/tea/cmd/milestones"
|
||||
"code.gitea.io/tea/modules/context"
|
||||
"code.gitea.io/tea/modules/print"
|
||||
|
@ -30,7 +29,7 @@ var CmdMilestones = cli.Command{
|
|||
&milestones.CmdMilestonesReopen,
|
||||
&milestones.CmdMilestonesIssues,
|
||||
},
|
||||
Flags: flags.AllDefaultFlags,
|
||||
Flags: milestones.CmdMilestonesList.Flags,
|
||||
}
|
||||
|
||||
func runMilestones(ctx *cli.Context) error {
|
||||
|
|
|
@ -40,6 +40,9 @@ var CmdMilestonesIssues = cli.Command{
|
|||
},
|
||||
&flags.PaginationPageFlag,
|
||||
&flags.PaginationLimitFlag,
|
||||
flags.FieldsFlag(print.IssueFields, []string{
|
||||
"index", "kind", "title", "state", "updated", "labels",
|
||||
}),
|
||||
}, flags.AllDefaultFlags...),
|
||||
}
|
||||
|
||||
|
@ -107,7 +110,11 @@ func runMilestoneIssueList(cmd *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
print.IssuesPullsList(issues, ctx.Output)
|
||||
fields, err := flags.GetFields(cmd, print.IssueFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
print.IssuesPullsList(issues, ctx.Output, fields)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -6,28 +6,11 @@ package repos
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/tea/modules/print"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// printFieldsFlag provides a selection of fields to print
|
||||
var printFieldsFlag = cli.StringFlag{
|
||||
Name: "fields",
|
||||
Aliases: []string{"f"},
|
||||
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
|
||||
%s
|
||||
`, strings.Join(print.RepoFields, ",")),
|
||||
Value: "owner,name,type,ssh",
|
||||
}
|
||||
|
||||
func getFields(ctx *cli.Context) []string {
|
||||
return strings.Split(ctx.String("fields"), ",")
|
||||
}
|
||||
|
||||
var typeFilterFlag = cli.StringFlag{
|
||||
Name: "type",
|
||||
Aliases: []string{"T"},
|
||||
|
|
|
@ -27,7 +27,9 @@ var CmdReposListFlags = append([]cli.Flag{
|
|||
Required: false,
|
||||
Usage: "List your starred repos instead",
|
||||
},
|
||||
&printFieldsFlag,
|
||||
flags.FieldsFlag(print.RepoFields, []string{
|
||||
"owner", "name", "type", "ssh",
|
||||
}),
|
||||
&typeFilterFlag,
|
||||
&flags.PaginationPageFlag,
|
||||
&flags.PaginationLimitFlag,
|
||||
|
@ -80,7 +82,12 @@ func RunReposList(cmd *cli.Context) error {
|
|||
reposFiltered = filterReposByType(rps, typeFilter)
|
||||
}
|
||||
|
||||
print.ReposList(reposFiltered, ctx.Output, getFields(cmd))
|
||||
fields, err := flags.GetFields(cmd, print.RepoFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
print.ReposList(reposFiltered, ctx.Output, fields)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,9 @@ var CmdReposSearch = cli.Command{
|
|||
Required: false,
|
||||
Usage: "Filter archived repos (true|false)",
|
||||
},
|
||||
&printFieldsFlag,
|
||||
flags.FieldsFlag(print.RepoFields, []string{
|
||||
"owner", "name", "type", "ssh",
|
||||
}),
|
||||
&flags.PaginationPageFlag,
|
||||
&flags.PaginationLimitFlag,
|
||||
}, flags.LoginOutputFlags...),
|
||||
|
@ -123,6 +125,10 @@ func runReposSearch(cmd *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
print.ReposList(rps, ctx.Output, getFields(cmd))
|
||||
fields, err := flags.GetFields(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
print.ReposList(rps, ctx.Output, fields)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package print
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// formatSize get kb in int and return string
|
||||
func formatSize(kb int64) string {
|
||||
if kb < 1024 {
|
||||
return fmt.Sprintf("%d Kb", kb)
|
||||
}
|
||||
mb := kb / 1024
|
||||
if mb < 1024 {
|
||||
return fmt.Sprintf("%d Mb", mb)
|
||||
}
|
||||
gb := mb / 1024
|
||||
if gb < 1024 {
|
||||
return fmt.Sprintf("%d Gb", gb)
|
||||
}
|
||||
return fmt.Sprintf("%d Tb", gb/1024)
|
||||
}
|
||||
|
||||
// FormatTime give a date-time in local timezone if available
|
||||
func FormatTime(t time.Time) string {
|
||||
location, err := time.LoadLocation("Local")
|
||||
if err != nil {
|
||||
return t.Format("2006-01-02 15:04 UTC")
|
||||
}
|
||||
return t.In(location).Format("2006-01-02 15:04")
|
||||
}
|
||||
|
||||
func formatDuration(seconds int64, outputType string) string {
|
||||
if isMachineReadable(outputType) {
|
||||
return fmt.Sprint(seconds)
|
||||
}
|
||||
return time.Duration(1e9 * seconds).String()
|
||||
}
|
||||
|
||||
func formatLabel(label *gitea.Label, allowColor bool, text string) string {
|
||||
colorProfile := termenv.Ascii
|
||||
if allowColor {
|
||||
colorProfile = termenv.EnvColorProfile()
|
||||
}
|
||||
if len(text) == 0 {
|
||||
text = label.Name
|
||||
}
|
||||
styled := termenv.String(text)
|
||||
styled = styled.Foreground(colorProfile.Color("#" + label.Color))
|
||||
return fmt.Sprint(styled)
|
||||
}
|
||||
|
||||
func formatPermission(p *gitea.Permission) string {
|
||||
if p.Admin {
|
||||
return "admin"
|
||||
} else if p.Push {
|
||||
return "write"
|
||||
}
|
||||
return "read"
|
||||
}
|
||||
|
||||
func formatUserName(u *gitea.User) string {
|
||||
if len(u.FullName) == 0 {
|
||||
return u.UserName
|
||||
}
|
||||
return u.FullName
|
||||
}
|
|
@ -6,7 +6,7 @@ package print
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
@ -24,68 +24,103 @@ func IssueDetails(issue *gitea.Issue) {
|
|||
))
|
||||
}
|
||||
|
||||
// IssuesList prints a listing of issues
|
||||
func IssuesList(issues []*gitea.Issue, output string) {
|
||||
t := tableWithHeader(
|
||||
"Index",
|
||||
"Title",
|
||||
"State",
|
||||
"Author",
|
||||
"Milestone",
|
||||
"Updated",
|
||||
)
|
||||
|
||||
for _, issue := range issues {
|
||||
author := issue.Poster.FullName
|
||||
if len(author) == 0 {
|
||||
author = issue.Poster.UserName
|
||||
}
|
||||
mile := ""
|
||||
if issue.Milestone != nil {
|
||||
mile = issue.Milestone.Title
|
||||
}
|
||||
t.addRow(
|
||||
strconv.FormatInt(issue.Index, 10),
|
||||
issue.Title,
|
||||
string(issue.State),
|
||||
author,
|
||||
mile,
|
||||
FormatTime(issue.Updated),
|
||||
)
|
||||
}
|
||||
t.print(output)
|
||||
}
|
||||
|
||||
// IssuesPullsList prints a listing of issues & pulls
|
||||
// TODO combine with IssuesList
|
||||
func IssuesPullsList(issues []*gitea.Issue, output string) {
|
||||
t := tableWithHeader(
|
||||
"Index",
|
||||
"State",
|
||||
"Kind",
|
||||
"Author",
|
||||
"Updated",
|
||||
"Title",
|
||||
)
|
||||
func IssuesPullsList(issues []*gitea.Issue, output string, fields []string) {
|
||||
printIssues(issues, output, fields)
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
name := issue.Poster.FullName
|
||||
if len(name) == 0 {
|
||||
name = issue.Poster.UserName
|
||||
// IssueFields are all available fields to print with IssuesList()
|
||||
var IssueFields = []string{
|
||||
"index",
|
||||
"state",
|
||||
"kind",
|
||||
"author",
|
||||
"author-id",
|
||||
"url",
|
||||
|
||||
"title",
|
||||
"body",
|
||||
|
||||
"created",
|
||||
"updated",
|
||||
"deadline",
|
||||
|
||||
"assignees",
|
||||
"milestone",
|
||||
"labels",
|
||||
"comments",
|
||||
}
|
||||
|
||||
func printIssues(issues []*gitea.Issue, output string, fields []string) {
|
||||
labelMap := map[int64]string{}
|
||||
var printables = make([]printable, len(issues))
|
||||
|
||||
for i, x := range issues {
|
||||
// pre-serialize labels for performance
|
||||
for _, label := range x.Labels {
|
||||
if _, ok := labelMap[label.ID]; !ok {
|
||||
labelMap[label.ID] = formatLabel(label, !isMachineReadable(output), "")
|
||||
}
|
||||
}
|
||||
kind := "Issue"
|
||||
if issue.PullRequest != nil {
|
||||
kind = "Pull"
|
||||
}
|
||||
t.addRow(
|
||||
strconv.FormatInt(issue.Index, 10),
|
||||
string(issue.State),
|
||||
kind,
|
||||
name,
|
||||
FormatTime(issue.Updated),
|
||||
issue.Title,
|
||||
)
|
||||
// store items with printable interface
|
||||
printables[i] = &printableIssue{x, &labelMap}
|
||||
}
|
||||
|
||||
t := tableFromItems(fields, printables)
|
||||
t.print(output)
|
||||
}
|
||||
|
||||
type printableIssue struct {
|
||||
*gitea.Issue
|
||||
formattedLabels *map[int64]string
|
||||
}
|
||||
|
||||
func (x printableIssue) FormatField(field string) string {
|
||||
switch field {
|
||||
case "index":
|
||||
return fmt.Sprintf("%d", x.Index)
|
||||
case "state":
|
||||
return string(x.State)
|
||||
case "kind":
|
||||
if x.PullRequest != nil {
|
||||
return "Pull"
|
||||
}
|
||||
return "Issue"
|
||||
case "author":
|
||||
return formatUserName(x.Poster)
|
||||
case "author-id":
|
||||
return x.Poster.UserName
|
||||
case "url":
|
||||
return x.HTMLURL
|
||||
case "title":
|
||||
return x.Title
|
||||
case "body":
|
||||
return x.Body
|
||||
case "created":
|
||||
return FormatTime(x.Created)
|
||||
case "updated":
|
||||
return FormatTime(x.Updated)
|
||||
case "deadline":
|
||||
return FormatTime(*x.Deadline)
|
||||
case "milestone":
|
||||
if x.Milestone != nil {
|
||||
return x.Milestone.Title
|
||||
}
|
||||
return ""
|
||||
case "labels":
|
||||
var labels = make([]string, len(x.Labels))
|
||||
for i, l := range x.Labels {
|
||||
labels[i] = (*x.formattedLabels)[l.ID]
|
||||
}
|
||||
return strings.Join(labels, " ")
|
||||
case "assignees":
|
||||
var assignees = make([]string, len(x.Assignees))
|
||||
for i, a := range x.Assignees {
|
||||
assignees[i] = formatUserName(a)
|
||||
}
|
||||
return strings.Join(assignees, " ")
|
||||
case "comments":
|
||||
return fmt.Sprintf("%d", x.Comments)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
package print
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// LabelsList prints a listing of labels
|
||||
|
@ -21,14 +19,10 @@ func LabelsList(labels []*gitea.Label, output string) {
|
|||
"Description",
|
||||
)
|
||||
|
||||
p := termenv.ColorProfile()
|
||||
|
||||
for _, label := range labels {
|
||||
color := termenv.String(label.Color)
|
||||
|
||||
t.addRow(
|
||||
strconv.FormatInt(label.ID, 10),
|
||||
fmt.Sprint(color.Background(p.Color("#"+label.Color))),
|
||||
formatLabel(label, !isMachineReadable(output), label.Color),
|
||||
label.Name,
|
||||
label.Description,
|
||||
)
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package print
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// formatSize get kb in int and return string
|
||||
func formatSize(kb int64) string {
|
||||
if kb < 1024 {
|
||||
return fmt.Sprintf("%d Kb", kb)
|
||||
}
|
||||
mb := kb / 1024
|
||||
if mb < 1024 {
|
||||
return fmt.Sprintf("%d Mb", mb)
|
||||
}
|
||||
gb := mb / 1024
|
||||
if gb < 1024 {
|
||||
return fmt.Sprintf("%d Gb", gb)
|
||||
}
|
||||
return fmt.Sprintf("%d Tb", gb/1024)
|
||||
}
|
||||
|
||||
// FormatTime give a date-time in local timezone if available
|
||||
func FormatTime(t time.Time) string {
|
||||
location, err := time.LoadLocation("Local")
|
||||
if err != nil {
|
||||
return t.Format("2006-01-02 15:04 UTC")
|
||||
}
|
||||
return t.In(location).Format("2006-01-02 15:04")
|
||||
}
|
|
@ -6,91 +6,19 @@ package print
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
type rp = *gitea.Repository
|
||||
type fieldFormatter = func(*gitea.Repository) string
|
||||
|
||||
var (
|
||||
fieldFormatters map[string]fieldFormatter
|
||||
|
||||
// RepoFields are the available fields to print with ReposList()
|
||||
RepoFields []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
fieldFormatters = map[string]fieldFormatter{
|
||||
"description": func(r rp) string { return r.Description },
|
||||
"forks": func(r rp) string { return fmt.Sprintf("%d", r.Forks) },
|
||||
"id": func(r rp) string { return r.FullName },
|
||||
"name": func(r rp) string { return r.Name },
|
||||
"owner": func(r rp) string { return r.Owner.UserName },
|
||||
"stars": func(r rp) string { return fmt.Sprintf("%d", r.Stars) },
|
||||
"ssh": func(r rp) string { return r.SSHURL },
|
||||
"updated": func(r rp) string { return FormatTime(r.Updated) },
|
||||
"url": func(r rp) string { return r.HTMLURL },
|
||||
"permission": func(r rp) string {
|
||||
if r.Permissions.Admin {
|
||||
return "admin"
|
||||
} else if r.Permissions.Push {
|
||||
return "write"
|
||||
}
|
||||
return "read"
|
||||
},
|
||||
"type": func(r rp) string {
|
||||
if r.Fork {
|
||||
return "fork"
|
||||
}
|
||||
if r.Mirror {
|
||||
return "mirror"
|
||||
}
|
||||
return "source"
|
||||
},
|
||||
}
|
||||
|
||||
for f := range fieldFormatters {
|
||||
RepoFields = append(RepoFields, f)
|
||||
}
|
||||
}
|
||||
|
||||
// ReposList prints a listing of the repos
|
||||
func ReposList(repos []*gitea.Repository, output string, fields []string) {
|
||||
if len(repos) == 0 {
|
||||
fmt.Println("No repositories found")
|
||||
return
|
||||
var printables = make([]printable, len(repos))
|
||||
for i, r := range repos {
|
||||
printables[i] = &printableRepo{r}
|
||||
}
|
||||
|
||||
if len(fields) == 0 {
|
||||
fmt.Println("No fields to print")
|
||||
return
|
||||
}
|
||||
|
||||
formatters := make([]fieldFormatter, len(fields))
|
||||
values := make([][]string, len(repos))
|
||||
|
||||
// find field format functions by header name
|
||||
for i, f := range fields {
|
||||
if formatter, ok := fieldFormatters[strings.ToLower(f)]; ok {
|
||||
formatters[i] = formatter
|
||||
} else {
|
||||
log.Fatalf("invalid field '%s'", f)
|
||||
}
|
||||
}
|
||||
|
||||
// extract values from each repo and store them in 2D table
|
||||
for i, repo := range repos {
|
||||
values[i] = make([]string, len(formatters))
|
||||
for j, format := range formatters {
|
||||
values[i][j] = format(repo)
|
||||
}
|
||||
}
|
||||
|
||||
t := table{headers: fields, values: values}
|
||||
t := tableFromItems(fields, printables)
|
||||
t.print(output)
|
||||
}
|
||||
|
||||
|
@ -142,7 +70,7 @@ func RepoDetails(repo *gitea.Repository, topics []string) {
|
|||
|
||||
perm := fmt.Sprintf(
|
||||
"- Permission:\t%s\n",
|
||||
fieldFormatters["permission"](repo),
|
||||
formatPermission(repo.Permissions),
|
||||
)
|
||||
|
||||
var tops string
|
||||
|
@ -161,3 +89,54 @@ func RepoDetails(repo *gitea.Repository, topics []string) {
|
|||
tops,
|
||||
))
|
||||
}
|
||||
|
||||
// RepoFields are the available fields to print with ReposList()
|
||||
var RepoFields = []string{
|
||||
"description",
|
||||
"forks",
|
||||
"id",
|
||||
"name",
|
||||
"owner",
|
||||
"stars",
|
||||
"ssh",
|
||||
"updated",
|
||||
"url",
|
||||
"permission",
|
||||
"type",
|
||||
}
|
||||
|
||||
type printableRepo struct{ *gitea.Repository }
|
||||
|
||||
func (x printableRepo) FormatField(field string) string {
|
||||
switch field {
|
||||
case "description":
|
||||
return x.Description
|
||||
case "forks":
|
||||
return fmt.Sprintf("%d", x.Forks)
|
||||
case "id":
|
||||
return x.FullName
|
||||
case "name":
|
||||
return x.Name
|
||||
case "owner":
|
||||
return x.Owner.UserName
|
||||
case "stars":
|
||||
return fmt.Sprintf("%d", x.Stars)
|
||||
case "ssh":
|
||||
return x.SSHURL
|
||||
case "updated":
|
||||
return FormatTime(x.Updated)
|
||||
case "url":
|
||||
return x.HTMLURL
|
||||
case "permission":
|
||||
return formatPermission(x.Permissions)
|
||||
case "type":
|
||||
if x.Fork {
|
||||
return "fork"
|
||||
}
|
||||
if x.Mirror {
|
||||
return "mirror"
|
||||
}
|
||||
return "source"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -22,6 +22,24 @@ type table struct {
|
|||
sortColumn uint // ↑
|
||||
}
|
||||
|
||||
// printable can be implemented for structs to put fields dynamically into a table
|
||||
type printable interface {
|
||||
FormatField(field string) string
|
||||
}
|
||||
|
||||
// high level api to print a table of items with dynamic fields
|
||||
func tableFromItems(fields []string, values []printable) table {
|
||||
t := table{headers: fields}
|
||||
for _, v := range values {
|
||||
row := make([]string, len(fields))
|
||||
for i, f := range fields {
|
||||
row[i] = v.FormatField(f)
|
||||
}
|
||||
t.addRowSlice(row)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func tableWithHeader(header ...string) table {
|
||||
return table{headers: header}
|
||||
}
|
||||
|
@ -54,16 +72,16 @@ func (t table) Less(i, j int) bool {
|
|||
}
|
||||
|
||||
func (t *table) print(output string) {
|
||||
switch {
|
||||
case output == "" || output == "table":
|
||||
switch output {
|
||||
case "", "table":
|
||||
outputtable(t.headers, t.values)
|
||||
case output == "csv":
|
||||
case "csv":
|
||||
outputdsv(t.headers, t.values, ",")
|
||||
case output == "simple":
|
||||
case "simple":
|
||||
outputsimple(t.headers, t.values)
|
||||
case output == "tsv":
|
||||
case "tsv":
|
||||
outputdsv(t.headers, t.values, "\t")
|
||||
case output == "yaml":
|
||||
case "yml", "yaml":
|
||||
outputyaml(t.headers, t.values)
|
||||
default:
|
||||
fmt.Printf("unknown output type '" + output + "', available types are:\n- csv: comma-separated values\n- simple: space-separated values\n- table: auto-aligned table format (default)\n- tsv: tab-separated values\n- yaml: YAML format\n")
|
||||
|
@ -119,3 +137,11 @@ func outputyaml(headers []string, values [][]string) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isMachineReadable(outputFormat string) bool {
|
||||
switch outputFormat {
|
||||
case "yml", "yaml", "csv":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -5,22 +5,12 @@
|
|||
package print
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func formatDuration(seconds int64, outputType string) string {
|
||||
switch outputType {
|
||||
case "yaml":
|
||||
case "csv":
|
||||
return fmt.Sprint(seconds)
|
||||
}
|
||||
return time.Duration(1e9 * seconds).String()
|
||||
}
|
||||
|
||||
// TrackedTimesList print list of tracked times to stdout
|
||||
func TrackedTimesList(times []*gitea.TrackedTime, outputType string, from, until time.Time, printTotal bool) {
|
||||
tab := tableWithHeader(
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
// Contains checks containment
|
||||
func Contains(haystack []string, needle string) bool {
|
||||
for _, s := range haystack {
|
||||
if s == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
Loading…
Reference in New Issue
Block a user