cli: parse each option value once
validate() parsed every value to bounds-check it, then the typed getters re-parsed the same string at read time (with a comment admitting the failure was unreachable). A single parseValue() now validates and converts, finalize() runs it once per option after layering, and the getters just read the stored typed value.
This commit is contained in:
@@ -9,15 +9,17 @@ import (
|
||||
|
||||
// Options is the resolved configuration: a flat name->value map plus a record
|
||||
// of which names were explicitly set (so callers can tell a default from a
|
||||
// chosen value). Values are kept as strings end-to-end and converted only at
|
||||
// the point of use by the typed getters below.
|
||||
// chosen value). The raw strings carry the layered override merge; finalize()
|
||||
// then parses each one exactly once into typed, and the numeric/bool getters
|
||||
// read that — no value is parsed twice.
|
||||
type Options struct {
|
||||
vals map[string]string
|
||||
set map[string]bool
|
||||
vals map[string]string
|
||||
set map[string]bool
|
||||
typed map[string]any // parsed value per option, filled once by finalize()
|
||||
}
|
||||
|
||||
func newOptions() *Options {
|
||||
return &Options{vals: map[string]string{}, set: map[string]bool{}}
|
||||
return &Options{vals: map[string]string{}, set: map[string]bool{}, typed: map[string]any{}}
|
||||
}
|
||||
|
||||
// IsSet reports whether name was given on the command line or in the config
|
||||
@@ -44,30 +46,13 @@ func boolWord(s string) (val, ok bool) {
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// Bool reports whether the value is a truthy boolean word.
|
||||
func (o *Options) Bool(name string) bool {
|
||||
val, _ := boolWord(o.vals[name])
|
||||
return val
|
||||
}
|
||||
|
||||
// Int returns the value as an int, or 0 if empty/invalid. The value has already
|
||||
// passed validate(), so a parse failure here is unreachable in normal flow.
|
||||
func (o *Options) Int(name string) int {
|
||||
n, _ := strconv.ParseInt(strings.TrimSpace(o.vals[name]), 10, 64)
|
||||
return int(n)
|
||||
}
|
||||
|
||||
// Float returns the value as a float64, or 0 if empty/invalid.
|
||||
func (o *Options) Float(name string) float64 {
|
||||
f, _ := strconv.ParseFloat(strings.TrimSpace(o.vals[name]), 64)
|
||||
return f
|
||||
}
|
||||
|
||||
// Size returns a byte count parsed from a value like "20M" or "512K".
|
||||
func (o *Options) Size(name string) int64 {
|
||||
n, _ := parseSize(o.vals[name])
|
||||
return n
|
||||
}
|
||||
// Bool, Int, Float and Size read the value finalize() already parsed and stored
|
||||
// in typed; an unset option (no entry) reads as the zero value. The conversion
|
||||
// happened once, at finalize, so these never re-parse a string.
|
||||
func (o *Options) Bool(name string) bool { v, _ := o.typed[name].(bool); return v }
|
||||
func (o *Options) Int(name string) int { v, _ := o.typed[name].(int64); return int(v) }
|
||||
func (o *Options) Float(name string) float64 { v, _ := o.typed[name].(float64); return v }
|
||||
func (o *Options) Size(name string) int64 { v, _ := o.typed[name].(int64); return v }
|
||||
|
||||
// List returns a repeatable option's accumulated values.
|
||||
func (o *Options) List(name string) []string {
|
||||
|
||||
Reference in New Issue
Block a user