cli: simplify option model per pike review
- appendList: one home for the List newline-join rule (was duplicated across accumulate and set) - fold the redundant Int64 getter into Int; drop the truthy helper for the shared boolWord vocabulary - add Opt.Metavar so help derives the value placeholder from data instead of branching on the option name (seed-time) No behavior change; --help output is byte-identical.
This commit is contained in:
43
cli/help.go
43
cli/help.go
@@ -89,24 +89,31 @@ func flagLabel(o *Opt) string {
|
|||||||
b.WriteString(" ")
|
b.WriteString(" ")
|
||||||
}
|
}
|
||||||
b.WriteString("--" + o.Long)
|
b.WriteString("--" + o.Long)
|
||||||
switch o.Kind {
|
if m := metavar(o); m != "" {
|
||||||
case Bool:
|
b.WriteString("=" + m)
|
||||||
// no argument shown
|
|
||||||
case Size:
|
|
||||||
b.WriteString("=SIZE")
|
|
||||||
case Int:
|
|
||||||
b.WriteString("=N")
|
|
||||||
case Float:
|
|
||||||
// seed-time is a count of minutes; only seed-ratio is an actual ratio.
|
|
||||||
if o.Long == "seed-time" {
|
|
||||||
b.WriteString("=MIN")
|
|
||||||
} else {
|
|
||||||
b.WriteString("=RATIO")
|
|
||||||
}
|
|
||||||
case Enum:
|
|
||||||
b.WriteString("=" + strings.Join(o.Enum, "|"))
|
|
||||||
default:
|
|
||||||
b.WriteString("=VAL")
|
|
||||||
}
|
}
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// metavar is the placeholder shown after a value-taking flag, e.g. the "N" in
|
||||||
|
// "--split=N". An explicit Opt.Metavar wins; otherwise it derives from the Kind.
|
||||||
|
// Bool takes no value, so its metavar is empty and no "=ARG" is printed.
|
||||||
|
func metavar(o *Opt) string {
|
||||||
|
if o.Metavar != "" {
|
||||||
|
return o.Metavar
|
||||||
|
}
|
||||||
|
switch o.Kind {
|
||||||
|
case Bool:
|
||||||
|
return ""
|
||||||
|
case Size:
|
||||||
|
return "SIZE"
|
||||||
|
case Int:
|
||||||
|
return "N"
|
||||||
|
case Float:
|
||||||
|
return "RATIO"
|
||||||
|
case Enum:
|
||||||
|
return strings.Join(o.Enum, "|")
|
||||||
|
default:
|
||||||
|
return "VAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ type Opt struct {
|
|||||||
Kind Kind
|
Kind Kind
|
||||||
Default string
|
Default string
|
||||||
Help string
|
Help string
|
||||||
|
// Metavar overrides the "=PLACEHOLDER" shown after the flag in --help. Empty
|
||||||
|
// means derive it from Kind (N, SIZE, RATIO, ...). Set it only when the
|
||||||
|
// Kind-derived default would mislead, e.g. seed-time is minutes, not a ratio.
|
||||||
|
Metavar string
|
||||||
Tag Tag
|
Tag Tag
|
||||||
Enum []string // valid values when Kind == Enum
|
Enum []string // valid values when Kind == Enum
|
||||||
// Min is the inclusive lower Int/Size bound. The zero value enforces a
|
// Min is the inclusive lower Int/Size bound. The zero value enforces a
|
||||||
@@ -103,7 +107,7 @@ var options = []Opt{
|
|||||||
|
|
||||||
// --- bittorrent ---
|
// --- bittorrent ---
|
||||||
{Long: "torrent-file", Short: 'T', Kind: Str, Help: "path to a .torrent file", Tag: BitTorrent},
|
{Long: "torrent-file", Short: 'T', Kind: Str, Help: "path to a .torrent file", Tag: BitTorrent},
|
||||||
{Long: "seed-time", Kind: Float, Help: "minutes to seed after completion (0 = no seeding)", Tag: BitTorrent},
|
{Long: "seed-time", Kind: Float, Metavar: "MIN", Help: "minutes to seed after completion (0 = no seeding)", Tag: BitTorrent},
|
||||||
{Long: "seed-ratio", Kind: Float, Default: "1.0", Help: "stop seeding at this share ratio (0 = unlimited)", Tag: BitTorrent},
|
{Long: "seed-ratio", Kind: Float, Default: "1.0", Help: "stop seeding at this share ratio (0 = unlimited)", Tag: BitTorrent},
|
||||||
{Long: "bt-stop-timeout", Kind: Int, Default: "0", Min: 0, Help: "stop a torrent with no download progress for N seconds (0 = off)", Tag: BitTorrent},
|
{Long: "bt-stop-timeout", Kind: Int, Default: "0", Min: 0, Help: "stop a torrent with no download progress for N seconds (0 = off)", Tag: BitTorrent},
|
||||||
{Long: "bt-metadata-timeout", Kind: Int, Default: "60", Min: 0, Help: "stop a magnet that can't fetch metadata in N seconds (0 = off)", Tag: BitTorrent},
|
{Long: "bt-metadata-timeout", Kind: Int, Default: "60", Min: 0, Help: "stop a magnet that can't fetch metadata in N seconds (0 = off)", Tag: BitTorrent},
|
||||||
|
|||||||
@@ -48,13 +48,11 @@ func (o *Options) Bool(name string) bool {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int returns the value as an int, or 0 if empty/invalid.
|
// Int returns the value as an int, or 0 if empty/invalid. The value has already
|
||||||
func (o *Options) Int(name string) int { return int(o.Int64(name)) }
|
// passed validate(), so a parse failure here is unreachable in normal flow.
|
||||||
|
func (o *Options) Int(name string) int {
|
||||||
// Int64 returns the value as an int64, or 0 if empty/invalid.
|
|
||||||
func (o *Options) Int64(name string) int64 {
|
|
||||||
n, _ := strconv.ParseInt(strings.TrimSpace(o.vals[name]), 10, 64)
|
n, _ := strconv.ParseInt(strings.TrimSpace(o.vals[name]), 10, 64)
|
||||||
return n
|
return int(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float returns the value as a float64, or 0 if empty/invalid.
|
// Float returns the value as a float64, or 0 if empty/invalid.
|
||||||
|
|||||||
28
cli/parse.go
28
cli/parse.go
@@ -41,7 +41,7 @@ func Parse(args []string) (*Result, error) {
|
|||||||
opts := newOptions()
|
opts := newOptions()
|
||||||
applyDefaults(opts)
|
applyDefaults(opts)
|
||||||
|
|
||||||
if !truthy(cmdVals["no-conf"]) {
|
if noConf, _ := boolWord(cmdVals["no-conf"]); !noConf {
|
||||||
explicit := cmdVals["conf-path"]
|
explicit := cmdVals["conf-path"]
|
||||||
path, fromUser := confPath(explicit)
|
path, fromUser := confPath(explicit)
|
||||||
if err := applyConfig(opts, path, fromUser); err != nil {
|
if err := applyConfig(opts, path, fromUser); err != nil {
|
||||||
@@ -138,14 +138,23 @@ func parseArgs(args []string) (vals map[string]string, uris []string, action Act
|
|||||||
// accumulate stores a value, joining repeatable List options with newlines.
|
// accumulate stores a value, joining repeatable List options with newlines.
|
||||||
func accumulate(vals map[string]string, o *Opt, val string) {
|
func accumulate(vals map[string]string, o *Opt, val string) {
|
||||||
if o.Kind == List {
|
if o.Kind == List {
|
||||||
if prev, ok := vals[o.Long]; ok {
|
vals[o.Long] = appendList(vals[o.Long], val)
|
||||||
vals[o.Long] = prev + "\n" + val
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
vals[o.Long] = val
|
vals[o.Long] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appendList is the single home of the List newline-join rule, shared by the
|
||||||
|
// raw-parse layer (accumulate) and the resolved layer (set). An empty prev (the
|
||||||
|
// option not yet seen) yields val unchanged, so the first value carries no
|
||||||
|
// leading newline.
|
||||||
|
func appendList(prev, val string) string {
|
||||||
|
if prev == "" {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return prev + "\n" + val
|
||||||
|
}
|
||||||
|
|
||||||
func applyDefaults(o *Options) {
|
func applyDefaults(o *Options) {
|
||||||
for i := range options {
|
for i := range options {
|
||||||
opt := &options[i]
|
opt := &options[i]
|
||||||
@@ -215,10 +224,8 @@ func set(o *Options, name, val string) error {
|
|||||||
if err := validate(opt, val); err != nil {
|
if err := validate(opt, val); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if opt.Kind == List {
|
if opt.Kind == List && o.set[name] {
|
||||||
if prev, ok := o.vals[name]; ok && o.set[name] {
|
val = appendList(o.vals[name], val)
|
||||||
val = prev + "\n" + val
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
o.vals[name] = val
|
o.vals[name] = val
|
||||||
o.set[name] = true
|
o.set[name] = true
|
||||||
@@ -304,8 +311,3 @@ func confPath(explicit string) (path string, fromUser bool) {
|
|||||||
}
|
}
|
||||||
return filepath.Join(home, ".config", "got", "got.conf"), false
|
return filepath.Join(home, ".config", "got", "got.conf"), false
|
||||||
}
|
}
|
||||||
|
|
||||||
func truthy(s string) bool {
|
|
||||||
val, _ := boolWord(s)
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user