Skip to content

Commit eb7aa3b

Browse files
committed
✨ feat: gflag - add new method FuncOpt() for quick add a func option flag
1 parent 4038651 commit eb7aa3b

File tree

5 files changed

+75
-8
lines changed

5 files changed

+75
-8
lines changed

app.go

+5
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,11 @@ func (app *App) Run(args []string) (code int) {
505505
return app.exitOnEnd(exCode)
506506
}
507507

508+
// RunArgs running a command with custom args
509+
func (app *App) RunArgs(args ...string) int {
510+
return app.Run(args)
511+
}
512+
508513
// RunLine manual run a command by command line string.
509514
//
510515
// eg: app.RunLine("top --top-opt val0 sub --sub-opt val1 arg0")

base.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -241,12 +241,12 @@ func (b *base) ResetData() {
241241
}
242242
}
243243

244-
// GetCommand get a command by name
244+
// GetCommand get a command by top name
245245
func (b *base) GetCommand(name string) *Command {
246246
return b.commands[name]
247247
}
248248

249-
// Command get a command by name
249+
// Command get a command by top name
250250
func (b *base) Command(name string) (c *Command, exist bool) {
251251
c, exist = b.commands[name]
252252
return
@@ -272,13 +272,13 @@ func (b *base) HasCommands() bool {
272272
return len(b.cmdNames) > 0
273273
}
274274

275-
// HasCommand name check
275+
// HasCommand top command name check
276276
func (b *base) HasCommand(name string) bool {
277277
_, has := b.cmdNames[name]
278278
return has
279279
}
280280

281-
// IsCommand name check. alias of the HasCommand()
281+
// IsCommand top command name check. alias of the HasCommand()
282282
func (b *base) IsCommand(name string) bool {
283283
_, has := b.cmdNames[name]
284284
return has
@@ -322,7 +322,7 @@ func (b *base) addCommand(pName string, c *Command) {
322322
b.commands[cName] = c
323323
}
324324

325-
// Match command by path names. eg. ["top", "sub"]
325+
// Match command by path names. eg: ["top", "sub"]
326326
func (b *base) Match(names []string) *Command {
327327
ln := len(names)
328328
if ln == 0 {
@@ -351,7 +351,7 @@ func (b *base) FindCommand(path string) *Command {
351351
return b.Match(splitPath2names(path))
352352
}
353353

354-
// FindByPath command by path. eg. "top:sub" or "top sub"
354+
// FindByPath command by path. eg: "top:sub" or "top sub"
355355
func (b *base) FindByPath(path string) *Command {
356356
return b.Match(splitPath2names(path))
357357
}

gflag/gflag_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,20 @@ func TestFlags_Uint64Opt(t *testing.T) {
193193
assert.Eq(t, uint64(16), uint2)
194194
}
195195

196+
func TestFlags_FuncOpt(t *testing.T) {
197+
fs := gcli.NewFlags("testFlags")
198+
199+
var str string
200+
fs.FuncOpt("str, s", "desc", func(v string) error {
201+
str = v
202+
return nil
203+
})
204+
205+
assert.Eq(t, "", str)
206+
assert.NoErr(t, fs.Parse([]string{"-s", "abc"}))
207+
assert.Eq(t, "abc", str)
208+
}
209+
196210
func TestFlags_VarOpt(t *testing.T) {
197211
fs := gcli.NewFlags("testFlags")
198212

@@ -441,6 +455,34 @@ func TestFlags_FromStruct_noNameStruct(t *testing.T) {
441455
assert.True(t, fs.IsOption("abbrev"))
442456
}
443457

458+
func TestExtType_Strings(t *testing.T) {
459+
var v1 any
460+
v1 = gcli.Strings{}
461+
val, ok := v1.(flag.Value)
462+
assert.True(t, ok)
463+
assert.NotNil(t, val)
464+
465+
v1 = &gcli.Strings{}
466+
val, ok = v1.(flag.Value)
467+
assert.True(t, ok)
468+
assert.NotNil(t, val)
469+
}
470+
471+
func TestFlags_FromStruct_var_Strings(t *testing.T) {
472+
type fileReplaceOpt struct {
473+
// Dir string `flag:"desc=the directory for find and replace"`
474+
Files gcli.Strings `flag:"desc=the files want replace content"`
475+
}
476+
477+
opt := fileReplaceOpt{Files: make(gcli.Strings, 0)}
478+
479+
fs := gcli.NewFlags("test")
480+
err := fs.FromStruct(&opt)
481+
482+
assert.NoErr(t, err)
483+
assert.True(t, fs.HasOption("files"))
484+
}
485+
444486
func TestFlags_FromStruct(t *testing.T) {
445487
type userOpts struct {
446488
Int int `flag:"name=int0;shorts=i;required=true;desc=int option message"`

gflag/opts.go

+20
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,26 @@ func (ops *CliOpts) uint64Opt(ptr *uint64, opt *CliOpt) {
318318
opt.flag = ops.fSet.Lookup(name)
319319
}
320320

321+
// FuncOptFn func option flag func type
322+
type FuncOptFn func(string) error
323+
324+
// FuncOpt binding a func option flag
325+
//
326+
// Usage:
327+
//
328+
// cmd.FuncOpt("name", "description ...", func(s string) error {
329+
// // do something ...
330+
// return nil
331+
// })
332+
func (ops *CliOpts) FuncOpt(nameAndShorts, desc string, fn FuncOptFn, setFns ...CliOptFn) {
333+
opt := NewOpt(nameAndShorts, desc, nil, setFns...)
334+
name := ops.checkFlagInfo(opt)
335+
336+
// binding option to flag.FlagSet
337+
ops.fSet.Func(name, opt.Desc, fn)
338+
opt.flag = ops.fSet.Lookup(name)
339+
}
340+
321341
// Var binding an custom var option flag
322342
func (ops *CliOpts) Var(ptr flag.Value, opt *CliOpt) { ops.varOpt(ptr, opt) }
323343

gflag/parser.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ func (p *Parser) MustFromStruct(ptr any, ruleType ...uint8) {
254254

255255
// FromStruct from struct tag binding options
256256
//
257-
// ## Named rule:
257+
// ## Named rule(default)
258258
//
259259
// // tag format: name=val0;shorts=i;required=true;desc=a message
260260
// type UserCmdOpts struct {
@@ -384,7 +384,7 @@ func (p *Parser) FromStruct(ptr any, ruleType ...uint8) (err error) {
384384
case reflect.String:
385385
p.StrVar((*string)(ptr), opt)
386386
default:
387-
return fmt.Errorf("field: %s - invalid type for binding flag", name)
387+
return fmt.Errorf("field: %s - unsupport type(%s) for binding flag", name, ft.Kind())
388388
}
389389
}
390390
return nil

0 commit comments

Comments
 (0)