Categorygithub.com/fungolang/screw
modulepackage
1.0.1
Repository: https://github.com/fungolang/screw.git
Documentation: pkg.go.dev

# README

screw

Go

screw is a command line parser based on struct.

Feature

  • Support for environment variable binding env DEBUG=xx ./proc
  • Supports parameter collection cat a.txt b.txt, you can puta.txt, b.txtBulk members are categorized and collected into the structure members you specify
  • Support for short optionsproc -d or long optionsproc --debug
  • POSIX style command line support, supporting command combinations ls -ltrisls -l -t -rabbreviated to facilitate the implementation of common POSIX standard commands
  • Sub Command(subcommand)support to facilitate git-style subcommandgit add ,a concise way of registering subcommands, as long as the structure is written, 3,4,5 to infinite subcommands are also supported
  • Defaults supportdefault:"1", support multiple data types so you don't have to worry about type conversion
  • Repeat Command Error
  • Strict short option, long option error. Avoid Ambiguity Options
  • Validation mode support, no need to write a bunch ofif x!= "" or if y!=0
  • command priority can be obtained to set command aliases easily
  • Parse flag package code to generate screw code
  • SpecifyParsefunction to automatically bind data

Installation

go get github.com/fungolang/screw

Quick start

package main

import (
	"fmt"
	"github.com/fungolang/screw"
)

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

func main() {

	h := Hello{}
	screw.SetVersion("v0.2.0")
	screw.SetAbout("This is a simple example demo")
	screw.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/fungolang/screw"
)

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

func main() {
        id := &IntDemo{}
        screw.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/fungolang/screw"
)

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

func main() {
        fd := &Float64Demo{}
        screw.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/fungolang/screw"
)

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

func main() {
        dd := &DurationDemo{}
        screw.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/fungolang/screw"
)

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

func main() {
        s := &StringDemo{}
        screw.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/fungolang/screw"
)

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

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

similar to join command

Adding the greedy attribute supports greedy array writing. Similar to join command.

package main

import (
    "fmt"

    "github.com/fungolang/screw"
)

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

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

/*
Run
./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/fungolang/screw"
)

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

func main() {

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

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

set default value

Default values can be set using default tags, written directly to common types, and JSON for composite types

package main

import (
    "fmt"
    "github.com/fungolang/screw"
)

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{}
    screw.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/fungolang/screw"
)

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

func main() {
	e := env{}
	screw.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 generates an environment variable name based on the structure name, with the rule that the hump command name is changed to an uppercase underscore

// file name use_env.go
package main

import (
	"fmt"
	"github.com/fungolang/screw"
)

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

func main() {
	e := env{}
	screw.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/fungolang/screw"
)

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

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

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

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

	switch {
	case screw.IsSetSubcommand("mv"):
		fmt.Printf("subcommand mv\n")
	case screw.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 way to implement subcommands using screw is that the subcommand structure only implements the SubMain method, which the screw library will automatically call for you. It is recommended that you omit writing a bunch of if else judgments in main (as opposed to method 1), especially when there are a lot of subcommands.

package main

import (
	"fmt"
	"github.com/fungolang/screw"
)

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

func (a *add) SubMain() {
	//When the add subcommand is set
	//screw automatically calls this function
}

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

func (m *mv) SubMain() {
	//When MV subcommand is set
	//screw automatically calls this function
}

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

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

Get command priority

package main

import (
	"fmt"
	"github.com/fungolang/screw"
)

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

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

func main() {

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

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

Can only be set once

Specified options can only be set once, and error will occur if the command line option is used twice.

package main

import (
    "github.com/fungolang/screw"
)

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

func main() {
    o := Once{}
    screw.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 Writing, using fixed short, long tags to generate short, long options. It can be visually compared with cat examples. The more command-line options you have, the more time you save and efficiency you can achieve.

package main

import (
    "fmt"
    "github.com/fungolang/screw"
)

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

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

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

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

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

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

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

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

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

Multi structure series

Multi-structure series function. A command line view composed of multiple structures If command-line parsing is going to take place within more than one (>=2) structure, you can use the structure concatenation function, which is used by the first few structures.screw.Register()Interface, last structure usesscrew.Bind()function.

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



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

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

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

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

 // Test the effect with the following command line parameters
 // ./example --server-address", ":8080", "--rate", "1s", "--thread-num", "20", "--open-vad"

Support callback function parsing

  • Use the notation callback=name, where name is the parsing function that needs to be called.
type TestCallback struct {
	Size int `screw:"short;long;callback=ParseSize" usage:"parse size"`
	Max  int `screw:"short;long"`
}

func (t *TestCallback) ParseSize(val string) {
	//Do some parsing
	// t.Size =value after parsing
}

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

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

Advanced features

Advanced features include some features of screw packages

Parsing flag code to generate screw code

If your command wants to migrate to screw, but in the face of a lot of flag code, use the screw command to do everything.

1.Install screw command

go get github.com/fungolang/screw/cmd/screw

2.Resolving code containing flag packages using screw

Convert flag libraries inside main.go to screw package calls

screw -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()
}

The output code is as follows

package main

import (
	"github.com/fungolang/screw"
)

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

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

Implementing linux command options

cat

package main

import (
	"fmt"
	"github.com/fungolang/screw"
)

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

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

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

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

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

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

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

func main() {

	c := cat{}
	err := screw.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>
*/

# Functions

Bind interface, including the following functions Structure field registration Command line parsing.
No description provided by the author
No description provided by the author
Bind must be a successful version.
No description provided by the author
No description provided by the author
Constructor.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
Print Help.

# Variables

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

No description provided by the author
No description provided by the author
Parse flag.
No description provided by the author
No description provided by the author

# Type aliases

No description provided by the author