repo mirror list: split column key from display header · Entire

repo mirror list: split column key from display header

8a8c30b→main·

gtrrz-victor·1w ago·2 files·+50 added/-48 removed

Replace the parseSortColumn parenthetical-stripping (regexp + sortKeyOf) with a column{key, header} struct: key is the canonical --sort/match name, header is the table text. They differ only where the header adds a display hint the key shouldn't (NAME -> "NAME (owner/repo)"), so --sort matching is a plain equality on key with no header parsing.

--sort now takes the key ("name"); the full display form is no longer a second accepted spelling (it never needed to be — it was only a workaround for overloading one string for both jobs).

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

01KX0MWF385YSK197X1BWAJ6CJView transcript

[?
Rename Repo Column to Name for ConsistencyClaude Code·1 step](/content/gh/entireio/cli/session/dec0d061-ea6a-4326-b5dd-6ba25b48eed6#timeline-01KX0MWF385YSK197X1BWAJ6CJ/index.html)

Changes

2

17 unmodified lines

// Column header names, the single source of truth for both the table headers
// (mirrorColumns/availableMirrorColumns) and the --sort key switches. parseSort-
// Column returns the canonical header it matched, so the sort switches compare
// against these constants directly. The NAME header carries an inline
// "(owner/repo)" hint spelling out what the cell holds; parseSortColumn accepts
// the header with that trailing parenthetical stripped, so `--sort name` works.
const (
    colName     = "NAME (owner/repo)"
    colCloneURL = "CLONE URL"
    colPrivate  = "PRIVATE"
    colAccess   = "ACCESS"
    colStatus   = "STATUS"
)

// column is a table column with two separable identities: key is the canonical
// name a caller types for --sort (and the value parseSortColumn returns, so the
// sort switches compare against these constants directly); header is the text
// shown in the table. They differ only where the header carries a display hint
// the sort key shouldn't — e.g. NAME's inline "(owner/repo)" — which keeps
// --sort matching a simple equality on key with no header parsing.
type column struct {
    key    string
    header string
}

var (
    colName     = column{key: "NAME", header: "NAME (owner/repo)"}
    colCloneURL = column{key: "CLONE URL", header: "CLONE URL"}
    colPrivate  = column{key: "PRIVATE", header: "PRIVATE"}
    colAccess   = column{key: "ACCESS", header: "ACCESS"}
    colStatus   = column{key: "STATUS", header: "STATUS"}
)

// columnHeaders is the display-header view of a column set, for the table/field
// renderers (runCoreList/runCoreObject) which take plain header strings.
func columnHeaders(cols []column) []string {
    h := make([]string, len(cols))
    for i, c := range cols {
        h[i] = c.header
    }
    return h
}

// mirrorColumns is the human table/field view of a mirror: the scannable
// owner/repo name, the clone URL you'd copy, and whether the upstream is
// private. Owner, provider, and cluster aren't columns of their own — they're
// model's internal ids are dropped. The clone URL is synthesized from the
// mirror's coords (the form `git clone` accepts), since the list API doesn't
// return it.
var mirrorColumns = []column{colName, colCloneURL, colPrivate}

// mirrorPrivate renders the PRIVATE column ("yes"/"no"), shared by the table
// row and the --sort private key so both agree on the cell value.
func availableMirrorRow(m coreapi.AvailableMirror) []string {
    return []string{m.Owner + "/" + m.Repo, string(m.Access), string(m.Status)}
}

// sortKeyOf returns the header with its trailing parenthetical hint removed,
// i.e. the short name accepted by --sort ("NAME (owner/repo)" -> "NAME").
func sortKeyOf(header string) string {
    return parenHint.ReplaceAllString(header, "")
}

// parseSortColumn resolves a --sort spec to the canonical column header it
// names (one of the columns entries) and a direction. It trims first, then
// reads the '-' prefix, so leading/trailing whitespace is handled identically
// on every path (the direction and the column name never disagree). An empty
// spec selects the first column. A spec matches a column by its full header or
// by the header with its trailing parenthetical hint stripped, so both
// `--sort "name (owner/repo)"` and the friendly `--sort name` resolve. An
// unknown name errors naming the valid (short) columns. Returning the matched
// header lets callers switch on the col* constants directly.
func parseSortColumn(spec string, columns []column) (col column, desc bool, err error) {
    spec = strings.TrimSpace(spec)
    desc = strings.HasPrefix(spec, "-")
    name := strings.TrimSpace(strings.TrimPrefix(spec, "-"))
    if name == "" {
        return columns[0], desc, nil
    }
    for _, h := range columns {
        if strings.EqualFold(h.key, name) {
            return h, desc, nil
        }
    }
    valid := make([]string, len(columns))
    for i, h := range columns {
        valid[i] = strings.ToLower(h.key)
    }
    return column{}, false, fmt.Errorf("unknown sort column %q; valid columns: %s", name, strings.Join(valid, ", "))
}