Coder Social home page Coder Social logo

hedzr / cmdr Goto Github PK

View Code? Open in Web Editor NEW
133.0 5.0 9.0 2.43 MB

POSIX-compliant command-line UI (CLI) parser and Hierarchical-configuration operations

Home Page: https://hedzr.com/cmdr-docs/

License: Apache License 2.0

Go 99.14% Shell 0.18% Dockerfile 0.65% Makefile 0.03%
command-line-parser command-line-interface commandline-interface commandlineparser commandline-arguments commandline command-line cli getopt hierarchy-configurations

cmdr's Introduction

cmdr

Go GitHub go.mod Go version GitHub tag (latest SemVer) GoDoc FOSSA Status go.dev Go Report Card codecov Mentioned in Awesome Go

Unstable v2: Working in progress, the main API might be stable till v2.1.0

cmdr is a POSIX-compliant, command-line argument parser library with Golang.

Since v2, our license moved to Apache 2.0.

ee99d078e2f7

See the image frames at #1.

Motivation

There are many dirty codes in the cmdr.v1 which cannot be refactored as well. It prompted we reimplment a new one as v2.

The passing winter, we did rewrite the cmdr.v2 to keep it clean and absorbed in parsing and dispatching. Some abilities were removed and relayouted to new modules. That's why the Option Store has been split as a standalone module hedzr/store1. A faster and colorful slog-like logger has been implemented freshly as hedzr/logg2. hedzr/evendeep3 provides a deep fully-functional object copy tool. It helps to deep copy some internal objects easily. It is also ready for you. hedzr/is4 is an environment detecting framework with many out-of-the-box detectors, such as is.InTesting and is.InDebugging.

Anyway, the whole supply chain painted:

graph BT
  hzis(hedzr/is)-->hzlogg(hedzr/logg/slog)
  hzis-->hzdiff(hedzr/evendeep)
  hzlogg-->hzdiff
  hzerrors(gopkg.in/hedzr/errors.v3)-->hzdiff
  hzerrors-->hzstore(hedzr/store)
  hzis-->hzstore(hedzr/store)
  hzlogg-->hzstore(hedzr/store)
  hzdiff-->hzstore(hedzr/store)
  hzlogg-->cmdr(hedzr/cmdr/v2)
  hzis-->cmdr
  hzlogg-->cmdr
  hzdiff-->cmdr
  hzstore-->cmdr

Loading
  1. The .netCore version Cmdr.Core is available now.
  2. A cxx version cmdr-cxx was released (Happy Spring Festival 2021).

Features

v2 is in earlier state but the baseline is stable:

  • Basic command-line arguments parser like POSIX getopt and go stdlib flag.

    • Short flag, single character or a string here to support golang CLI style

      • Compact flags if possible. Also the sticking value will be parsed. For example: -c1b23zv = -c 1 -b 23 -z -v
      • Hit info: -v -v -v = -v (hitCount == 3, hitTitle == 'v')
      • Optimized for slice: -a 1,2,3 -a 4 -a 5,6 => []int{1,2,3,4,5,6}
      • Value can be sticked or not. Valid forms: -c1, -c 1, -c=1 and quoted: -c"1", -c'1', -c="1", -c='1', etc.
      • ...
    • Long flags and aliases

    • Eventual subcommands: an OnAction handler can be attached.

    • Eventual subcommands and flags: PreActions, PostAction, OnMatching, OnMatched, ...,

    • Auto bind to environment variables, For instance: command line HELP=1 app = app --help.

    • Builtin commands and flags:

      • --help, -h
      • --version, -V
      • --verbose. -v
      • ...
    • Help Screen: auto generate and print

    • Smart suggestions when wrong cmd or flag parsed. Jaro-winkler distance is used.

  • Loosely parse subcmds and flags:

    • Subcommands and flags can be input in any order
    • Lookup a flag along with subcommands tree for resolving the duplicated flags
  • Can integrate with hedzr/store1

    • High-performance in-memory KV store for hierarchical data.
    • Extract data to user-spec type with auto-converting
    • Loadable external sources: environ, config files, consul, etcd, etc..
      • extensible codecs and providers for loading from data sources
  • Three kinds of config files are searched and loaded via loaders.NewConfigFileLoader():

    • Primary: main config, shipped with installable package.
    • Secondary: 2ndry config. Wrapped by reseller(s).
    • Alternative: user's local config, writeable. The runtime changeset will be written back to this file while app stopping.
  • TODO

    • Shell autocompletion
    • ...

More minor details need to be evaluated and reimplemented if it's still meaningful in v2.

History

v2 is staying in earlier state:

  • Latest: v2.0.3

    • split loaders as a standalone repo
    • split examples and tests to standalone
    • update deps
    • fix bugs
  • Full list: CHANGELOG

Guide

A simple cli-app can be:

package main

import (
	logz "github.com/hedzr/logg/slog"

	"github.com/hedzr/cmdr/v2"
	"github.com/hedzr/cmdr/v2/cli"
	"github.com/hedzr/cmdr/v2/loaders"
	"github.com/hedzr/cmdr/v2/pkg/dir"
	"github.com/hedzr/store"
)

func main() {
	app := prepareApp()

	// // simple run the parser of app and trigger the matched command's action
	// _ = app.Run(
	// 	cmdr.WithForceDefaultAction(false), // true for debug in developing time
	// )

	if err := app.Run(
		cmdr.WithStore(store.New()),
		cmdr.WithExternalLoaders(
			loaders.NewConfigFileLoader(),
			loaders.NewEnvVarLoader(),
		),
		cmdr.WithForceDefaultAction(true), // true for debug in developing time
	); err != nil {
		logz.Error("Application Error:", "err", err)
	}
}

func prepareApp() (app cli.App) {
	app = cmdr.New().
		Info("demo-app", "0.3.1").
		Author("hedzr")
	app.AddFlg(func(b cli.FlagBuilder) {
		b.Titles("no-default").
			Description("disable force default action").
			OnMatched(func(f *cli.Flag, position int, hitState *cli.MatchState) (err error) {
				app.Store().Set("app.force-default-action", false)
				return
			})
	})
	app.AddCmd(func(b cli.CommandBuilder) {
		b.Titles("jump").
			Description("jump command").
			Examples(`jump example`).
			Deprecated(`jump is a demo command`).
			Hidden(false)

		b.AddCmd(func(b cli.CommandBuilder) {
			b.Titles("to").
				Description("to command").
				Examples(``).
				Deprecated(`v0.1.1`).
				Hidden(false).
				OnAction(func(cmd *cli.Command, args []string) (err error) {
					app.Store().Set("app.demo.working", dir.GetCurrentDir())
					println()
					println(dir.GetCurrentDir())
					println()
					println(app.Store().Dump())
					return // handling command action here
				})
			b.AddFlg(func(b cli.FlagBuilder) {
				b.Default(false).
					Titles("full", "f").
					Description("full command").
					Build()
			})
		})
	})

	app.AddFlg(func(b cli.FlagBuilder) {
		b.Titles("dry-run", "n").
			Default(false).
			Description("run all but without committing")
	})

	app.Flg("wet-run", "w").
		Default(false).
		Description("run all but with committing").
		Build() // no matter even if you're adding the duplicated one.
	return
}

Thanks to JODL

Thanks to JetBrains for donating product licenses to help develop cmdr
jetbrains goland

License

Since v2, our license moved to Apache 2.0.

The v1 keeps under MIT itself.

FOSSA Status

Footnotes

  1. hedzr/store is a high-performance configure management library 2

  2. hedzr/logg provides a slog like and colorful logging library

  3. hedzr/evendeep offers a customizable deepcopy tool to you. There are also deepequal, deepdiff tools in it.

  4. hedzr/is is a basic environ detectors library

cmdr's People

Contributors

dependabot[bot] avatar fossabot avatar hedzr 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

cmdr's Issues

禁用 help 之后

谢谢你的作品,很棒的库,流式调用链相当舒服,使用中遇到了一个疑问:

const (
	versionName = "0.0.1"
	appName     = "cmdrtest"
)

func buildRootCmd() (rootCmd *cmdr.RootCommand) {

	root := cmdr.Root(appName, versionName)

	// root.NewSubCommand().
	// 	Titles("h", "help").
	// 	Description("show help screen", "").
	// 	Action(func(cmd *cmdr.Command, args []string) (err error) {
	// 		fmt.Println("this is help text")
	// 		os.Exit(0)
	// 		return
	// 	})

	// root.NewFlag(cmdr.OptFlagTypeBool).
	// 	Titles("h", "help").
	// 	Description("show help screen", "").
	// 	DefaultValue(false, "").
	// 	OnSet(func(keyPath string, value interface{}) {
	// 		fmt.Println("this is help text")
	// 		os.Exit(0)
	// 		return
	// 	})

	rootCmd = root.RootCommand()

	return
}
func main() {

	// To disable internal commands and flags, uncomment the following codes
	cmdr.EnableVersionCommands = false
	cmdr.EnableVerboseCommands = false
	cmdr.EnableHelpCommands = false
	cmdr.EnableGenerateCommands = false
	cmdr.EnableCmdrCommands = false

	rootCmd := buildRootCmd()
	if err := cmdr.Exec(rootCmd); err != nil {
		panic(err)
	}
}

这个时候 -h 或者 --help 似乎是无效的 (Unknown flag: -h)

但是直接运行会默认输出 usage,最后一行仍然会是:

Type '-h' or '--help' to get command help screen.

如果想要实现它,用 NewSubCommand 的话会不能带 - 符号,就和最后一行的输出相矛盾了。此时该用 NewFlag(cmdr.OptFlagTypeBool)OnSet 吗?但总感觉它应该是 Command 范畴,因为可能需要实现 go help build 这种效果

双引号的问题

const (
	versionName = "0.0.1"
	appName     = "cmdrtest"
)

func buildRootCmd() (rootCmd *cmdr.RootCommand) {

	root := cmdr.Root(appName, versionName)

	root.NewFlag(cmdr.OptFlagTypeString).
		Titles("o", "output-file").
		Description("output file", "").
		DefaultValue("", "").
		OnSet(func(keyPath string, value interface{}) {
			fmt.Println(keyPath)
			os.Exit(0)
			return
		})

	rootCmd = root.RootCommand()

	return
}

func main() {

	rootCmd := buildRootCmd()
	if err := cmdr.Exec(rootCmd,
		cmdr.WithBuiltinCommands(false, false, false, false, false),
	); err != nil {
		panic(err)
	}
}

编译完如果 cmdrtest -o "" 就会 panic

panic: runtime error: index out of range [0] with length 0

goroutine 1 [running]:
github.com/hedzr/cmdr.(*ptpkg).preprocessPkg(0xc0002c6000, 0xc00002a0c0, 0x3, 0x4, 0x0, 0x5)
        $GOPATH/pkg/mod/github.com/hedzr/[email protected]/ptpkg.go:170 +0x33a
github.com/hedzr/cmdr.(*ptpkg).processTypeString(0xc0002c6000, 0xc00002a0c0, 0x3, 0x4, 0xffffffffffffffff, 0xc0000220d1)
        $GOPATH/pkg/mod/github.com/hedzr/[email protected]/ptpkg.go:280 +0x6e
github.com/hedzr/cmdr.(*ptpkg).tryExtractingValue(0xc0002c6000, 0xc00002a0c0, 0x3, 0x4, 0xc0002784e8, 0x9e509f)
        $GOPATH/pkg/mod/github.com/hedzr/[email protected]/ptpkg.go:100 +0x112
github.com/hedzr/cmdr.(*ExecWorker).flagsMatched(0xc0002b8120, 0xc0002c6000, 0xc0002981c0, 0xc00002a0c0, 0x3, 0x4, 0x9c8ede, 0xc0002c6000, 0xc0002c6028)
        $GOPATH/pkg/mod/github.com/hedzr/[email protected]/exec_match.go:133 +0x5f
github.com/hedzr/cmdr.(*ExecWorker).flagsMatching(0xc0002b8120, 0xc0002c6000, 0xc0002981c0, 0xc00022be48, 0xc00002a0c0, 0x3, 0x4, 0x0, 0x0, 0x0)
        $GOPATH/pkg/mod/github.com/hedzr/[email protected]/exec_match.go:99 +0x3ce
github.com/hedzr/cmdr.(*ExecWorker).xxTestCmd(0xc0002b8120, 0xc0002c6000, 0xc00022be48, 0xc0002981c0, 0xc00002a0c0, 0x3, 0x4, 0x0, 0x1, 0xc000134060)
        $GOPATH/pkg/mod/github.com/hedzr/[email protected]/exec.go:244 +0x15c
github.com/hedzr/cmdr.(*ExecWorker).InternalExecFor(0xc0002b8120, 0xc0002981c0, 0xc00002a0c0, 0x3, 0x4, 0x0, 0x0)
        $GOPATH/pkg/mod/github.com/hedzr/[email protected]/exec.go:194 +0x2da
github.com/hedzr/cmdr.Exec(0xc0002981c0, 0xc00022bf38, 0x1, 0x1, 0x43e6c1, 0x1081ec0)
        $GOPATH/pkg/mod/github.com/hedzr/[email protected]/exec.go:88 +0x9e
main.main()

单引号的话就没问题~

Gallery

wget-demo app

image

demo app

image

man page

# generate man pages to `./man1`
mkdir man1 && bin/demo gen man
# move them to system location:
mv ./man1/* /usr/local/man/man1/
# And, query them via `man 1 demo` or `man 1 demo server`:
man demo-generate

And:
image

ToggleGroup & flag.Args()

Hi,
Maybe I'm missing what the feature ToggleGroup is but I thought that by setting a values like this would make the values mutually exclusive

                {
                        BaseOpt: cmdr.BaseOpt{
                                Short:       "L",
                                Full:        "list",
                                Description: "list command",
                                Group:       cStartup,
                        },
                        DefaultValue: false,
                        ToggleGroup:  "target",
                },
                {
                        BaseOpt: cmdr.BaseOpt{
                                Short:       "G",
                                Full:        "grain",
                                Description: "grain command",
                                Group:       cStartup,
                        },
                        DefaultValue: false,
                        ToggleGroup:  "target",
                },

but I can run both commands and I don't get an error.

Also, I would have wanted to have the option to get the remaining args that were not parsed just as in the official flag package.

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.