Skip to content

Commit

Permalink
parse: allow per parser options
Browse files Browse the repository at this point in the history
  • Loading branch information
gobwas committed Dec 17, 2020
1 parent 35ba11b commit 9c1f135
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 32 deletions.
16 changes: 6 additions & 10 deletions flagutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ func (fn PrinterFunc) Name(ctx context.Context, fs parse.FlagSet) (func(*flag.Fl

type parser struct {
Parser
stash func(*flag.Flag) bool
ignoreUndefined bool
stash func(*flag.Flag) bool
ignoreUndefined bool
allowResetSpecified bool
}

type config struct {
parsers []*parser
parserOptions []ParserOption
ignoreUndefined bool
customUsage bool
unquoteUsageMode UnquoteUsageMode
}
Expand All @@ -67,16 +67,12 @@ func buildConfig(opts []ParseOption) config {
func Parse(ctx context.Context, flags *flag.FlagSet, opts ...ParseOption) (err error) {
c := buildConfig(opts)

fs := parse.NewFlagSet(flags,
parse.WithIgnoreUndefined(c.ignoreUndefined),
)
fs := parse.NewFlagSet(flags)
for _, p := range c.parsers {
parse.NextLevel(fs)
parse.Stash(fs, p.stash)
if p.ignoreUndefined {
// User may ask to ignore undefined per parser as well.
parse.IgnoreUndefined(fs)
}
parse.IgnoreUndefined(fs, p.ignoreUndefined)
parse.AllowResetSpecified(fs, p.allowResetSpecified)

if err = p.Parse(ctx, fs); err != nil {
if err == flag.ErrHelp {
Expand Down
43 changes: 32 additions & 11 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@ type ParseOptionFunc func(*config)

func (fn ParseOptionFunc) setupParseConfig(c *config) { fn(c) }

type ParserOptionFunc func(*config, *parser)
type ParserOptionFunc func(*parser)

func (fn ParserOptionFunc) setupParseConfig(c *config) { fn(c, nil) }
func (fn ParserOptionFunc) setupParserConfig(p *parser) { fn(nil, p) }
func (fn ParserOptionFunc) setupParserConfig(p *parser) { fn(p) }

func stashFunc(check func(*flag.Flag) bool) (opt ParserOptionFunc) {
return ParserOptionFunc(func(c *config, p *parser) {
type ParseOrParserOptionFunc func(*config, *parser)

func (fn ParseOrParserOptionFunc) setupParseConfig(c *config) { fn(c, nil) }
func (fn ParseOrParserOptionFunc) setupParserConfig(p *parser) { fn(nil, p) }

func stashFunc(check func(*flag.Flag) bool) (opt ParseOrParserOptionFunc) {
return ParseOrParserOptionFunc(func(c *config, p *parser) {
if c != nil {
c.parserOptions = append(c.parserOptions, opt)
return
Expand All @@ -47,24 +51,30 @@ func stashFunc(check func(*flag.Flag) bool) (opt ParserOptionFunc) {
})
}

func WithStashName(name string) ParserOptionFunc {
func WithStashName(name string) ParseOrParserOptionFunc {
return stashFunc(func(f *flag.Flag) bool {
return f.Name == name
})
}

func WithStashPrefix(prefix string) ParserOptionFunc {
func WithStashPrefix(prefix string) ParseOrParserOptionFunc {
return stashFunc(func(f *flag.Flag) bool {
return strings.HasPrefix(f.Name, prefix)
})
}

func WithStashRegexp(re *regexp.Regexp) ParserOptionFunc {
func WithStashRegexp(re *regexp.Regexp) ParseOrParserOptionFunc {
return stashFunc(func(f *flag.Flag) bool {
return re.MatchString(f.Name)
})
}

func WithResetSpecified() ParserOptionFunc {
return ParserOptionFunc(func(p *parser) {
p.allowResetSpecified = true
})
}

// WithParser returns a parse option and makes p to be used during Parse().
func WithParser(p Parser, opts ...ParserOption) ParseOptionFunc {
x := &parser{
Expand All @@ -79,17 +89,28 @@ func WithParser(p Parser, opts ...ParserOption) ParseOptionFunc {
}

// WithIgnoreUndefined makes Parse() to not fail on setting undefined flag.
func WithIgnoreUndefined() ParserOptionFunc {
return ParserOptionFunc(func(c *config, p *parser) {
func WithIgnoreUndefined() (opt ParseOrParserOptionFunc) {
return ParseOrParserOptionFunc(func(c *config, p *parser) {
switch {
case c != nil:
c.ignoreUndefined = true
c.parserOptions = append(c.parserOptions, opt)
case p != nil:
p.ignoreUndefined = true
}
})
}

func WithAllowResetSpecified() (opt ParseOrParserOptionFunc) {
return ParseOrParserOptionFunc(func(c *config, p *parser) {
switch {
case c != nil:
c.parserOptions = append(c.parserOptions, opt)
case p != nil:
p.allowResetSpecified = true
}
})
}

// WithCustomUsage makes Parse() to ignore flag.FlagSet.Usage field when
// receiving flag.ErrHelp error from some parser and print results of
// flagutil.PrintDefaults() instead.
Expand Down
28 changes: 17 additions & 11 deletions parse/flagset.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,28 @@ func Stash(fs FlagSet, fn func(*flag.Flag) bool) {
fset.stash = fn
}

func IgnoreUndefined(fs FlagSet) {
func IgnoreUndefined(fs FlagSet, ignore bool) {
fset := fs.(*flagSet)
fset.ignoreUndefined = true
fset.ignoreUndefined = ignore
}

func AllowResetSpecified(fs FlagSet, allow bool) {
fset := fs.(*flagSet)
fset.allowResetSpecified = allow
}

type flagSet struct {
dest *flag.FlagSet
ignoreUndefined bool
provided map[string]bool
stash func(*flag.Flag) bool
dest *flag.FlagSet
ignoreUndefined bool
allowResetSpecified bool
specified map[string]bool
stash func(*flag.Flag) bool
}

func NewFlagSet(flags *flag.FlagSet, opts ...FlagSetOption) FlagSet {
fs := &flagSet{
dest: flags,
provided: make(map[string]bool),
dest: flags,
specified: make(map[string]bool),
}
for _, opt := range opts {
opt(fs)
Expand All @@ -64,7 +70,7 @@ func NewFlagSet(flags *flag.FlagSet, opts ...FlagSetOption) FlagSet {
}

func (fs *flagSet) Set(name, value string) error {
if fs.provided[name] {
if fs.specified[name] && !fs.allowResetSpecified {
return nil
}
f := fs.dest.Lookup(name)
Expand Down Expand Up @@ -92,13 +98,13 @@ func (fs *flagSet) stashed(f *flag.Flag) bool {

func (fs *flagSet) update() {
fs.dest.Visit(func(f *flag.Flag) {
fs.provided[f.Name] = true
fs.specified[f.Name] = true
})
}

func (fs *flagSet) VisitUnspecified(fn func(*flag.Flag)) {
fs.dest.VisitAll(func(f *flag.Flag) {
if !fs.provided[f.Name] && !fs.stashed(f) {
if !fs.specified[f.Name] && !fs.stashed(f) {
fn(fs.clone(f))
}
})
Expand Down

0 comments on commit 9c1f135

Please sign in to comment.