mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
fix: reconcile the issues stream and remove the legacy output shims
Use one canPrompt and pullNumberArguments for the comment commands, set display preferences instead of the retired environment variables in tests, and delete writeList, writeItem, batch, table, writeRecord(s), the legacy column map, confirm, require, validateMilestoneFlags and listWindow now that every command uses their replacements. Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
13 files changed
+23
-172
No files matched your search
@@ -407,24 +407,6 @@ func acceptsArgs(args []string) bool {
|
|||||||
return err == nil && cmd.ParseFlags(rest) == nil && cmd.ValidateArgs(cmd.Flags().Args()) == nil
|
return err == nil && cmd.ParseFlags(rest) == nil && cmd.ValidateArgs(cmd.Flags().Args()) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirm asks for confirmation under the prompt policy unless --yes was supplied.
|
|
||||||
func (a *application) confirm(cmd *cobra.Command, description string) error {
|
|
||||||
s, err := a.deps.Settings.Load()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return terminal.Confirm(cmd.Context(), description, a.options.yes, a.deps.Streams, a.policy(s), a.deps.Prompter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// require prompts for a missing required value under the prompt policy.
|
|
||||||
func (a *application) require(cmd *cobra.Command, value *string, flag string) (bool, error) {
|
|
||||||
s, err := a.deps.Settings.Load()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return terminal.Require(cmd.Context(), value, flag, a.deps.Streams, a.policy(s), a.deps.Prompter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *application) policy(snapshot settings.Snapshot) terminal.Policy {
|
func (a *application) policy(snapshot settings.Snapshot) terminal.Policy {
|
||||||
return terminal.Policy{Enabled: snapshot.Preferences.Prompt == "enabled", NoInput: a.options.noInput, Machine: a.options.json != "", Interactive: a.options.interactive}
|
return terminal.Policy{Enabled: snapshot.Preferences.Prompt == "enabled", NoInput: a.options.noInput, Machine: a.options.json != "", Interactive: a.options.interactive}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -113,8 +112,8 @@ func (a *application) readComment(cmd *cobra.Command, f commentFlags, initial st
|
|||||||
|
|
||||||
// promptComment asks for a comment in the configured editor, or in a multi-line field without one.
|
// promptComment asks for a comment in the configured editor, or in a multi-line field without one.
|
||||||
func (a *application) promptComment(cmd *cobra.Command, initial string) (string, terminal.Draft, error) {
|
func (a *application) promptComment(cmd *cobra.Command, initial string) (string, terminal.Draft, error) {
|
||||||
if prompt, err := a.canPrompt(); err != nil || !prompt {
|
if !a.canPrompt() {
|
||||||
return "", terminal.Draft{}, cmp.Or(err, clierr.Wrap(clierr.Usage, errors.New("missing required input: supply --body, --body-file or --editor")))
|
return "", terminal.Draft{}, clierr.Wrap(clierr.Usage, errors.New("missing required input: supply --body, --body-file or --editor"))
|
||||||
}
|
}
|
||||||
if preferences, err := a.preferences(); err == nil && preferences.Editor != "" {
|
if preferences, err := a.preferences(); err == nil && preferences.Editor != "" {
|
||||||
draft, err := a.editDraft(cmd, initial)
|
draft, err := a.editDraft(cmd, initial)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ func TestCommentConfirmationNamesTheTarget(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
deps, out, diagnostics := serverDeps(t, server.URL)
|
deps, out, diagnostics := serverDeps(t, server.URL)
|
||||||
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1"})
|
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1"})
|
||||||
|
plainOutput(t, deps)
|
||||||
prompt := &scriptedPrompter{accept: true, input: "Prompted"}
|
prompt := &scriptedPrompter{accept: true, input: "Prompted"}
|
||||||
deps.Prompter = prompt
|
deps.Prompter = prompt
|
||||||
deps.Streams.InTTY, deps.Streams.ErrTTY, deps.Streams.OutTTY = true, true, true
|
deps.Streams.InTTY, deps.Streams.ErrTTY, deps.Streams.OutTTY = true, true, true
|
||||||
@@ -160,7 +161,8 @@ func TestCommentGroupCommentsLikeGh(t *testing.T) {
|
|||||||
"id:\t80\nauthor:\tbob\ncreated_at:\t2026-10-11T19:00:00Z\nurl:\t\n--\nSecond\nline\n--\n"+
|
"id:\t80\nauthor:\tbob\ncreated_at:\t2026-10-11T19:00:00Z\nurl:\t\n--\nSecond\nline\n--\n"+
|
||||||
"id:\t81\nauthor:\tbob\ncreated_at:\t2026-10-11T19:10:00Z\nurl:\t\n--\nThird\n--\n", out.String())
|
"id:\t81\nauthor:\tbob\ncreated_at:\t2026-10-11T19:10:00Z\nurl:\t\n--\nThird\n--\n", out.String())
|
||||||
deps.Streams.OutTTY = true
|
deps.Streams.OutTTY = true
|
||||||
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1", "TEA_MARKDOWN": "raw"})
|
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1"})
|
||||||
|
plainOutput(t, deps)
|
||||||
require.NoError(t, run("issue", "comment", "list", "1"))
|
require.NoError(t, run("issue", "comment", "list", "1"))
|
||||||
require.Equal(t, "#79 alice • about 2 hours ago\n First **one**\n\n"+
|
require.Equal(t, "#79 alice • about 2 hours ago\n First **one**\n\n"+
|
||||||
"#80 bob • about 1 hour ago (edited)\n Second\n line\n\n"+
|
"#80 bob • about 1 hour ago (edited)\n Second\n line\n\n"+
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ func TestSearchTables(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
deps, out, diagnostics := serverDeps(t, server.URL)
|
deps, out, diagnostics := serverDeps(t, server.URL)
|
||||||
deps.Now = func() time.Time { return time.Date(2026, 10, 11, 20, 0, 0, 0, time.UTC) }
|
deps.Now = func() time.Time { return time.Date(2026, 10, 11, 20, 0, 0, 0, time.UTC) }
|
||||||
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1", "TEA_HYPERLINKS": "never"})
|
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1"})
|
||||||
|
plainOutput(t, deps)
|
||||||
deps.Streams.OutTTY = true
|
deps.Streams.OutTTY = true
|
||||||
require.NoError(t, Execute(context.Background(), deps, []string{"search", "prs", "login", "-s", "all", "-L", "2", "--author", "@me"}))
|
require.NoError(t, Execute(context.Background(), deps, []string{"search", "prs", "login", "-s", "all", "-L", "2", "--author", "@me"}))
|
||||||
host := strings.TrimPrefix(server.URL, "http://")
|
host := strings.TrimPrefix(server.URL, "http://")
|
||||||
|
|||||||
@@ -308,3 +308,12 @@ func rawMarkdown(t *testing.T, deps Dependencies) {
|
|||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// plainOutput keeps terminal output free of rendered Markdown and hyperlinks so it compares as text.
|
||||||
|
func plainOutput(t *testing.T, deps Dependencies) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, deps.Settings.Update(settings.TargetPreferences, func(s *settings.Snapshot) error {
|
||||||
|
s.Preferences.Markdown, s.Preferences.Hyperlinks = "raw", "never"
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -6,13 +6,10 @@ package app
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gitea.dev/tea/internal/services"
|
"gitea.dev/tea/internal/services"
|
||||||
"gitea.dev/tea/internal/settings"
|
"gitea.dev/tea/internal/settings"
|
||||||
"gitea.dev/tea/internal/terminal"
|
"gitea.dev/tea/internal/terminal"
|
||||||
"github.com/mattn/go-runewidth"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -209,19 +206,6 @@ func writeBatchJSON[T any](a *application, cmd *cobra.Command, selections int, r
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// batch applies run to each selection like batchDone, for commands that predate it: its human
|
|
||||||
// output is one table of the results.
|
|
||||||
func batch[K, T any](a *application, cmd *cobra.Command, keys []K, label func(K) string, run func(K) (T, error), row func(T) []terminal.Cell) error {
|
|
||||||
results, failures := applyBatch(cmd, keys, label, run, func(T) {})
|
|
||||||
var err error
|
|
||||||
if a.options.json == "" {
|
|
||||||
err = a.writeHuman(cmd, legacyTable(a, cmd, results, row))
|
|
||||||
} else {
|
|
||||||
err = writeBatchJSON(a, cmd, len(keys), results)
|
|
||||||
}
|
|
||||||
return errors.Join(append(failures, err)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// batchDone applies a mutation to each selection like batch, reporting each success with a done
|
// batchDone applies a mutation to each selection like batch, reporting each success with a done
|
||||||
// line from report, such as "Closed issue alice/x#5 (Fix login)", and the results as JSON.
|
// line from report, such as "Closed issue alice/x#5 (Fix login)", and the results as JSON.
|
||||||
func batchDone[K, T any](a *application, cmd *cobra.Command, keys []K, label func(K) string, run func(K) (T, error), report func(T) string) error {
|
func batchDone[K, T any](a *application, cmd *cobra.Command, keys []K, label func(K) string, run func(K) (T, error), report func(T) string) error {
|
||||||
@@ -233,103 +217,9 @@ func batchDone[K, T any](a *application, cmd *cobra.Command, keys []K, label fun
|
|||||||
return errors.Join(append(failures, err)...)
|
return errors.Join(append(failures, err)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeList writes items as a JSON array, or as one table through the pager. List commands move
|
|
||||||
// to writeTable.
|
|
||||||
func writeList[T any](a *application, cmd *cobra.Command, items []T, row func(T) []terminal.Cell) error {
|
|
||||||
if a.options.json != "" {
|
|
||||||
return a.writeJSON(cmd, items)
|
|
||||||
}
|
|
||||||
return a.writeHuman(cmd, legacyTable(a, cmd, items, row))
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeItem writes one item as a JSON object, or as its list row. Views move to writeView, and
|
|
||||||
// mutations to done and printURL.
|
|
||||||
func writeItem[T any](a *application, cmd *cobra.Command, item T, row func(T) []terminal.Cell) error {
|
|
||||||
if a.options.json != "" {
|
|
||||||
return a.writeJSON(cmd, item)
|
|
||||||
}
|
|
||||||
return a.writeHuman(cmd, legacyTable(a, cmd, []T{item}, row))
|
|
||||||
}
|
|
||||||
|
|
||||||
// legacyColumns gives lists that still render through writeList a header row and a flexible
|
|
||||||
// column, keyed by command path without the root. Their owners replace an entry with a listSpec.
|
|
||||||
var legacyColumns = map[string][]terminal.Column{}
|
|
||||||
|
|
||||||
// numberedColumns head the rows of issueRow and pullRow.
|
|
||||||
var numberedColumns = []terminal.Column{{Header: "ID", Prefix: "#"}, {Header: "Title", Flex: true}, {Header: "State"}, {Header: "Author"}}
|
|
||||||
|
|
||||||
// legacyTable renders rows of commands that predate listSpec: tab-separated when piped, and on a
|
|
||||||
// TTY fit to the terminal width by truncating their widest column, unless legacyColumns names
|
|
||||||
// their columns.
|
|
||||||
func legacyTable[T any](a *application, cmd *cobra.Command, items []T, row func(T) []terminal.Cell) string {
|
|
||||||
rows := make([][]terminal.Cell, len(items))
|
|
||||||
for i, item := range items {
|
|
||||||
rows[i] = row(item)
|
|
||||||
}
|
|
||||||
columns, ok := legacyColumns[strings.TrimPrefix(cmd.CommandPath(), cmd.Root().Name()+" ")]
|
|
||||||
if !ok {
|
|
||||||
columns = widestFlex(rows)
|
|
||||||
}
|
|
||||||
return terminal.Table(columns, rows, a.display())
|
|
||||||
}
|
|
||||||
|
|
||||||
// widestFlex makes the column with the widest cell flexible.
|
|
||||||
func widestFlex(rows [][]terminal.Cell) []terminal.Column {
|
|
||||||
var widths []int
|
|
||||||
for _, row := range rows {
|
|
||||||
for i, cell := range row {
|
|
||||||
if i == len(widths) {
|
|
||||||
widths = append(widths, 0)
|
|
||||||
}
|
|
||||||
widths[i] = max(widths[i], runewidth.StringWidth(terminal.SafeLine(cell.Text)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
columns := make([]terminal.Column, len(widths))
|
|
||||||
if len(widths) > 0 {
|
|
||||||
columns[slices.Index(widths, slices.Max(widths))].Flex = true
|
|
||||||
}
|
|
||||||
return columns
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeRecord writes one item as a JSON object, or as a human-readable record.
|
|
||||||
func writeRecord[T any](a *application, cmd *cobra.Command, item T, record func(T) terminal.Record) error {
|
|
||||||
if a.options.json != "" {
|
|
||||||
return a.writeJSON(cmd, item)
|
|
||||||
}
|
|
||||||
return a.writeHuman(cmd, record(item).Render(a.colorEnabled()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeRecords writes items as a JSON array, or as consecutive human-readable records.
|
|
||||||
func writeRecords[T any](a *application, cmd *cobra.Command, items []T, record func(T) terminal.Record) error {
|
|
||||||
if a.options.json != "" {
|
|
||||||
return a.writeJSON(cmd, items)
|
|
||||||
}
|
|
||||||
color := a.colorEnabled()
|
|
||||||
var text strings.Builder
|
|
||||||
for _, item := range items {
|
|
||||||
text.WriteString(record(item).Render(color))
|
|
||||||
}
|
|
||||||
return a.writeHuman(cmd, text.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// numberedRecord heads an issue or pull request with its emphasized number and title.
|
|
||||||
func numberedRecord(number int64, title, url string) terminal.Record {
|
|
||||||
return terminal.Record{Heading: []terminal.Cell{{Text: fmt.Sprintf("#%d", number), Bold: true}, {Text: title}}, Lines: []string{url}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stateColors colors issue, pull request and milestone states with the terminal's own palette.
|
// stateColors colors issue, pull request and milestone states with the terminal's own palette.
|
||||||
var stateColors = map[string]string{"open": "green", "closed": "red", "merged": "magenta", "draft": "muted"}
|
var stateColors = map[string]string{"open": "green", "closed": "red", "merged": "magenta", "draft": "muted"}
|
||||||
|
|
||||||
// table renders rows aligned like a TTY table without header or truncation, for commands that
|
|
||||||
// predate listSpec and compose their own output.
|
|
||||||
func table[T any](items []T, row func(T) []terminal.Cell, color bool) string {
|
|
||||||
rows := make([][]terminal.Cell, len(items))
|
|
||||||
for i, item := range items {
|
|
||||||
rows[i] = row(item)
|
|
||||||
}
|
|
||||||
return terminal.Table(nil, rows, terminal.Display{TTY: true, Color: color, TrueColor: true, Links: color})
|
|
||||||
}
|
|
||||||
|
|
||||||
// morePagesHint tells human readers on stderr that the listing continues past this page.
|
// morePagesHint tells human readers on stderr that the listing continues past this page.
|
||||||
func (a *application) morePagesHint(cmd *cobra.Command, more bool, page int) error {
|
func (a *application) morePagesHint(cmd *cobra.Command, more bool, page int) error {
|
||||||
if !more || a.options.json != "" {
|
if !more || a.options.json != "" {
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ func TestAttachmentTablesDownloadsAndDoneLines(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
deps, out, diagnostics := serverDeps(t, server.URL)
|
deps, out, diagnostics := serverDeps(t, server.URL)
|
||||||
deps.Now = func() time.Time { return time.Date(2026, 10, 11, 20, 0, 0, 0, time.UTC) }
|
deps.Now = func() time.Time { return time.Date(2026, 10, 11, 20, 0, 0, 0, time.UTC) }
|
||||||
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1", "TEA_HYPERLINKS": "never"})
|
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1"})
|
||||||
|
plainOutput(t, deps)
|
||||||
run := func(tty bool, args ...string) error {
|
run := func(tty bool, args ...string) error {
|
||||||
out.Reset()
|
out.Reset()
|
||||||
diagnostics.Reset()
|
diagnostics.Reset()
|
||||||
|
|||||||
@@ -32,10 +32,7 @@ $ tea issue edit 12 --body-file notes.md`, Annotations: map[string]string{"tea.a
|
|||||||
if err := validateBodySources(cmd); err != nil {
|
if err := validateBodySources(cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
prompt, err := a.canPrompt()
|
prompt := a.canPrompt()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !localFlagsChanged(cmd) {
|
if !localFlagsChanged(cmd) {
|
||||||
if !prompt || a.deps.IssueEditForm == nil {
|
if !prompt || a.deps.IssueEditForm == nil {
|
||||||
return errNothingToEdit
|
return errNothingToEdit
|
||||||
|
|||||||
+1
-21
@@ -244,10 +244,7 @@ $ tea issue create --title "Idea" --web`, Args: argsExact(0), RunE: func(cmd *co
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prompt, err := a.canPrompt()
|
prompt := a.canPrompt()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !localFlagsChanged(cmd) && prompt && a.deps.IssueForm != nil {
|
if !localFlagsChanged(cmd) && prompt && a.deps.IssueForm != nil {
|
||||||
return a.createIssueInteractively(cmd)
|
return a.createIssueInteractively(cmd)
|
||||||
}
|
}
|
||||||
@@ -491,23 +488,6 @@ func milestoneID(cmd *cobra.Command, t target, raw string) (int64, error) {
|
|||||||
return milestone.ID, err
|
return milestone.ID, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateMilestoneFlags checks the --milestone ID and --milestone-name flags of pr create and pr
|
|
||||||
// edit before any request.
|
|
||||||
func validateMilestoneFlags(cmd *cobra.Command, name string, id int64) error {
|
|
||||||
if cmd.Flags().Changed("milestone-name") {
|
|
||||||
if cmd.Flags().Changed("milestone") {
|
|
||||||
return clierr.Wrap(clierr.Usage, errors.New("--milestone and --milestone-name are mutually exclusive"))
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(name) == "" {
|
|
||||||
return clierr.Wrap(clierr.Usage, errors.New("--milestone-name cannot be empty; use --milestone=0 for none"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if id < 0 {
|
|
||||||
return clierr.Wrap(clierr.Usage, errors.New("--milestone must be a positive ID, or 0 for none"))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func milestoneChoices(items []services.Milestone) []terminal.IssueMilestoneChoice {
|
func milestoneChoices(items []services.Milestone) []terminal.IssueMilestoneChoice {
|
||||||
choices := make([]terminal.IssueMilestoneChoice, len(items))
|
choices := make([]terminal.IssueMilestoneChoice, len(items))
|
||||||
for i, item := range items {
|
for i, item := range items {
|
||||||
|
|||||||
@@ -247,10 +247,7 @@ $ tea label edit old-stuff --archived`, Annotations: map[string]string{"tea.argu
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
prompt, err := a.canPrompt()
|
prompt := a.canPrompt()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
form, hasForm := a.deps.Prompter.(terminal.LabelForm)
|
form, hasForm := a.deps.Prompter.(terminal.LabelForm)
|
||||||
if opts == (services.LabelOptions{}) && (!prompt || !hasForm) {
|
if opts == (services.LabelOptions{}) && (!prompt || !hasForm) {
|
||||||
return errNothingToEdit
|
return errNothingToEdit
|
||||||
|
|||||||
@@ -222,7 +222,6 @@ func formOptions(values terminal.MilestoneFormValues) services.MilestoneOptions
|
|||||||
return services.MilestoneOptions{Title: &values.Title, Description: &values.Description, Deadline: &normalized}
|
return services.MilestoneOptions{Title: &values.Title, Description: &values.Description, Deadline: &normalized}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (a *application) milestoneCreateCommand(service milestoneService) *cobra.Command {
|
func (a *application) milestoneCreateCommand(service milestoneService) *cobra.Command {
|
||||||
var fields milestoneFields
|
var fields milestoneFields
|
||||||
var state string
|
var state string
|
||||||
|
|||||||
@@ -89,7 +89,8 @@ func TestMilestoneTablesViewsAndDoneLines(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
deps, out, diagnostics := serverDeps(t, server.URL)
|
deps, out, diagnostics := serverDeps(t, server.URL)
|
||||||
deps.Now = func() time.Time { return time.Date(2026, 10, 11, 20, 0, 0, 0, time.UTC) }
|
deps.Now = func() time.Time { return time.Date(2026, 10, 11, 20, 0, 0, 0, time.UTC) }
|
||||||
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1", "TEA_MARKDOWN": "raw", "TEA_HYPERLINKS": "never"})
|
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1"})
|
||||||
|
plainOutput(t, deps)
|
||||||
run := func(tty bool, args ...string) {
|
run := func(tty bool, args ...string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
out.Reset()
|
out.Reset()
|
||||||
|
|||||||
@@ -64,13 +64,6 @@ type Page[T any] struct {
|
|||||||
Counted bool
|
Counted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// listWindow returns items [(page-1)*limit, page*limit) even when the server caps its page size
|
|
||||||
// below limit (MAX_RESPONSE_ITEMS), and reports whether the collection continues past them.
|
|
||||||
func listWindow[T any](limit, page int, fetch func(limit, page int) ([]T, http.Header, error)) ([]T, bool, error) {
|
|
||||||
window, err := listPage(limit, page, fetch)
|
|
||||||
return window.Items, window.More, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// listPage is listWindow that also reports the collection size the server counted.
|
// listPage is listWindow that also reports the collection size the server counted.
|
||||||
func listPage[T any](limit, page int, fetch func(limit, page int) ([]T, http.Header, error)) (Page[T], error) {
|
func listPage[T any](limit, page int, fetch func(limit, page int) ([]T, http.Header, error)) (Page[T], error) {
|
||||||
items, headers, err := fetch(limit, page)
|
items, headers, err := fetch(limit, page)
|
||||||
|
|||||||
Reference in new issue
Block a user