Code Monkey home page Code Monkey logo

clop's Introduction

clop

Go codecov Go Report Card

clop (Command Line Option Parse)是一款基于struct的命令行解析器,麻雀虽小,五脏俱全(从零实现)。

clop.png

feature

  • 支持环境变量绑定 env DEBUG=xx ./proc
  • 支持参数搜集 cat a.txt b.txt,可以把a.txt, b.txt散装成员归归类,收集到你指定的结构体成员里
  • 支持短选项proc -d 或者长选项proc --debug不在话下
  • posix风格命令行支持,支持命令组合ls -ltrls -l -t -r简写形式,方便实现普通posix 标准命令
  • 子命令(subcommand)支持,方便实现git风格子命令git add ,简洁的子命令注册方式,只要会写结构体就行,3,4,5到无穷尽子命令也支持,只要你喜欢,用上clop就可以实现
  • 默认值支持default:"1",支持多种数据类型,让你省去类型转换的烦恼
  • 贴心的重复命令报错
  • 严格的短选项,长选项报错。避免二义性选项诞生
  • 效验模式支持,不需要写一堆的if x!= "" or if y!=0浪费青春的代码
  • 可以获取命令优先级别,方便设置命令别名
  • 解析flag包代码生成clop代码
  • 指定解析函数, 自动绑定数据

feature list

内容

Installation

go get github.com/guonaihong/clop

Quick start

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type Hello struct {
	File string `clop:"-f; --file" usage:"file"`
}

func main() {

	h := Hello{}
	clop.SetVersion("v0.2.0")
	clop.SetVersionOption("", "version")
	clop.SetAbout("这是一个简单的示例demo")
	clop.Bind(&h)
	fmt.Printf("%#v\n", h)
}
// ./one -f test
// main.Hello{File:"test"}
// ./one --file test
// main.Hello{File:"test"}

example

base type

int

package main

import (
        "fmt"

        "github.com/guonaihong/clop"
)

type IntDemo struct {
        Int int `clop:"short;long" usage:"int"`
}

func main() {
        id := &IntDemo{}
        clop.Bind(id)
        fmt.Printf("id = %v\n", id)
}
//  ./int -i 3
// id = &{3}
// ./int --int 3
// id = &{3}

float64

package main

import (
        "fmt"

        "github.com/guonaihong/clop"
)

type Float64Demo struct {
        Float64 float64 `clop:"short;long" usage:"float64"`
}

func main() {
        fd := &Float64Demo{}
        clop.Bind(fd)
        fmt.Printf("fd = %v\n", fd)
}
// ./float64 -f 3.14
// fd = &{3.14}
// ./float64 --float64 3.14
// fd = &{3.14}

duration

package main

import (
        "fmt"
        "time"

        "github.com/guonaihong/clop"
)

type DurationDemo struct {
        Duration time.Duration `clop:"short;long" usage:"duration"`
}

func main() {
        dd := &DurationDemo{}
        clop.Bind(dd)
        fmt.Printf("dd = %v\n", dd)
}
// ./duration -d 1h
// dd = &{1h0m0s}
// ./duration --duration 1h
// dd = &{1h0m0s}

string

package main

import (
        "fmt"

        "github.com/guonaihong/clop"
)

type StringDemo struct {
        String string `clop:"short;long" usage:"string"`
}

func main() {
        s := &StringDemo{}
        clop.Bind(s)
        fmt.Printf("s = %v\n", s)
}
// ./string --string hello
// s = &{hello}
// ./string -s hello
// s = &{hello}

array

similar to curl command

package main

import (
        "fmt"

        "github.com/guonaihong/clop"
)

type ArrayDemo struct {
        Header []string `clop:"-H;long" usage:"header"`
}

func main() {
        h := &ArrayDemo{}
        clop.Bind(h)
        fmt.Printf("h = %v\n", h)
}
// ./array -H session:sid --header token:my
// h = &{[session:sid token:my]}

similar to join command

加上greedy属性,就支持数组贪婪写法。类似join命令。

package main

import (
    "fmt"

    "github.com/guonaihong/clop"
)

type test struct {
    A []int `clop:"-a;greedy" usage:"test array"`
    B int   `clop:"-b" usage:"test int"`
}

func main() {
    a := &test{}
    clop.Bind(a)
    fmt.Printf("%#v\n", a)
}

/*
运行
./use_array -a 12 34 56 78 -b 100
输出
&main.test{A:[]int{12, 34, 56, 78}, B:100}
*/

required flag

package main

import (
	"github.com/guonaihong/clop"
)

type curl struct {
	Url string `clop:"-u; --url" usage:"url" valid:"required"`
}

func main() {

	c := curl{}
	clop.Bind(&c)
}

// ./required 
// error: -u; --url must have a value!
// For more information try --help

set default value

可以使用default tag设置默认值,普通类型直接写,复合类型用json表示

package main

import (
    "fmt"
    "github.com/guonaihong/clop"
)

type defaultExample struct {
    Int          int       `default:"1"`
    Float64      float64   `default:"3.64"`
    Float32      float32   `default:"3.32"`
    SliceString  []string  `default:"[\"one\", \"two\"]"`
    SliceInt     []int     `default:"[1,2,3,4,5]"`
    SliceFloat64 []float64 `default:"[1.1,2.2,3.3,4.4,5.5]"`
}

func main() {
    de := defaultExample{}
    clop.Bind(&de)
    fmt.Printf("%v\n", de) 
}
// run
//         ./use_def
// output:
//         {1 3.64 3.32 [one two] [1 2 3 4 5] [1.1 2.2 3.3 4.4 5.5]}

Support environment variables

custom environment variable name

// file name use_env.go
package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type env struct {
	OmpNumThread string `clop:"env=omp_num_thread" usage:"omp num thread"`
	Path         string `clop:"env=XPATH" usage:"xpath"`
	Max          int    `clop:"env=MAX" usage:"max thread"`
}

func main() {
	e := env{}
	clop.Bind(&e)
	fmt.Printf("%#v\n", e)
}
// run
// env XPATH=`pwd` omp_num_thread=3 MAX=4 ./use_env 
// output
// main.env{OmpNumThread:"3", Path:"/home/guo", Max:4}

Quick writing of environment variables

使用env tag会根据结构体名, 生成一个环境变量名, 规则就是驼峰命令名, 改成大写下划线

// file name use_env.go
package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type env struct {
	OmpNumThread string `clop:"env" usage:"omp num thread"`
	Xpath         string `clop:"env" usage:"xpath"`
	Max          int    `clop:"env" usage:"max thread"`
}

func main() {
	e := env{}
	clop.Bind(&e)
	fmt.Printf("%#v\n", e)
}
// run
// env XPATH=`pwd` OMP_NUM_THREAD=3 MAX=4 ./use_env 
// output
// main.env{OmpNumThread:"3", Xpath:"/home/guo", Max:4}

subcommand

Sub command implementation method 1

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type add struct {
	All      bool     `clop:"-A; --all" usage:"add changes from all tracked and untracked files"`
	Force    bool     `clop:"-f; --force" usage:"allow adding otherwise ignored files"`
	Pathspec []string `clop:"args=pathspec"`
}

type mv struct {
	Force bool `clop:"-f; --force" usage:"allow adding otherwise ignored files"`
}

type git struct {
	Add add `clop:"subcommand=add" usage:"Add file contents to the index"`
	Mv  mv  `clop:"subcommand=mv" usage:"Move or rename a file, a directory, or a symlink"`
}

func main() {
	g := git{}
	clop.Bind(&g)
	fmt.Printf("git:%#v\n", g)
	fmt.Printf("git:set mv(%t) or set add(%t)\n", clop.IsSetSubcommand("mv"), clop.IsSetSubcommand("add"))

	switch {
	case clop.IsSetSubcommand("mv"):
		fmt.Printf("subcommand mv\n")
	case clop.IsSetSubcommand("add"):
		fmt.Printf("subcommand add\n")
	}
}

// run:
// ./git add -f

// output:
// git:main.git{Add:main.add{All:false, Force:true, Pathspec:[]string(nil)}, Mv:main.mv{Force:false}}
// git:set mv(false) or set add(true)
// subcommand add

Sub command implementation method 2

使用clop实现子命令的第2种做法, 子命令结构体只要实现SubMain方法, 该方法clop库会帮你自动调用. 省去在main里面写一堆if else判断(相对方法1来说), 特别是子命令特别多的情况, 推荐用这种方法.

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type add struct {
	All      bool     `clop:"-A; --all" usage:"add changes from all tracked and untracked files"`
	Force    bool     `clop:"-f; --force" usage:"allow adding otherwise ignored files"`
	Pathspec []string `clop:"args=pathspec"`
}

func (a *add) SubMain() {
// 当add子命令被设置时
// clop会自动调用这个函数
}

type mv struct {
	Force bool `clop:"-f; --force" usage:"allow adding otherwise ignored files"`
}

func (m *mv) SubMain() {
// 当mv 子命令被设置时
// clop会自动调用这个函数
}

type git struct {
	Add add `clop:"subcommand=add" usage:"Add file contents to the index"`
	Mv  mv  `clop:"subcommand=mv" usage:"Move or rename a file, a directory, or a symlink"`
}

func main() {
	g := git{}
	clop.Bind(&g)
}

Get command priority

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type cat struct {
	NumberNonblank bool `clop:"-b;--number-nonblank"
                             usage:"number nonempty output lines, overrides"`

	ShowEnds bool `clop:"-E;--show-ends"
                       usage:"display $ at end of each line"`
}

func main() {

	c := cat{}
	clop.Bind(&c)

	if clop.GetIndex("number-nonblank") < clop.GetIndex("show-ends") {
		fmt.Printf("cat -b -E\n")
	} else {
		fmt.Printf("cat -E -b \n")
	}
}
// cat -be 
// 输出 cat -b -E
// cat -Eb
// 输出 cat -E -b

Can only be set once

指定选项只能被设置一次,如果命令行选项,使用两次则会报错。

package main

import (
    "github.com/guonaihong/clop"
)

type Once struct {
    Debug bool `clop:"-d; --debug; once" usage:"debug mode"`
}

func main() {
    o := Once{}
    clop.Bind(&o)
}
/*
./once -debug -debug
error: The argument '-d' was provided more than once, but cannot be used multiple times
For more information try --help
*/

quick write

快速写法,通过使用固定的short, long tag生成短,长选项。可以和 cat 例子直观比较下。命令行选项越多,越能节约时间,提升效率。

package main

import (
    "fmt"
    "github.com/guonaihong/clop"
)

type cat struct {
	NumberNonblank bool `clop:"-c;long" 
	                     usage:"number nonempty output lines, overrides"`

	ShowEnds bool `clop:"-E;long" 
	               usage:"display $ at end of each line"`

	Number bool `clop:"-n;long" 
	             usage:"number all output lines"`

	SqueezeBlank bool `clop:"-s;long" 
	                   usage:"suppress repeated empty output lines"`

	ShowTab bool `clop:"-T;long" 
	              usage:"display TAB characters as ^I"`

	ShowNonprinting bool `clop:"-v;long" 
	                      usage:"use ^ and M- notation, except for LFD and TAB" `

	Files []string `clop:"args=files"`
}

func main() {
 	c := cat{}
	err := clop.Bind(&c)

	fmt.Printf("%#v, %s\n", c, err)
}

Multi structure series

多结构体串联功能. 多结构体统一组成一个命令行视图

如果命令行解析是要怼到多个(>=2)结构体里面, 可以使用结构体串联功能, 前面几个结构体使用clop.Register()接口, 最后一个结构体使用clop.Bind()函数.

/*
┌────────────────┐
│                │
│                │
│  ServerAddress │                        ┌─────────────────────┐
├────────────────┤                        │                     │
│                │   ──────────────────►  │                     │
│                │                        │  clop.MustRegitser()│
│     Rate       │                        │                     │
│                │                        └─────────────────────┘
└────────────────┘



┌────────────────┐
│                │
│   ThreadNum    │
│                │                        ┌─────────────────────┐
│                │                        │                     │
├────────────────┤   ──────────────────►  │                     │
│                │                        │ clop.Bind()         │
│   OpenVad      │                        │                     │
│                │                        │                     │
└────────────────┘                        └─────────────────────┘
 */

type Server struct {
	ServerAddress string `clop:"long" usage:"Server address"`
	Rate time.Duration `clop:"long" usage:"The speed at which audio is sent"`
}

type Asr struct{
	ThreadNum int `clop:"long" usage:"thread number"`
	OpenVad bool `clop:"long" usage:"open vad"`
}

 func main() {
	 asr := Asr{}
	 ser := Server{}
	 clop.MustRegister(&asr)
	 clop.Bind(&ser)
 }

 // 可以使用如下命令行参数测试下效果
 // ./example --server-address", ":8080", "--rate", "1s", "--thread-num", "20", "--open-vad"

Support callback function parsing

  • 使用callback=name的写法, 其中name就是需要调用的解析函数。
type TestCallback struct {
	Size int `clop:"short;long;callback=ParseSize" usage:"parse size"`
	Max  int `clop:"short;long"`
}

func (t *TestCallback) ParseSize(val string) {
	// 做些解析工作
	// t.Size = 解析之后的值
}

func main() {
 	t := TestCallback{}
	err := clop.Bind(&t)

	fmt.Printf("%#v, %s\n", t, err)
}

Advanced features

高级功能里面有一些clop包比较有特色的功能

Parsing flag code to generate clop code

让你爽翻天, 如果你的command想迁移至clop, 但是面对众多的flag代码, 又不想花费太多时间在无谓的人肉code转换上, 这时候你就需要clop命令, 一行命令解决你的痛点.

1.安装clop命令

go get github.com/guonaihong/clop/cmd/clop

2.使用clop解析包含flag包的代码

就可以把main.go里面的flag库转成clop包的调用方式

clop -f main.go

main.go代码如下

package main

import "flag"

func main() {
	s := flag.String("string", "", "string usage")
	i := flag.Int("int", "", "int usage")
	flag.Parse()
}

输出代码如下

package main

import (
	"github.com/guonaihong/clop"
)

type flagAutoGen struct {
	Flag string `clop:"--string" usage:"string usage" `
	Flag int    `clop:"--int" usage:"int usage" `
}

func main() {
	var flagVar flagAutoGen
	clop.Bind(&flagVar)
}

Implementing linux command options

cat

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type cat struct {
	NumberNonblank bool `clop:"-c;--number-nonblank" 
	                     usage:"number nonempty output lines, overrides"`

	ShowEnds bool `clop:"-E;--show-ends" 
	               usage:"display $ at end of each line"`

	Number bool `clop:"-n;--number" 
	             usage:"number all output lines"`

	SqueezeBlank bool `clop:"-s;--squeeze-blank" 
	                   usage:"suppress repeated empty output lines"`

	ShowTab bool `clop:"-T;--show-tabs" 
	              usage:"display TAB characters as ^I"`

	ShowNonprinting bool `clop:"-v;--show-nonprinting" 
	                      usage:"use ^ and M- notation, except for LFD and TAB" `

	Files []string `clop:"args=files"`
}

func main() {

	c := cat{}
	err := clop.Bind(&c)

	fmt.Printf("%#v, %s\n", c, err)
}

/*
Usage:
    ./cat [Flags] <files> 

Flags:
    -E,--show-ends           display $ at end of each line 
    -T,--show-tabs           display TAB characters as ^I 
    -c,--number-nonblank     number nonempty output lines, overrides 
    -n,--number              number all output lines 
    -s,--squeeze-blank       suppress repeated empty output lines 
    -v,--show-nonprinting    use ^ and M- notation, except for LFD and TAB 

Args:
    <files>
*/

faq

The subcommand or option does not take effect

q: 关于子命令或者命令行命令没有生效
a: 可以检查下结构体的字段是否是大写开头。

clop's People

Contributors

greyh4t avatar guonaihong avatar wangxin008 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

clop's Issues

在命令行使用重复选项报错

clop选择默认宽松的方式。(主要方便开发大量的linux命令,现有命令都是默认宽松模式)

新增once标记,如果是非slice类型变量,在命令行重复使用会报错。
伪代码如下

type D struct {
    Debug bool `clop:"-d;once" usage:"debug mod"`
}

./debug -d -d
报错

设计总目标

设计总目标

  • 极致压榨基于struct的方式(暂时不考虑回调函数和链式的做法)
  • 支持posix规范
  • 以gnu 110多个命令+curl+git作为最终测试验收标准

支持导入flag库的调用方式

思路

通过ast包, 遍历go的代码, 然后生成clop 结构体数据.
直接使用正则表达式做
还是使用ast包做, 正则表达式容易做出效果, 但是最后收敛问题需要花费太多的时间

添加subcommand的自动路由功能

如果根据IsSetSubcommand对子命令是否存在的判断结果来执行相应func,当子命令过多的时候会出现过多的冗余代码,希望内部实现关系映射,直接根据子命令做内部func的路由

支持环境变量

伪代码如下

   type env struct {
        Url []string `clop:"-u; --url; env=CLOP-TEST-URL" usage:"URL to work with"`
    }

e := env{}
clop.Bind(&e)

行为约束.

  • 环境变量的解析放在命令行的后面
  • 如果写了多个环境变量配置,只有最后一个才生效
  • 环境变量的值为false时,bind到结构体里面也是false

扩展设计args行为

思路笔记

  • 是否有必要支持多args参数?
    #3 第一版本的args设计里面,只支持一个args参数。最后想了要,有必要,如果支持多args,可以更方便地bind变量,这里面加个约束。slice的args参数只能放到最后一个,不支持两个复合类型的args的变量。

需要验收通过的

TODO 和错误处理一起
加上args是可选参数还是必选参数?

  • slice args+basetype行为=报错
  • basetype...+slice args行为=正确。
  • slice args+slice args 行为=报错。
  • slice选项+slice args = slice选项得到大部分参数,slice args 得到最后一个参数

未注册选项行为

$ ./unregistered -x
error: Found argument '-x' which wasn't expected, or isn't valid in this context

For more information try --help

$ ./unregistered --xx
error: Found argument '--xx' which wasn't expected, or isn't valid in this context

For more information try --help

第一个小目标,完成一个最简单的解析器

完成最基本的解析器

package main

import (
    "fmt"
    "github.com/guonaihong/clop"
)

func main() {

     type cat struct {
        NumberNonblank bool `clop:"-b;--number-nonblank"
                            usage:"number nonempty output lines, overrides"`

        ShowEnds bool `clop:"-E;--show-ends"
                            usage:"display $ at end of each line"`

        Number bool `clop:"-n;--number"
                            usage:"number all output lines"`

        SqueezeBlank bool `clop:"-s;--squeeze-blank"
                            usage:"suppress repeated empty output lines"`

        ShowTab bool `clop:"-T;--show-tabs"
                            usage:"display TAB characters as ^I"`

        ShowNonprinting bool `clop:"-v;--show-nonprinting"
                            usage:"use ^ and M- notation, except for LFD and TAB" `
    }   
    c := cat{}
    clop.Bind(&c)
}

增加short, long, $from_var_name模板用法

新增feature

比如

type client struct {
	HotWorld   bool `clop:"-h;--hot-world" usage:"hot world"`
	Grammar    bool `clop:"-g;--grammar" usage:"grammar"`
	SmallModel bool `clop:"-s;--small-model" usage:"small model"`
}

可以写成,如下,效果是一样的。

type client struct {
	HotWorld   bool `clop:"short;long" usage:"$from_var_name"`
	Grammar    bool `clop:"short;long" usage:"$from_var_name"`
	SmallModel bool `clop:"short;long" usage:"$from_var_name"`
}

出发点

有模板代码的feature,可以使用vim的列模式,快速实现命令行选项。

修改

$from_var_name 暂且不实现。

子母命令支持

伪代码如下:

package main

import (
    "fmt"
    "github.com/guonaihong/clop"
)

type add struct {
    All   bool `clop:"-A; --all" usage:"add changes from all tracked and untracked files"`
    Force bool `clop:"-f; --force" usage:"allow adding otherwise ignored files"`
}

type mv struct {
    Force bool `clop:"-f; --force" usage:"allow adding otherwise ignored files"`
}

type git struct {
    Add add `clop:"subcommand=add" usage:"Add file contents to the index"`
    Mv  mv  `clop:"subcommand=mv" usage:"Move or rename a file, a directory, or a symlink"`
}

func main() {
    g := git{}
    err := clop.Bind(&g)
    fmt.Printf("err = %v, %#v\n", err, g)
    fmt.Printf("mv(%t) add(%t)\n", clop.IsSetSubcommand("mv"), clop.IsSetSubcommand("add"))
}

todo
完善提示信息
fix测试代码

默认值支持

下面是伪代码

type gurl struct {
    URL string `clop:"-u; --url" usage:"url" default:"http://192.168.1.1"`
    C int `clop:"-c; -ab.c" usage:"c" default:"1"`
    Files []string `clop:"args=files" usage:"file" default:"[\"a.txt", \"b.txt\"]"`
}

省略clop tag,取首字母为短远项,取全部字段为长选项

伪代码如下
如果不传递clop tag,在命令行里面,这个结构体成员变量,首字母(小写) == 短选项,成员变量名等于长选项,单个字母的只有短选项。

type log struct{
    Level `usage:"log level"`
   // Level `clop:"-l; --level" usage:"log level"`
}

about,version,usage信息补全

about,version,usage信息补全

v0.0.1
a quick start example
Usage:
     [Flags] [Options] <input> <CLOP-FORMAT> <files> 
  • TODO 测试About和Versions可以支持的类型
    不支持空结构体。

实现args功能

在shell里面,没有被当成命令行选项的参数,一般另有用途,clop提供args tag,用于保存下面的r.go pool.c会存放至cat.Args变量里。

cat -n r.go -T pool.c

clop用法

 type cat struct {
        NumberNonblank bool `clop:"-b;--number-nonblank"
                            usage:"number nonempty output lines, overrides"`

        ShowEnds bool `clop:"-E;--show-ends"
                            usage:"display $ at end of each line"`

        Number bool `clop:"-n;--number"
                            usage:"number all output lines"`

        SqueezeBlank bool `clop:"-s;--squeeze-blank"
                            usage:"suppress repeated empty output lines"`

        ShowTab bool `clop:"-T;--show-tabs"
                            usage:"display TAB characters as ^I"`

        ShowNonprinting bool `clop:"-v;--show-nonprinting"
                            usage:"use ^ and M- notation, except for LFD and TAB" `

        Args []string `clop:"args"`
    }   

c := cat{}
clop.Bind(&c)

必选项标记支持

加上这个选项,这个命令行选项,必须传入。

package main

import (
	"github.com/guonaihong/clop"
)

type gurl struct {
	Url  string `clop:"-u; --url" usage:"url" valid:"required"`
	Text string `clop:"-t; --text" usage:"text" valid:"required"`
}

func main() {

	g := gurl{}
	clop.Bind(&g)
	//fmt.Printf("%s\n", err)
}

v0.0.1版本计划

开发周期

2020-3-30

功能规划

#2 完成一个最简单的解析器
#3 实现收集args参数功能
#7 实现--help输出帮助信息
#14 扩展解析器,在处理slice变量时--新增贪婪模式
#11 支持环境变量
#20 about,version,usage信息补全
#19 子命令支持
#21 一个最精简的readme.md
#22 新增Usage接口
#23 help 信息里面的Usage 如设置Subcommand要显示出来
#24 help显示里面的每个子命令都要显示procName
#10 必选项标记支持
#25 测试valid里面tag写错的情况
#9 默认值支持
#8 未注册选项行为
#12 注册重复选项报错
#17 对于长选项是只能使用两个减号,短选项只能使用一个减号
#16 省略clop tag,取首字母为短远项,取全部字段为长选项
#5 长选项支持=等于号
#28 bugfix,数据校验和子命令结合报错
#15 只有环境变量,没有命令行选项的在help如何体现?
#30 增加ci/cd

已完成

  • #2 完成一个最简单的解析器
  • #3 实现收集args参数功能
  • #7 实现--help输出帮助信息
  • #14 扩展解析器,在处理slice变量时--新增贪婪模式
  • #11 支持环境变量
  • #20 about,version,usage信息补全
  • #19 子命令支持
  • #21 一个最精简的readme.md
  • #22 新增Usage接口
  • #23 help 信息里面的Usage 如设置Subcommand要显示出来
  • #24 help显示里面的每个子命令都要显示procName
  • #10 必选项标记支持
  • #25 测试valid里面tag写错的情况
  • #9 默认值支持
  • #8 未注册选项行为
  • #12 注册重复选项报错
  • #17 对于长选项是只能使用两个减号,短选项只能使用一个减号
  • #16 省略clop tag,取首字母为短远项,取全部字段为长选项
  • #5 短,长选项都支持=等于号
  • #28 bugfix,数据校验和子命令结合报错
  • #15 只有环境变量,没有命令行选项的在help如何体现?
  • #30 增加ci/cd

需要通过的case

args

  • slice args+basetype行为=报错
  • basetype...+slice args行为=正确。
  • slice args+slice args 行为=报错。
  • slice选项+slice args = slice选项得到大部分参数,slice args 得到最后一个参数

数据格式为数组时,default的数据被保留了

package main

import (
	"log"

	"github.com/guonaihong/clop"
)

type A struct {
	Name []int `clop:"-e" usage:"数组测试" valid:"required" default:"[1,2]"`
}

func main() {
	a := A{}
	err := clop.Bind(&a)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("%+v\n", a)
}

此代码执行时,命令行输入 ./test.exe -e 3 -e 4,输出的结果是[1 2 3 4],难道不应该是[3, 4]

扩展解析器,在处理slice变量时--新增贪婪模式

新的解析器在处理slice类型变,需要支持多种情况,下面的-c指代选项名称

  • 第一种。
    -c "val1" -c "val2" -c "val3"
  • 第二种
    -c "val1" "val2" "val3"
  • 第三种
    -cval1 val2 val3

第2种第3种模式,只要修改结构体tag,新增greedy就行。

 type curl2 struct {
        Count []string `clop:"-c; greedy" usage:"test count"`
}   

fix 使用slice变量,默认进入贪婪模式

  • 测试的shell命令
curl -H "h1:v1" -H "h2:v2" url
  • 定义的结构体
       type Curl struct {
               Method string   `clop:"-X; --request" usage:"Specify request command to use"`
               Header []string `clop:"-H; --header" usage:"Pass custom header(s) to server"`
               URL    string   `clop:"args=url" usage:"url"`
       }
  • 实际输出
    Header的值为 h1:v1 h2:v2 url,URL值为空
  • 需要输出
    Header的值为 h1:v1 h2:v2,URL值为url

只有环境变量,没有命令行选项的在help如何体现?

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type env struct {
	OmpNumThread string `clop:"env=omp_num_thread" usage:"omp num thread"`
	Path         string `clop:"env=XPATH" usage:"xpath"`
	Max          int    `clop:"env=MAX" usage:"max thread"`
}

func main() {
	e := env{}
	clop.Bind(&e)
	fmt.Printf("%#v\n", e)
}

输出


Usage:
    ./use_env <omp_num_thread> <XPATH> <MAX> 
Args:
    <omp_num_thread>    omp num thread [env: omp_num_thread=]
    <XPATH>             xpath [env: XPATH=]
    <MAX>               max thread [env: MAX=]

数组格式参数的优化

现在添加数组格式的参数只能通过 -l 123 -l 456这种多次添加的方式实现,当数组内容比较多的时候很麻烦,是否可以支持 -l 123 456-l 123,456 这种方式呢

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.