clop v0.2.5, a pure struct command-line parser implemented in Go

  •  project address
    • https://gitee.com/guonaihong/clop
    • https://github.com/guonaihong/clop
  • The content of this update
    • Optimization: SetVersion interface
    • Added: issues template
       

clop

Go codecov Go Report Card

clop (Command Line Option Parse) is a struct-based command line parser. Although the sparrow is small, it has all the internal organs (implemented from scratch).

feature

  • Support environment variable binding env DEBUG=xx ./proc
  • Support parameter collection , you can classify bulk members and collect them into the structure members you specify cat a.txt b.txta.txt, b.txt
  • Supports short proc -d or long proc --debugoptions
  • POSIX style command line support, support command combination ls -ltris a ls -l -t -rshorthand form, which is convenient to implement common POSIX standard commands
  • Subcommand ( subcommand) support, easy to implement git style subcommands , simple subcommand registration method, as long as you can write the structure, 3,4,5 to infinite subcommands are also supported, as long as you like, you can use clop to achievegit add 
  • Default value support default:"1", support for multiple data types, save you the trouble of type conversion
  • Intimate repeat command error
  • Strictly short options, long options report an error. Avoid ambiguous options
  • Validation mode support, no need to write a bunch of code or waste your youthif x!= ""  if y!=0
  • You can get the command priority level, which is convenient for setting command aliases
  • Parse the flag package code to generate clop code

content

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 . SetAbout ( "This is a simple example 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

With the greedy attribute, array greedy writing is supported. Similar to the join command.

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 
output 
&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

You can use the default tag to set the default value, the common type is written directly, and the composite type is represented by json

package mainimport (
    "fmt""github.com/guonaihong/clop"
)typedefaultExamplestruct {
    Intint`default:"1"`Float64float64`default:"3.64"`Float32float32`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]"` : =de() {
    mainfunc
}


    

                   
             
             
      
         
     

   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

Using env tag will generate an environment variable name based on the structure name. The rule is the camel case command name, which is changed to uppercase and underscore.

// 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"`
}funcmain() {
	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

The second method of using clop to implement subcommands. As long as the subcommand structure implements SubMainthe method, the method clop library will automatically call it for you. It is unnecessary to write a bunch of if else judgments in main (relative to method 1), especially This method is recommended when there are too many subcommands.

package mainimport (
	"fmt""github.com/guonaihong/clop"
)typeaddstruct {
	Allbool`clop:"-A; --all" usage:"add changes from all tracked and untracked files"`Forcebool`clop:"-f; --force" usage:"allow adding otherwise ignored files"`Pathspec []string`clop:"args=pathspec"` boolForce{
	structmvtype
}// clop will automatically call this function// when the add subcommand is set() {
SubMain)add*a(func
}


	

             
	         
	 

 


    `clop:"-f; --force" usage:"allow adding otherwise ignored files"` 
} func ( m * mv ) SubMain () {
 // when the mv subcommand is set // clop will automatically call this function 
} 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

The specified option can only be set once, if the command line option is used twice, an error will be reported.

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

Fast way to generate short and long options by using fixed short, long tags. It can be visually compared with the cat example. The more command-line options you have, the more time you can save and more efficiency.  

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

Multiple structure concatenation function. Multiple structures are unified into one command line view

If the command line parsing is to go into multiple (>=2) structures, you can use the structure concatenation function, the first few structures use clop.Register()interfaces, and the last structure use clop.Bind()functions.

/*
┌────────────────┐
│                │
│                │
│  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 ) 
 } // You can use the following command line parameters to test the effect // ./example --server-address", ":8080", "--rate", "1s", " --thread-num", "20", "--open-vad"

      

 
 

Advanced features

There are some unique functions of the clop package in the advanced functions

Parsing flag code to generate clop code

It makes you feel great. If your command wants to migrate to clop, but you are faced with a lot of flag codes, and you don’t want to spend too much time on unnecessary code conversion, then you need the clop command, a one-line command to solve your pain points .

1. Install the clop command

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

2. Use clop to parse the code containing the flag package

You can convert the flag library in main.go into the calling method of the clop package

clop -f main.go

main.gocode show as below

package main

import "flag"

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

The output code is as follows

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>
*/ 

Guess you like

Origin www.oschina.net/news/197874