clop

package module
v0.0.0-...-f1e5e4e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 31, 2020 License: Apache-2.0 Imports: 19 Imported by: 1

README

clop

Go codecov Go Report Card

clop 是一款基于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 标准命令
  • 子命令支持,方便实现git风格子命令git add ,简洁的子命令注册方式,只要会写结构体就行,3,4,5到无穷尽子命令也支持,只要你喜欢,用上clop就可以实现
  • 默认值支持default:"1",支持多种数据类型,让你省去类型转换的烦恼
  • 贴心的重复命令报错
  • 严格的短选项,长选项报错。避免二义性选项诞生
  • 效验模式支持,不需要写一堆的if x!= "" or if y!=0浪费青春的代码
  • 可以获取命令优先级别,方便设置命令别名

内容

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.Bind(&h)
	fmt.Printf("%#v\n", h)
}
// ./one -f test
// main.Hello{File:"test"}
// ./one --file test
// main.Hello{File:"test"}

example

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
// 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}
subcommand
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"))
}
// 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)

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

Support arrays

加上greedy属性,就支持数组贪婪写法。类似join命令。 如不加,就是类似于curl -H 的写法

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

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

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrDuplicateOptions = errors.New("is already in use")
	//ErrUsageEmpty       = errors.New("usage cannot be empty")
	ErrUnsupported  = errors.New("unsupported command")
	ErrNotFoundName = errors.New("no command line options found")
	ErrOptionName   = errors.New("Illegal option name")
)
View Source
var CommandLine = New(os.Args[1:])
View Source
var ErrNotPointerType = errors.New("Not pointer type")
View Source
var ErrUnsupportedType = errors.New("Unsupported type")
View Source
var (
	// 显示usage信息里面的[default: xxx]信息,如果为false,就不显示
	ShowUsageDefault = true
)

Functions

func Bind

func Bind(x interface{}) error

func GetIndex

func GetIndex(optName string) uint64

func IsSetSubcommand

func IsSetSubcommand(subcommand string) bool

func StringToBytes

func StringToBytes(s string) (b []byte)

func Unquote

func Unquote(s string) (string, error)

本函数来自stdlib, 简单修改了下

func Usage

func Usage()

Types

type Clop

type Clop struct {
	// contains filtered or unexported fields
}

func New

func New(args []string) *Clop

func (*Clop) Bind

func (c *Clop) Bind(x interface{}) (err error)

func (*Clop) GetIndex

func (c *Clop) GetIndex(optName string) uint64

func (*Clop) IsSetSubcommand

func (c *Clop) IsSetSubcommand(subcommand string) bool

func (*Clop) SetExit

func (c *Clop) SetExit(exit bool) *Clop

设置出错行为,默认出错会退出进程(true), 为false则不会

func (*Clop) SetOutput

func (c *Clop) SetOutput(w io.Writer) *Clop

func (*Clop) SetProcName

func (c *Clop) SetProcName(procName string) *Clop

设置进程名

func (*Clop) Usage

func (c *Clop) Usage()

type Help

type Help struct {
	ProcessName      string
	Version          string
	About            string
	Flags            []showOption
	Options          []showOption
	Args             []showOption
	Subcommand       []showOption
	MaxNameLen       int
	ShowUsageDefault bool
}

type Option

type Option struct {
	// contains filtered or unexported fields
}

type Subcommand

type Subcommand struct {
	*Clop
	// contains filtered or unexported fields
}

使用递归定义,可以很轻松地解决subcommand嵌套的情况

type Tag

type Tag string

func (Tag) Get

func (tag Tag) Get(key string) (value string)

func (Tag) Lookup

func (tag Tag) Lookup(key string) (value string, ok bool)

该函数原版本来自stdlib, 简单修改了下

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL