├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── action.go ├── cli.go ├── cli_test.go ├── clir.go ├── clir_logo.png ├── clir_test.go ├── command.go ├── command_test.go ├── examples ├── basic │ └── main.go ├── chained │ └── main.go ├── custom-banner │ └── main.go ├── custom-flag-error │ └── main.go ├── default │ └── main.go ├── flags-compact │ └── main.go ├── flags-function │ └── main.go ├── flags-positional │ └── main.go ├── flags-slice │ └── main.go ├── flags │ └── main.go ├── flagstruct │ └── main.go ├── hidden │ └── main.go ├── nested-subcommands │ └── main.go ├── otherargs │ └── main.go ├── subcommandinheritflags │ └── main.go └── subcommands │ └── main.go ├── go.mod ├── go.sum └── website ├── docs ├── examples │ ├── basic.md │ ├── chained.md │ ├── custom-banner.md │ ├── custom-flag-error.md │ ├── default.md │ ├── flags-compact.md │ ├── flags-function.md │ ├── flags-positional.md │ ├── flags-slice.md │ ├── flags.md │ ├── flagstruct.md │ ├── hidden.md │ ├── nested-subcommands.md │ ├── otherargs.md │ ├── subcommandinheritflags.md │ └── subcommands.md ├── faq.md ├── gettingstarted.md ├── guide │ ├── actions.md │ ├── cli.md │ ├── custombanner.md │ ├── flags.md │ ├── index.md │ ├── otherargs.md │ ├── prepostrun.md │ └── subcommands.md ├── index.md └── static │ ├── clir_logo.png │ └── clir_logo_white.png ├── mkdocs.yml └── requirements.txt /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | types: [assigned, opened, synchronize, reopened] 9 | 10 | jobs: 11 | build: 12 | name: Build & run examples 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [macos-latest, ubuntu-latest, windows-latest] 17 | steps: 18 | 19 | - name: Set up Go 1.19 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version: 1.22 23 | id: go 24 | 25 | - name: Check out code 26 | uses: actions/checkout@master 27 | 28 | - name: Build basic 29 | run: go build -v ./examples/basic 30 | 31 | - name: Run basic 32 | run: ./basic 33 | 34 | - name: Build chained 35 | run: go build -v ./examples/chained 36 | 37 | - name: Run chained 38 | run: ./chained -name World -age 30 -awesome true 39 | 40 | - name: Build custom-banner 41 | run: go build -v ./examples/custom-banner 42 | 43 | - name: Run custom-banner 44 | run: ./custom-banner -help 45 | 46 | - name: Build flags 47 | run: go build -v ./examples/flags 48 | 49 | - name: Run flags 50 | run: ./flags -name World -age 30 -awesome true 51 | 52 | - name: Build flags-compact 53 | run: go build -v ./examples/flags-compact 54 | 55 | - name: Run flags-compact 56 | run: ./flags-compact -name World -age 30 -awesome true 57 | 58 | - name: Build nested-subcommands 59 | run: go build -v ./examples/nested-subcommands 60 | 61 | - name: Run nested-subcommands top 62 | run: ./nested-subcommands top 63 | 64 | - name: Run nested-subcommands middle 65 | run: ./nested-subcommands top middle 66 | 67 | - name: Run nested-subcommands bottom 68 | run: ./nested-subcommands top middle bottom 69 | 70 | - name: Build subcommands 71 | run: go build -v ./examples/subcommands 72 | 73 | - name: Run subcommands init 74 | run: ./subcommands init 75 | 76 | - name: Run subcommands test 77 | run: ./subcommands test 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | examples/otherargs/otherargs 14 | .idea 15 | website/site -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | - `Added` for new features. 9 | - `Changed` for changes in existing functionality. 10 | - `Deprecated` for soon-to-be removed features. 11 | - `Removed` for now removed features. 12 | - `Fixed` for any bug fixes. 13 | - `Security` in case of vulnerabilities. 14 | 15 | ## [Unreleased] 16 | 17 | ### Added 18 | - Added support for slice flags. Added by @zkep in [PR](https://github.com/leaanthony/clir/pull/23) 19 | 20 | ### Fixed 21 | 22 | ### Changed -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Lea Anthony 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |

4 |

5 | A Simple and Clear CLI library. Dependency free.

6 | 7 | 8 | Awesome 9 | 10 | CodeFactor 11 | 12 | 13 | 14 | 15 |

16 | 17 | ### Features 18 | 19 | * Nested Subcommands 20 | * Uses the standard library `flag` package 21 | * Struct based flags 22 | * Positional Args 23 | * Auto-generated help 24 | * Custom banners 25 | * Hidden Subcommands 26 | * Default Subcommand 27 | * Dependency free 28 | 29 | ### Example 30 | 31 | ```go 32 | package main 33 | 34 | import ( 35 | "fmt" 36 | 37 | "github.com/leaanthony/clir" 38 | ) 39 | 40 | func main() { 41 | // Create new cli 42 | cli := clir.NewCli("Flags", "A simple example", "v0.0.1") 43 | 44 | // Name 45 | name := "Anonymous" 46 | cli.StringFlag("name", "Your name", &name) 47 | 48 | // Define action for the command 49 | cli.Action(func() error { 50 | fmt.Printf("Hello %s!\n", name) 51 | return nil 52 | }) 53 | 54 | if err := cli.Run(); err != nil { 55 | fmt.Printf("Error encountered: %v\n", err) 56 | } 57 | } 58 | ``` 59 | 60 | #### Generated Help 61 | 62 | ```shell 63 | $ flags --help 64 | Flags v0.0.1 - A simple example 65 | 66 | Flags: 67 | 68 | -help 69 | Get help on the 'flags' command. 70 | -name string 71 | Your name 72 | ``` 73 | 74 | #### Documentation 75 | 76 | The main documentation may be found [here](https://clir.leaanthony.com). 77 | 78 | #### License Status 79 | 80 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fleaanthony%2Fclir.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fleaanthony%2Fclir?ref=badge_large) 81 | -------------------------------------------------------------------------------- /action.go: -------------------------------------------------------------------------------- 1 | package clir 2 | 3 | // Action represents a function that gets calls when the command is called by 4 | // the user 5 | type Action func() error 6 | -------------------------------------------------------------------------------- /cli.go: -------------------------------------------------------------------------------- 1 | package clir 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | // Cli - The main application object. 9 | type Cli struct { 10 | version string 11 | rootCommand *Command 12 | defaultCommand *Command 13 | preRunCommand func(*Cli) error 14 | postRunCommand func(*Cli) error 15 | bannerFunction func(*Cli) string 16 | errorHandler func(string, error) error 17 | } 18 | 19 | // Version - Get the Application version string. 20 | func (c *Cli) Version() string { 21 | return c.version 22 | } 23 | 24 | // Name - Get the Application Name 25 | func (c *Cli) Name() string { 26 | return c.rootCommand.name 27 | } 28 | 29 | // ShortDescription - Get the Application short description. 30 | func (c *Cli) ShortDescription() string { 31 | return c.rootCommand.shortdescription 32 | } 33 | 34 | // SetBannerFunction - Set the function that is called 35 | // to get the banner string. 36 | func (c *Cli) SetBannerFunction(fn func(*Cli) string) { 37 | c.bannerFunction = fn 38 | } 39 | 40 | // SetErrorFunction - Set custom error message when undefined 41 | // flags are used by the user. First argument is a string containing 42 | // the command path used. Second argument is the undefined flag error. 43 | func (c *Cli) SetErrorFunction(fn func(string, error) error) { 44 | c.errorHandler = fn 45 | } 46 | 47 | // AddCommand - Adds a command to the application. 48 | func (c *Cli) AddCommand(command *Command) { 49 | c.rootCommand.AddCommand(command) 50 | } 51 | 52 | // PrintBanner - Prints the application banner! 53 | func (c *Cli) PrintBanner() { 54 | fmt.Println(c.bannerFunction(c)) 55 | fmt.Println("") 56 | } 57 | 58 | // PrintHelp - Prints the application's help. 59 | func (c *Cli) PrintHelp() { 60 | c.rootCommand.PrintHelp() 61 | } 62 | 63 | // Run - Runs the application with the given arguments. 64 | func (c *Cli) Run(args ...string) error { 65 | if c.preRunCommand != nil { 66 | err := c.preRunCommand(c) 67 | if err != nil { 68 | return err 69 | } 70 | } 71 | if len(args) == 0 { 72 | args = os.Args[1:] 73 | } 74 | if err := c.rootCommand.run(args); err != nil { 75 | return err 76 | } 77 | 78 | if c.postRunCommand != nil { 79 | err := c.postRunCommand(c) 80 | if err != nil { 81 | return err 82 | } 83 | } 84 | 85 | return nil 86 | } 87 | 88 | // DefaultCommand - Sets the given command as the command to run when 89 | // no other commands given. 90 | func (c *Cli) DefaultCommand(defaultCommand *Command) *Cli { 91 | c.defaultCommand = defaultCommand 92 | return c 93 | } 94 | 95 | // NewSubCommand - Creates a new SubCommand for the application. 96 | func (c *Cli) NewSubCommand(name, description string) *Command { 97 | return c.rootCommand.NewSubCommand(name, description) 98 | } 99 | 100 | // NewSubCommandInheritFlags - Creates a new SubCommand for the application, inherit flags from parent Command 101 | func (c *Cli) NewSubCommandInheritFlags(name, description string) *Command { 102 | return c.rootCommand.NewSubCommandInheritFlags(name, description) 103 | } 104 | 105 | // PreRun - Calls the given function before running the specific command. 106 | func (c *Cli) PreRun(callback func(*Cli) error) { 107 | c.preRunCommand = callback 108 | } 109 | 110 | // PostRun - Calls the given function after running the specific command. 111 | func (c *Cli) PostRun(callback func(*Cli) error) { 112 | c.postRunCommand = callback 113 | } 114 | 115 | // BoolFlag - Adds a boolean flag to the root command. 116 | func (c *Cli) BoolFlag(name, description string, variable *bool) *Cli { 117 | c.rootCommand.BoolFlag(name, description, variable) 118 | return c 119 | } 120 | 121 | // StringFlag - Adds a string flag to the root command. 122 | func (c *Cli) StringFlag(name, description string, variable *string) *Cli { 123 | c.rootCommand.StringFlag(name, description, variable) 124 | return c 125 | } 126 | 127 | // IntFlag - Adds an int flag to the root command. 128 | func (c *Cli) IntFlag(name, description string, variable *int) *Cli { 129 | c.rootCommand.IntFlag(name, description, variable) 130 | return c 131 | } 132 | 133 | func (c *Cli) AddFlags(flags interface{}) *Cli { 134 | c.rootCommand.AddFlags(flags) 135 | return c 136 | } 137 | 138 | // Action - Define an action from this command. 139 | func (c *Cli) Action(callback Action) *Cli { 140 | c.rootCommand.Action(callback) 141 | return c 142 | } 143 | 144 | // LongDescription - Sets the long description for the command. 145 | func (c *Cli) LongDescription(longdescription string) *Cli { 146 | c.rootCommand.LongDescription(longdescription) 147 | return c 148 | } 149 | 150 | // OtherArgs - Returns the non-flag arguments passed to the cli. 151 | // NOTE: This should only be called within the context of an action. 152 | func (c *Cli) OtherArgs() []string { 153 | return c.rootCommand.flags.Args() 154 | } 155 | 156 | func (c *Cli) NewSubCommandFunction(name string, description string, test interface{}) *Cli { 157 | c.rootCommand.NewSubCommandFunction(name, description, test) 158 | return c 159 | } 160 | -------------------------------------------------------------------------------- /cli_test.go: -------------------------------------------------------------------------------- 1 | package clir 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestCli(t *testing.T) { 10 | c := NewCli("test", "description", "0") 11 | 12 | t.Run("Run SetBannerFunction()", func(t *testing.T) { 13 | c.SetBannerFunction(func(*Cli) string { return "" }) 14 | }) 15 | 16 | t.Run("Run SetFlagFunction()", func(t *testing.T) { 17 | c.SetErrorFunction(func(cmdPath string, err error) error { return err }) 18 | }) 19 | 20 | t.Run("Run AddCommand()", func(t *testing.T) { 21 | c.AddCommand(&Command{name: "test"}) 22 | }) 23 | 24 | t.Run("Run PrintBanner()", func(t *testing.T) { 25 | c.PrintBanner() 26 | }) 27 | 28 | t.Run("Run Run()", func(t *testing.T) { 29 | c.Run("test") 30 | c.Run() 31 | 32 | c.preRunCommand = func(*Cli) error { return errors.New("testing coverage") } 33 | c.Run("test") 34 | c.postRunCommand = func(*Cli) error { return errors.New("testing coverage") } 35 | }) 36 | 37 | t.Run("Run DefaultCommand()", func(t *testing.T) { 38 | c.DefaultCommand(&Command{}) 39 | }) 40 | 41 | t.Run("Run NewSubCommand()", func(t *testing.T) { 42 | c.NewSubCommand("name", "description") 43 | }) 44 | 45 | t.Run("Run PreRun()", func(t *testing.T) { 46 | c.PreRun(func(*Cli) error { return nil }) 47 | }) 48 | 49 | t.Run("Run PostRun()", func(t *testing.T) { 50 | c.PostRun(func(*Cli) error { return nil }) 51 | }) 52 | 53 | t.Run("Run BoolFlag()", func(t *testing.T) { 54 | var variable bool 55 | c.BoolFlag("bool", "description", &variable) 56 | }) 57 | 58 | t.Run("Run StringFlag()", func(t *testing.T) { 59 | var variable string 60 | c.StringFlag("string", "description", &variable) 61 | }) 62 | 63 | t.Run("Run IntFlag()", func(t *testing.T) { 64 | var variable int 65 | c.IntFlag("int", "description", &variable) 66 | }) 67 | 68 | t.Run("Run Action()", func(t *testing.T) { 69 | c.Action(func() error { return nil }) 70 | }) 71 | 72 | t.Run("Run LongDescription()", func(t *testing.T) { 73 | c.LongDescription("long description") 74 | }) 75 | } 76 | 77 | type testStruct struct { 78 | Mode string `name:"mode" description:"The mode of build"` 79 | Count int 80 | } 81 | 82 | func TestCli_CLIAddFlags(t *testing.T) { 83 | c := NewCli("test", "description", "0") 84 | 85 | ts := &testStruct{} 86 | c.AddFlags(ts) 87 | 88 | modeFlag := c.rootCommand.flags.Lookup("mode") 89 | if modeFlag == nil { 90 | t.Errorf("expected flag mode to be added") 91 | } 92 | if modeFlag.Name != "mode" { 93 | t.Errorf("expected flag name to be added") 94 | } 95 | if modeFlag.Usage != "The mode of build" { 96 | t.Errorf("expected flag description to be added") 97 | } 98 | 99 | c.Action(func() error { 100 | if ts.Mode != "123" { 101 | t.Errorf("expected flag value to be set") 102 | } 103 | return nil 104 | }) 105 | e := c.Run("-mode", "123") 106 | if e != nil { 107 | t.Errorf("expected no error, got %v", e) 108 | } 109 | } 110 | 111 | func TestCli_CommandAddFlags(t *testing.T) { 112 | c := NewCli("test", "description", "0") 113 | sub := c.NewSubCommand("sub", "sub description") 114 | 115 | ts := &testStruct{} 116 | sub.AddFlags(ts) 117 | 118 | sub.Action(func() error { 119 | if ts.Mode != "123" { 120 | t.Errorf("expected flag value to be set") 121 | } 122 | return nil 123 | }) 124 | e := c.Run("sub", "-mode", "123") 125 | if e != nil { 126 | t.Errorf("expected no error, got %v", e) 127 | } 128 | } 129 | 130 | func TestCli_InheritFlags(t *testing.T) { 131 | c := NewCli("test", "description", "0") 132 | var name string 133 | c.StringFlag("name", "name of person", &name) 134 | sub := c.NewSubCommandInheritFlags("sub", "sub description") 135 | 136 | sub.Action(func() error { 137 | if name != "Janet" { 138 | t.Errorf("expected name to be Janet") 139 | } 140 | return nil 141 | }) 142 | e := c.Run("sub", "-name", "Janet") 143 | if e != nil { 144 | t.Errorf("expected no error, got %v", e) 145 | } 146 | 147 | } 148 | 149 | func TestCli_CommandAddFlagsWrongType(t *testing.T) { 150 | c := NewCli("test", "description", "0") 151 | sub := c.NewSubCommand("sub", "sub description") 152 | 153 | recoverTest := func() { 154 | var r interface{} 155 | if r = recover(); r == nil { 156 | t.Errorf("expected panic") 157 | } 158 | if r.(string) != "AddFlags() requires a pointer to a struct" { 159 | t.Errorf(`Expected: "AddFlags() requires a pointer to a struct". Got: "` + r.(string) + `"`) 160 | } 161 | } 162 | 163 | defer recoverTest() 164 | 165 | ts := testStruct{} 166 | sub.AddFlags(ts) 167 | 168 | e := c.Run("sub", "-mode", "123") 169 | if e != nil { 170 | t.Errorf("expected no error, got %v", e) 171 | } 172 | 173 | } 174 | 175 | func TestCli_CommandAddFlagsWrongPointerType(t *testing.T) { 176 | c := NewCli("test", "description", "0") 177 | sub := c.NewSubCommand("sub", "sub description") 178 | 179 | recoverTest := func() { 180 | var r interface{} 181 | if r = recover(); r == nil { 182 | t.Errorf("expected panic") 183 | } 184 | if r.(string) != "AddFlags() requires a pointer to a struct" { 185 | t.Errorf(`Expected: "AddFlags() requires a pointer to a struct". Got: "` + r.(string) + `"`) 186 | } 187 | } 188 | 189 | defer recoverTest() 190 | 191 | var i *int 192 | sub.AddFlags(i) 193 | 194 | e := c.Run("sub", "-mode", "123") 195 | if e != nil { 196 | t.Errorf("expected no error, got %v", e) 197 | } 198 | 199 | } 200 | 201 | func TestCli_CommandOtherSubArgs(t *testing.T) { 202 | c := NewCli("test", "description", "0") 203 | sub := c.NewSubCommand("sub", "sub description") 204 | 205 | ts := &testStruct{} 206 | sub.AddFlags(ts) 207 | 208 | sub.Action(func() error { 209 | if ts.Mode != "123" { 210 | t.Errorf("expected flag value to be set") 211 | } 212 | other := sub.OtherArgs() 213 | if len(other) != 2 { 214 | t.Errorf("expected 2 other args, got %v", other) 215 | } 216 | if other[0] != "other" { 217 | t.Errorf("expected other arg to be 'other', got %v", other[0]) 218 | } 219 | if other[1] != "args" { 220 | t.Errorf("expected other arg to be 'args', got %v", other[1]) 221 | } 222 | return nil 223 | }) 224 | e := c.Run("sub", "-mode", "123", "other", "args") 225 | if e != nil { 226 | t.Errorf("expected no error, got %v", e) 227 | } 228 | 229 | } 230 | func TestCli_CommandOtherCLIArgs(t *testing.T) { 231 | c := NewCli("test", "description", "0") 232 | 233 | c.Action(func() error { 234 | other := c.OtherArgs() 235 | if len(other) != 2 { 236 | t.Errorf("expected 2 other args, got %v", other) 237 | } 238 | if other[0] != "other" { 239 | t.Errorf("expected other arg to be 'other', got %v", other[0]) 240 | } 241 | if other[1] != "args" { 242 | t.Errorf("expected other arg to be 'args', got %v", other[1]) 243 | } 244 | return nil 245 | }) 246 | e := c.Run("other", "args") 247 | if e != nil { 248 | t.Errorf("expected no error, got %v", e) 249 | } 250 | 251 | } 252 | 253 | type Person struct { 254 | Name string `description:"The name of the person"` 255 | Age int `description:"The age of the person"` 256 | SSN uint `description:"The SSN of the person"` 257 | Age64 int64 `description:"The age of the person"` 258 | SSN64 uint64 `description:"The SSN of the person"` 259 | BankBalance float64 `description:"The bank balance of the person"` 260 | Married bool `description:"Whether the person is married"` 261 | } 262 | 263 | func TestCli_CommandAddFunction(t *testing.T) { 264 | c := NewCli("test", "description", "0") 265 | 266 | createPerson := func(person *Person) error { 267 | if person.Name != "Bob" { 268 | t.Errorf("expected name flag to be 'Bob', got %v", person.Name) 269 | } 270 | if person.Age != 30 { 271 | t.Errorf("expected age flag to be 30, got %v", person.Age) 272 | } 273 | if person.SSN != 123456789 { 274 | t.Errorf("expected ssn flag to be 123456789, got %v", person.SSN) 275 | } 276 | if person.Age64 != 30 { 277 | t.Errorf("expected age64 flag to be 30, got %v", person.Age64) 278 | } 279 | if person.SSN64 != 123456789 { 280 | t.Errorf("expected ssn64 flag to be 123456789, got %v", person.SSN64) 281 | } 282 | if person.BankBalance != 123.45 { 283 | t.Errorf("expected bankbalance flag to be 123.45, got %v", person.BankBalance) 284 | } 285 | if person.Married != true { 286 | t.Errorf("expected married flag to be true, got %v", person.Married) 287 | } 288 | return nil 289 | } 290 | 291 | c.NewSubCommandFunction("create", "create a person", createPerson) 292 | 293 | e := c.Run("create", "-name", "Bob", "-age", "30", "-ssn", "123456789", "-age64", "30", "-ssn64", "123456789", "-bankbalance", "123.45", "-married") 294 | if e != nil { 295 | t.Errorf("expected no error, got %v", e) 296 | } 297 | 298 | } 299 | 300 | type Person2 struct { 301 | Name string `description:"The name of the person" default:"Janet"` 302 | } 303 | 304 | func TestCli_CommandAddFunctionDefaults(t *testing.T) { 305 | c := NewCli("test", "description", "0") 306 | 307 | createPerson := func(person *Person2) error { 308 | if person.Name != "Janet" { 309 | t.Errorf("expected name flag to be 'Janet', got %v", person.Name) 310 | } 311 | return nil 312 | } 313 | 314 | c.NewSubCommandFunction("create", "create a person", createPerson) 315 | 316 | e := c.Run("create") 317 | if e != nil { 318 | t.Errorf("expected no error, got %v", e) 319 | } 320 | 321 | } 322 | 323 | func TestCli_CommandAddFunctionNoFunctionError(t *testing.T) { 324 | c := NewCli("test", "description", "0") 325 | 326 | recoverTest := func() { 327 | var r interface{} 328 | if r = recover(); r == nil { 329 | t.Errorf("expected panic") 330 | } 331 | if r.(string) != "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'" { 332 | t.Errorf(`expected error message to be: "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'". Got: "` + r.(string) + `"`) 333 | } 334 | } 335 | 336 | defer recoverTest() 337 | 338 | c.NewSubCommandFunction("create", "create a person", 0) 339 | 340 | e := c.Run("create") 341 | if e != nil { 342 | t.Errorf("expected no error, got %v", e) 343 | } 344 | 345 | } 346 | func TestCli_CommandAddFunctionNoPointerError(t *testing.T) { 347 | c := NewCli("test", "description", "0") 348 | 349 | recoverTest := func() { 350 | var r interface{} 351 | if r = recover(); r == nil { 352 | t.Errorf("expected panic") 353 | } 354 | if r.(string) != "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'" { 355 | t.Errorf(`expected error message to be: "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'". Got: "` + r.(string) + `"`) 356 | } 357 | } 358 | 359 | defer recoverTest() 360 | 361 | c.NewSubCommandFunction("create", "create a person", func(person Person2) error { 362 | return nil 363 | }) 364 | 365 | e := c.Run("create") 366 | if e != nil { 367 | t.Errorf("expected no error, got %v", e) 368 | } 369 | 370 | } 371 | 372 | func TestCli_CommandAddFunctionMultipleInputError(t *testing.T) { 373 | c := NewCli("test", "description", "0") 374 | 375 | recoverTest := func() { 376 | var r interface{} 377 | if r = recover(); r == nil { 378 | t.Errorf("expected panic") 379 | } 380 | if r.(string) != "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'" { 381 | t.Errorf(`expected error message to be: "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'". Got: "` + r.(string) + `"`) 382 | } 383 | } 384 | 385 | defer recoverTest() 386 | 387 | c.NewSubCommandFunction("create", "create a person", func(person *Person2, count int) error { 388 | return nil 389 | }) 390 | 391 | e := c.Run("create") 392 | if e != nil { 393 | t.Errorf("expected no error, got %v", e) 394 | } 395 | 396 | } 397 | 398 | func TestCli_CommandAddFunctionNoInputError(t *testing.T) { 399 | c := NewCli("test", "description", "0") 400 | 401 | recoverTest := func() { 402 | var r interface{} 403 | if r = recover(); r == nil { 404 | t.Errorf("expected panic") 405 | } 406 | if r.(string) != "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'" { 407 | t.Errorf(`expected error message to be: "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'". Got: "` + r.(string) + `"`) 408 | } 409 | } 410 | 411 | defer recoverTest() 412 | 413 | c.NewSubCommandFunction("create", "create a person", func() error { 414 | return nil 415 | }) 416 | 417 | e := c.Run("create") 418 | if e != nil { 419 | t.Errorf("expected no error, got %v", e) 420 | } 421 | 422 | } 423 | 424 | func TestCli_CommandAddFunctionNoReturnError(t *testing.T) { 425 | c := NewCli("test", "description", "0") 426 | 427 | recoverTest := func() { 428 | var r interface{} 429 | if r = recover(); r == nil { 430 | t.Errorf("expected panic") 431 | } 432 | if r.(string) != "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'" { 433 | t.Errorf(`expected error message to be: "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'". Got: "` + r.(string) + `"`) 434 | } 435 | } 436 | 437 | defer recoverTest() 438 | 439 | c.NewSubCommandFunction("create", "create a person", func(person *Person2) { 440 | }) 441 | 442 | e := c.Run("create") 443 | if e != nil { 444 | t.Errorf("expected no error, got %v", e) 445 | } 446 | } 447 | 448 | func TestCli_CommandAddFunctionWrongReturnError(t *testing.T) { 449 | c := NewCli("test", "description", "0") 450 | 451 | recoverTest := func() { 452 | var r interface{} 453 | if r = recover(); r == nil { 454 | t.Errorf("expected panic") 455 | } 456 | if r.(string) != "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'" { 457 | t.Errorf(`expected error message to be: "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'". Got: "` + r.(string) + `"`) 458 | } 459 | } 460 | 461 | defer recoverTest() 462 | 463 | c.NewSubCommandFunction("create", "create a person", func(person *Person2) int { 464 | return 0 465 | }) 466 | 467 | e := c.Run("create") 468 | if e != nil { 469 | t.Errorf("expected no error, got %v", e) 470 | } 471 | } 472 | 473 | func TestCli_CommandAddFunctionDefaultWrongTypeOfInputsError(t *testing.T) { 474 | c := NewCli("test", "description", "0") 475 | 476 | recoverTest := func() { 477 | var r interface{} 478 | if r = recover(); r == nil { 479 | t.Errorf("expected panic") 480 | } 481 | if r.(string) != "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'" { 482 | t.Errorf(`expected error message to be: "NewSubFunction 'create' requires a function with the signature 'func(*struct) error'". Got: "` + r.(string) + `"`) 483 | } 484 | } 485 | 486 | defer recoverTest() 487 | 488 | c.NewSubCommandFunction("create", "create a person", func(person *int) error { 489 | return nil 490 | }) 491 | 492 | e := c.Run("create") 493 | if e != nil { 494 | t.Errorf("expected no error, got %v", e) 495 | } 496 | } 497 | 498 | type Person7 struct { 499 | Name string `description:"The name of the person"` 500 | Admin bool `description:"Is the person an admin"` 501 | } 502 | 503 | func TestCli_CommandAddFunctionReturnsAnError(t *testing.T) { 504 | c := NewCli("test", "description", "0") 505 | 506 | c.NewSubCommandFunction("create", "create a person", func(person *Person7) error { 507 | return fmt.Errorf("error") 508 | }) 509 | 510 | e := c.Run("create") 511 | if e == nil { 512 | t.Errorf("expected error, got nil") 513 | } 514 | } 515 | 516 | type PosPerson struct { 517 | Name string `description:"The name of the person" pos:"1"` 518 | Admin bool `description:"Is the person an admin" pos:"2"` 519 | } 520 | 521 | func TestCli_PositionalArgs(t *testing.T) { 522 | c := NewCli("test", "test positional args", "0") 523 | 524 | c.NewSubCommandFunction("create", "create a person", func(person *PosPerson) error { 525 | if person.Name != "bob" { 526 | t.Errorf("expected 'bob', got '%v'", person.Name) 527 | } 528 | if person.Admin != true { 529 | t.Errorf("expected true, got %v", person.Admin) 530 | } 531 | return nil 532 | }) 533 | 534 | e := c.Run("create", "bob", "true") 535 | 536 | if e != nil { 537 | t.Errorf("unexpected error") 538 | } 539 | } 540 | 541 | type Person8 struct { 542 | Name string `description:"The name of the person" default:"bob"` 543 | Age uint `description:"The age of the person" default:"40"` 544 | NumberOfPets int `description:"The number of pets the person has" default:"2"` 545 | Salary float64 `description:"The salary of the person" default:"100000.00"` 546 | Admin bool `description:"Is the person an admin" default:"true"` 547 | } 548 | 549 | func TestCli_CommandAddFunctionUsesDefaults(t *testing.T) { 550 | c := NewCli("test", "description", "0") 551 | 552 | createPerson := func(person *Person8) error { 553 | if person.Name != "bob" { 554 | t.Errorf("expected name flag to be 'Bob', got %v", person.Name) 555 | } 556 | if person.Age != 40 { 557 | t.Errorf("expected age flag to be 40, got %v", person.Age) 558 | } 559 | if person.NumberOfPets != 2 { 560 | t.Errorf("expected NumberOfPets flag to be 2, got %v", person.NumberOfPets) 561 | } 562 | if person.Salary != 100000.00 { 563 | t.Errorf("expected Salary flag to be 100000.00, got %v", person.Salary) 564 | } 565 | if person.Admin != true { 566 | t.Errorf("expected Admin flag to be true, got %v", person.Admin) 567 | } 568 | return nil 569 | } 570 | 571 | c.NewSubCommandFunction("create", "create a person", createPerson) 572 | 573 | e := c.Run("create") 574 | if e != nil { 575 | t.Errorf("expected no error, got %v", e) 576 | } 577 | } 578 | -------------------------------------------------------------------------------- /clir.go: -------------------------------------------------------------------------------- 1 | // Package clir provides a simple API for creating command line apps 2 | package clir 3 | 4 | import ( 5 | "fmt" 6 | ) 7 | 8 | // defaultBannerFunction prints a banner for the application. 9 | // If version is a blank string, it is ignored. 10 | func defaultBannerFunction(c *Cli) string { 11 | version := "" 12 | if len(c.Version()) > 0 { 13 | version = " " + c.Version() 14 | } 15 | return fmt.Sprintf("%s%s - %s", c.Name(), version, c.ShortDescription()) 16 | } 17 | 18 | // NewCli - Creates a new Cli application object 19 | func NewCli(name, description, version string) *Cli { 20 | result := &Cli{ 21 | version: version, 22 | bannerFunction: defaultBannerFunction, 23 | } 24 | result.rootCommand = NewCommand(name, description) 25 | result.rootCommand.setApp(result) 26 | result.rootCommand.setParentCommandPath("") 27 | return result 28 | } 29 | -------------------------------------------------------------------------------- /clir_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leaanthony/clir/a8d9462bce754819cbd5a5596f22d68b3f318fb6/clir_logo.png -------------------------------------------------------------------------------- /clir_test.go: -------------------------------------------------------------------------------- 1 | package clir 2 | 3 | import "testing" 4 | 5 | func TestClir(t *testing.T) { 6 | var mockCli *Cli 7 | t.Run("Run NewCli()", func(t *testing.T) { 8 | mockCli = NewCli("name", "description", "version") 9 | t.Log(mockCli) 10 | }) 11 | 12 | t.Run("Run defaultBannerFunction()", func(t *testing.T) { 13 | err := defaultBannerFunction(mockCli) 14 | t.Log(err) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /command.go: -------------------------------------------------------------------------------- 1 | package clir 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "reflect" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // Command represents a command that may be run by the user 14 | type Command struct { 15 | name string 16 | commandPath string 17 | shortdescription string 18 | longdescription string 19 | subCommands []*Command 20 | subCommandsMap map[string]*Command 21 | longestSubcommand int 22 | actionCallback Action 23 | app *Cli 24 | flags *flag.FlagSet 25 | flagCount int 26 | helpFlag bool 27 | hidden bool 28 | positionalArgsMap map[string]reflect.Value 29 | sliceSeparator map[string]string 30 | } 31 | 32 | // NewCommand creates a new Command 33 | // func NewCommand(name string, description string, app *Cli, parentCommandPath string) *Command { 34 | func NewCommand(name string, description string) *Command { 35 | result := &Command{ 36 | name: name, 37 | shortdescription: description, 38 | subCommandsMap: make(map[string]*Command), 39 | hidden: false, 40 | positionalArgsMap: make(map[string]reflect.Value), 41 | sliceSeparator: make(map[string]string), 42 | } 43 | 44 | return result 45 | } 46 | 47 | func (c *Command) setParentCommandPath(parentCommandPath string) { 48 | // Set up command path 49 | if parentCommandPath != "" { 50 | c.commandPath += parentCommandPath + " " 51 | } 52 | c.commandPath += c.name 53 | 54 | // Set up flag set 55 | c.flags = flag.NewFlagSet(c.commandPath, flag.ContinueOnError) 56 | c.BoolFlag("help", "Get help on the '"+strings.ToLower(c.commandPath)+"' command.", &c.helpFlag) 57 | 58 | // result.Flags.Usage = result.PrintHelp 59 | 60 | } 61 | 62 | func (c *Command) inheritFlags(inheritFlags *flag.FlagSet) { 63 | // inherit flags 64 | inheritFlags.VisitAll(func(f *flag.Flag) { 65 | if f.Name != "help" { 66 | c.flags.Var(f.Value, f.Name, f.Usage) 67 | } 68 | }) 69 | } 70 | 71 | func (c *Command) setApp(app *Cli) { 72 | c.app = app 73 | } 74 | 75 | // parseFlags parses the given flags 76 | func (c *Command) parseFlags(args []string) error { 77 | // Parse flags 78 | tmp := os.Stderr 79 | os.Stderr = nil 80 | defer func() { 81 | os.Stderr = tmp 82 | }() 83 | 84 | // Credit: https://stackoverflow.com/a/74146375 85 | var positionalArgs []string 86 | for { 87 | if err := c.flags.Parse(args); err != nil { 88 | return err 89 | } 90 | // Consume all the flags that were parsed as flags. 91 | args = args[len(args)-c.flags.NArg():] 92 | if len(args) == 0 { 93 | break 94 | } 95 | // There's at least one flag remaining and it must be a positional arg since 96 | // we consumed all args that were parsed as flags. Consume just the first 97 | // one, and retry parsing, since subsequent args may be flags. 98 | positionalArgs = append(positionalArgs, args[0]) 99 | args = args[1:] 100 | } 101 | 102 | // Parse just the positional args so that flagset.Args()/flagset.NArgs() 103 | // return the expected value. 104 | // Note: This should never return an error. 105 | err := c.flags.Parse(positionalArgs) 106 | if err != nil { 107 | return err 108 | } 109 | 110 | if len(positionalArgs) > 0 { 111 | return c.parsePositionalArgs(positionalArgs) 112 | } 113 | return nil 114 | } 115 | 116 | // Run - Runs the Command with the given arguments 117 | func (c *Command) run(args []string) error { 118 | 119 | // If we have arguments, process them 120 | if len(args) > 0 { 121 | // Check for subcommand 122 | subcommand := c.subCommandsMap[args[0]] 123 | if subcommand != nil { 124 | return subcommand.run(args[1:]) 125 | } 126 | 127 | // Parse flags 128 | err := c.parseFlags(args) 129 | if err != nil { 130 | if c.app.errorHandler != nil { 131 | return c.app.errorHandler(c.commandPath, err) 132 | } 133 | return fmt.Errorf("Error: %s\nSee '%s --help' for usage", err, c.commandPath) 134 | } 135 | 136 | // Help takes precedence 137 | if c.helpFlag { 138 | c.PrintHelp() 139 | return nil 140 | } 141 | } 142 | 143 | // Do we have an action? 144 | if c.actionCallback != nil { 145 | return c.actionCallback() 146 | } 147 | 148 | // If we haven't specified a subcommand 149 | // check for an app level default command 150 | if c.app.defaultCommand != nil { 151 | // Prevent recursion! 152 | if c.app.defaultCommand != c { 153 | // only run default command if no args passed 154 | if len(args) == 0 { 155 | return c.app.defaultCommand.run(args) 156 | } 157 | } 158 | } 159 | 160 | // Nothing left we can do 161 | c.PrintHelp() 162 | 163 | return nil 164 | } 165 | 166 | // Action - Define an action from this command 167 | func (c *Command) Action(callback Action) *Command { 168 | c.actionCallback = callback 169 | return c 170 | } 171 | 172 | // PrintHelp - Output the help text for this command 173 | func (c *Command) PrintHelp() { 174 | c.app.PrintBanner() 175 | 176 | commandTitle := c.commandPath 177 | if c.shortdescription != "" { 178 | commandTitle += " - " + c.shortdescription 179 | } 180 | // Ignore root command 181 | if c.commandPath != c.name { 182 | fmt.Println(commandTitle) 183 | } 184 | if c.longdescription != "" { 185 | fmt.Println(c.longdescription + "\n") 186 | } 187 | if len(c.subCommands) > 0 { 188 | fmt.Println("Available commands:") 189 | fmt.Println("") 190 | for _, subcommand := range c.subCommands { 191 | if subcommand.isHidden() { 192 | continue 193 | } 194 | spacer := strings.Repeat(" ", 3+c.longestSubcommand-len(subcommand.name)) 195 | isDefault := "" 196 | if subcommand.isDefaultCommand() { 197 | isDefault = "[default]" 198 | } 199 | fmt.Printf(" %s%s%s %s\n", subcommand.name, spacer, subcommand.shortdescription, isDefault) 200 | } 201 | fmt.Println("") 202 | } 203 | if c.flagCount > 0 { 204 | fmt.Println("Flags:") 205 | fmt.Println() 206 | c.flags.SetOutput(os.Stdout) 207 | c.flags.PrintDefaults() 208 | c.flags.SetOutput(os.Stderr) 209 | 210 | } 211 | fmt.Println() 212 | } 213 | 214 | // isDefaultCommand returns true if called on the default command 215 | func (c *Command) isDefaultCommand() bool { 216 | return c.app.defaultCommand == c 217 | } 218 | 219 | // isHidden returns true if the command is a hidden command 220 | func (c *Command) isHidden() bool { 221 | return c.hidden 222 | } 223 | 224 | // Hidden hides the command from the Help system 225 | func (c *Command) Hidden() { 226 | c.hidden = true 227 | } 228 | 229 | // NewSubCommand - Creates a new subcommand 230 | func (c *Command) NewSubCommand(name, description string) *Command { 231 | result := NewCommand(name, description) 232 | c.AddCommand(result) 233 | return result 234 | } 235 | 236 | // AddCommand - Adds a subcommand 237 | func (c *Command) AddCommand(command *Command) { 238 | command.setApp(c.app) 239 | command.setParentCommandPath(c.commandPath) 240 | name := command.name 241 | c.subCommands = append(c.subCommands, command) 242 | c.subCommandsMap[name] = command 243 | if len(name) > c.longestSubcommand { 244 | c.longestSubcommand = len(name) 245 | } 246 | } 247 | 248 | // NewSubCommandInheritFlags - Creates a new subcommand, inherits flags from command 249 | func (c *Command) NewSubCommandInheritFlags(name, description string) *Command { 250 | result := c.NewSubCommand(name, description) 251 | result.inheritFlags(c.flags) 252 | return result 253 | } 254 | 255 | func (c *Command) AddFlags(optionStruct interface{}) *Command { 256 | // use reflection to determine if this is a pointer to a struct 257 | // if not, panic 258 | 259 | t := reflect.TypeOf(optionStruct) 260 | 261 | // Check for a pointer to a struct 262 | if t.Kind() != reflect.Ptr { 263 | panic("AddFlags() requires a pointer to a struct") 264 | } 265 | if t.Elem().Kind() != reflect.Struct { 266 | panic("AddFlags() requires a pointer to a struct") 267 | } 268 | 269 | // Iterate through the fields of the struct reading the struct tags 270 | // and adding the flags 271 | v := reflect.ValueOf(optionStruct).Elem() 272 | for i := 0; i < v.NumField(); i++ { 273 | field := v.Field(i) 274 | fieldType := t.Elem().Field(i) 275 | if !fieldType.IsExported() { 276 | continue 277 | } 278 | // If this is an embedded struct, recurse 279 | if fieldType.Type.Kind() == reflect.Struct { 280 | c.AddFlags(field.Addr().Interface()) 281 | continue 282 | } 283 | 284 | tag := t.Elem().Field(i).Tag 285 | name := tag.Get("name") 286 | description := tag.Get("description") 287 | defaultValue := tag.Get("default") 288 | pos := tag.Get("pos") 289 | sep := tag.Get("sep") 290 | c.positionalArgsMap[pos] = field 291 | if sep != "" { 292 | c.sliceSeparator[pos] = sep 293 | } 294 | if name == "" { 295 | name = strings.ToLower(t.Elem().Field(i).Name) 296 | } 297 | switch field.Kind() { 298 | case reflect.Bool: 299 | var defaultValueBool bool 300 | if defaultValue != "" { 301 | var err error 302 | defaultValueBool, err = strconv.ParseBool(defaultValue) 303 | if err != nil { 304 | panic("Invalid default value for bool flag") 305 | } 306 | } 307 | field.SetBool(defaultValueBool) 308 | c.BoolFlag(name, description, field.Addr().Interface().(*bool)) 309 | case reflect.String: 310 | if defaultValue != "" { 311 | // set value of field to default value 312 | field.SetString(defaultValue) 313 | } 314 | c.StringFlag(name, description, field.Addr().Interface().(*string)) 315 | case reflect.Int: 316 | if defaultValue != "" { 317 | // set value of field to default value 318 | value, err := strconv.Atoi(defaultValue) 319 | if err != nil { 320 | panic("Invalid default value for int flag") 321 | } 322 | field.SetInt(int64(value)) 323 | } 324 | c.IntFlag(name, description, field.Addr().Interface().(*int)) 325 | case reflect.Int8: 326 | if defaultValue != "" { 327 | // set value of field to default value 328 | value, err := strconv.Atoi(defaultValue) 329 | if err != nil { 330 | panic("Invalid default value for int8 flag") 331 | } 332 | field.SetInt(int64(value)) 333 | } 334 | c.Int8Flag(name, description, field.Addr().Interface().(*int8)) 335 | case reflect.Int16: 336 | if defaultValue != "" { 337 | // set value of field to default value 338 | value, err := strconv.Atoi(defaultValue) 339 | if err != nil { 340 | panic("Invalid default value for int16 flag") 341 | } 342 | field.SetInt(int64(value)) 343 | } 344 | c.Int16Flag(name, description, field.Addr().Interface().(*int16)) 345 | case reflect.Int32: 346 | if defaultValue != "" { 347 | // set value of field to default value 348 | value, err := strconv.Atoi(defaultValue) 349 | if err != nil { 350 | panic("Invalid default value for int32 flag") 351 | } 352 | field.SetInt(int64(value)) 353 | } 354 | c.Int32Flag(name, description, field.Addr().Interface().(*int32)) 355 | case reflect.Int64: 356 | if defaultValue != "" { 357 | // set value of field to default value 358 | value, err := strconv.Atoi(defaultValue) 359 | if err != nil { 360 | panic("Invalid default value for int64 flag") 361 | } 362 | field.SetInt(int64(value)) 363 | } 364 | c.Int64Flag(name, description, field.Addr().Interface().(*int64)) 365 | case reflect.Uint: 366 | if defaultValue != "" { 367 | // set value of field to default value 368 | value, err := strconv.Atoi(defaultValue) 369 | if err != nil { 370 | panic("Invalid default value for uint flag") 371 | } 372 | field.SetUint(uint64(value)) 373 | } 374 | c.UintFlag(name, description, field.Addr().Interface().(*uint)) 375 | case reflect.Uint8: 376 | if defaultValue != "" { 377 | // set value of field to default value 378 | value, err := strconv.Atoi(defaultValue) 379 | if err != nil { 380 | panic("Invalid default value for uint8 flag") 381 | } 382 | field.SetUint(uint64(value)) 383 | } 384 | c.Uint8Flag(name, description, field.Addr().Interface().(*uint8)) 385 | case reflect.Uint16: 386 | if defaultValue != "" { 387 | // set value of field to default value 388 | value, err := strconv.Atoi(defaultValue) 389 | if err != nil { 390 | panic("Invalid default value for uint16 flag") 391 | } 392 | field.SetUint(uint64(value)) 393 | } 394 | c.Uint16Flag(name, description, field.Addr().Interface().(*uint16)) 395 | case reflect.Uint32: 396 | if defaultValue != "" { 397 | // set value of field to default value 398 | value, err := strconv.Atoi(defaultValue) 399 | if err != nil { 400 | panic("Invalid default value for uint32 flag") 401 | } 402 | field.SetUint(uint64(value)) 403 | } 404 | c.Uint32Flag(name, description, field.Addr().Interface().(*uint32)) 405 | case reflect.Uint64: 406 | if defaultValue != "" { 407 | // set value of field to default value 408 | value, err := strconv.Atoi(defaultValue) 409 | if err != nil { 410 | panic("Invalid default value for uint64 flag") 411 | } 412 | field.SetUint(uint64(value)) 413 | } 414 | c.UInt64Flag(name, description, field.Addr().Interface().(*uint64)) 415 | case reflect.Float32: 416 | if defaultValue != "" { 417 | // set value of field to default value 418 | value, err := strconv.ParseFloat(defaultValue, 64) 419 | if err != nil { 420 | panic("Invalid default value for float32 flag") 421 | } 422 | field.SetFloat(value) 423 | } 424 | c.Float32Flag(name, description, field.Addr().Interface().(*float32)) 425 | case reflect.Float64: 426 | if defaultValue != "" { 427 | // set value of field to default value 428 | value, err := strconv.ParseFloat(defaultValue, 64) 429 | if err != nil { 430 | panic("Invalid default value for float64 flag") 431 | } 432 | field.SetFloat(value) 433 | } 434 | c.Float64Flag(name, description, field.Addr().Interface().(*float64)) 435 | case reflect.Slice: 436 | c.addSliceField(field, defaultValue, sep) 437 | c.addSliceFlags(name, description, field) 438 | default: 439 | if pos != "" { 440 | println("WARNING: Unsupported type for flag: ", fieldType.Type.Kind(), name) 441 | } 442 | } 443 | } 444 | 445 | return c 446 | } 447 | 448 | func (c *Command) addSliceFlags(name, description string, field reflect.Value) *Command { 449 | if field.Kind() != reflect.Slice { 450 | panic("addSliceFlags() requires a pointer to a slice") 451 | } 452 | t := reflect.TypeOf(field.Addr().Interface()) 453 | if t.Kind() != reflect.Ptr { 454 | panic("addSliceFlags() requires a pointer to a slice") 455 | } 456 | if t.Elem().Kind() != reflect.Slice { 457 | panic("addSliceFlags() requires a pointer to a slice") 458 | } 459 | switch t.Elem().Elem().Kind() { 460 | case reflect.Bool: 461 | c.BoolsFlag(name, description, field.Addr().Interface().(*[]bool)) 462 | case reflect.String: 463 | c.StringsFlag(name, description, field.Addr().Interface().(*[]string)) 464 | case reflect.Int: 465 | c.IntsFlag(name, description, field.Addr().Interface().(*[]int)) 466 | case reflect.Int8: 467 | c.Int8sFlag(name, description, field.Addr().Interface().(*[]int8)) 468 | case reflect.Int16: 469 | c.Int16sFlag(name, description, field.Addr().Interface().(*[]int16)) 470 | case reflect.Int32: 471 | c.Int32sFlag(name, description, field.Addr().Interface().(*[]int32)) 472 | case reflect.Int64: 473 | c.Int64sFlag(name, description, field.Addr().Interface().(*[]int64)) 474 | case reflect.Uint: 475 | c.UintsFlag(name, description, field.Addr().Interface().(*[]uint)) 476 | case reflect.Uint8: 477 | c.Uint8sFlag(name, description, field.Addr().Interface().(*[]uint8)) 478 | case reflect.Uint16: 479 | c.Uint16sFlag(name, description, field.Addr().Interface().(*[]uint16)) 480 | case reflect.Uint32: 481 | c.Uint32sFlag(name, description, field.Addr().Interface().(*[]uint32)) 482 | case reflect.Uint64: 483 | c.Uint64sFlag(name, description, field.Addr().Interface().(*[]uint64)) 484 | case reflect.Float32: 485 | c.Float32sFlag(name, description, field.Addr().Interface().(*[]float32)) 486 | case reflect.Float64: 487 | c.Float64sFlag(name, description, field.Addr().Interface().(*[]float64)) 488 | default: 489 | panic(fmt.Sprintf("addSliceFlags() not supported slice type %s", t.Elem().Elem().Kind().String())) 490 | } 491 | return c 492 | } 493 | 494 | func (c *Command) addSliceField(field reflect.Value, defaultValue, separator string) *Command { 495 | if defaultValue == "" { 496 | return c 497 | } 498 | if field.Kind() != reflect.Slice { 499 | panic("addSliceField() requires a pointer to a slice") 500 | } 501 | t := reflect.TypeOf(field.Addr().Interface()) 502 | if t.Kind() != reflect.Ptr { 503 | panic("addSliceField() requires a pointer to a slice") 504 | } 505 | if t.Elem().Kind() != reflect.Slice { 506 | panic("addSliceField() requires a pointer to a slice") 507 | } 508 | defaultSlice := []string{defaultValue} 509 | if separator != "" { 510 | defaultSlice = strings.Split(defaultValue, separator) 511 | } 512 | switch t.Elem().Elem().Kind() { 513 | case reflect.Bool: 514 | defaultValues := make([]bool, 0, len(defaultSlice)) 515 | for _, value := range defaultSlice { 516 | val, err := strconv.ParseBool(value) 517 | if err != nil { 518 | panic("Invalid default value for bool flag") 519 | } 520 | defaultValues = append(defaultValues, val) 521 | } 522 | field.Set(reflect.ValueOf(defaultValues)) 523 | case reflect.String: 524 | field.Set(reflect.ValueOf(defaultSlice)) 525 | case reflect.Int: 526 | defaultValues := make([]int, 0, len(defaultSlice)) 527 | for _, value := range defaultSlice { 528 | val, err := strconv.Atoi(value) 529 | if err != nil { 530 | panic("Invalid default value for int flag") 531 | } 532 | defaultValues = append(defaultValues, val) 533 | } 534 | field.Set(reflect.ValueOf(defaultValues)) 535 | case reflect.Int8: 536 | defaultValues := make([]int8, 0, len(defaultSlice)) 537 | for _, value := range defaultSlice { 538 | val, err := strconv.Atoi(value) 539 | if err != nil { 540 | panic("Invalid default value for int8 flag") 541 | } 542 | defaultValues = append(defaultValues, int8(val)) 543 | } 544 | field.Set(reflect.ValueOf(defaultValues)) 545 | case reflect.Int16: 546 | defaultValues := make([]int16, 0, len(defaultSlice)) 547 | for _, value := range defaultSlice { 548 | val, err := strconv.Atoi(value) 549 | if err != nil { 550 | panic("Invalid default value for int16 flag") 551 | } 552 | defaultValues = append(defaultValues, int16(val)) 553 | } 554 | field.Set(reflect.ValueOf(defaultValues)) 555 | case reflect.Int32: 556 | defaultValues := make([]int32, 0, len(defaultSlice)) 557 | for _, value := range defaultSlice { 558 | val, err := strconv.ParseInt(value, 10, 32) 559 | if err != nil { 560 | panic("Invalid default value for int32 flag") 561 | } 562 | defaultValues = append(defaultValues, int32(val)) 563 | } 564 | field.Set(reflect.ValueOf(defaultValues)) 565 | case reflect.Int64: 566 | defaultValues := make([]int64, 0, len(defaultSlice)) 567 | for _, value := range defaultSlice { 568 | val, err := strconv.ParseInt(value, 10, 64) 569 | if err != nil { 570 | panic("Invalid default value for int64 flag") 571 | } 572 | defaultValues = append(defaultValues, val) 573 | } 574 | field.Set(reflect.ValueOf(defaultValues)) 575 | case reflect.Uint: 576 | defaultValues := make([]uint, 0, len(defaultSlice)) 577 | for _, value := range defaultSlice { 578 | val, err := strconv.Atoi(value) 579 | if err != nil { 580 | panic("Invalid default value for uint flag") 581 | } 582 | defaultValues = append(defaultValues, uint(val)) 583 | } 584 | field.Set(reflect.ValueOf(defaultValues)) 585 | case reflect.Uint8: 586 | defaultValues := make([]uint8, 0, len(defaultSlice)) 587 | for _, value := range defaultSlice { 588 | val, err := strconv.Atoi(value) 589 | if err != nil { 590 | panic("Invalid default value for uint8 flag") 591 | } 592 | defaultValues = append(defaultValues, uint8(val)) 593 | } 594 | field.Set(reflect.ValueOf(defaultValues)) 595 | case reflect.Uint16: 596 | defaultValues := make([]uint16, 0, len(defaultSlice)) 597 | for _, value := range defaultSlice { 598 | val, err := strconv.Atoi(value) 599 | if err != nil { 600 | panic("Invalid default value for uint16 flag") 601 | } 602 | defaultValues = append(defaultValues, uint16(val)) 603 | } 604 | field.Set(reflect.ValueOf(defaultValues)) 605 | case reflect.Uint32: 606 | defaultValues := make([]uint32, 0, len(defaultSlice)) 607 | for _, value := range defaultSlice { 608 | val, err := strconv.Atoi(value) 609 | if err != nil { 610 | panic("Invalid default value for uint32 flag") 611 | } 612 | defaultValues = append(defaultValues, uint32(val)) 613 | } 614 | field.Set(reflect.ValueOf(defaultValues)) 615 | case reflect.Uint64: 616 | defaultValues := make([]uint64, 0, len(defaultSlice)) 617 | for _, value := range defaultSlice { 618 | val, err := strconv.Atoi(value) 619 | if err != nil { 620 | panic("Invalid default value for uint64 flag") 621 | } 622 | defaultValues = append(defaultValues, uint64(val)) 623 | } 624 | field.Set(reflect.ValueOf(defaultValues)) 625 | case reflect.Float32: 626 | defaultValues := make([]float32, 0, len(defaultSlice)) 627 | for _, value := range defaultSlice { 628 | val, err := strconv.Atoi(value) 629 | if err != nil { 630 | panic("Invalid default value for float32 flag") 631 | } 632 | defaultValues = append(defaultValues, float32(val)) 633 | } 634 | field.Set(reflect.ValueOf(defaultValues)) 635 | case reflect.Float64: 636 | defaultValues := make([]float64, 0, len(defaultSlice)) 637 | for _, value := range defaultSlice { 638 | val, err := strconv.Atoi(value) 639 | if err != nil { 640 | panic("Invalid default value for float64 flag") 641 | } 642 | defaultValues = append(defaultValues, float64(val)) 643 | } 644 | field.Set(reflect.ValueOf(defaultValues)) 645 | default: 646 | panic(fmt.Sprintf("addSliceField() not supported slice type %s", t.Elem().Elem().Kind().String())) 647 | } 648 | return c 649 | } 650 | 651 | // BoolFlag - Adds a boolean flag to the command 652 | func (c *Command) BoolFlag(name, description string, variable *bool) *Command { 653 | c.flags.BoolVar(variable, name, *variable, description) 654 | c.flagCount++ 655 | return c 656 | } 657 | 658 | // BoolsFlag - Adds a booleans flag to the command 659 | func (c *Command) BoolsFlag(name, description string, variable *[]bool) *Command { 660 | c.flags.Var(newBoolsValue(*variable, variable), name, description) 661 | c.flagCount++ 662 | return c 663 | } 664 | 665 | // StringFlag - Adds a string flag to the command 666 | func (c *Command) StringFlag(name, description string, variable *string) *Command { 667 | c.flags.StringVar(variable, name, *variable, description) 668 | c.flagCount++ 669 | return c 670 | } 671 | 672 | // StringsFlag - Adds a strings flag to the command 673 | func (c *Command) StringsFlag(name, description string, variable *[]string) *Command { 674 | c.flags.Var(newStringsValue(*variable, variable), name, description) 675 | c.flagCount++ 676 | return c 677 | } 678 | 679 | // IntFlag - Adds an int flag to the command 680 | func (c *Command) IntFlag(name, description string, variable *int) *Command { 681 | c.flags.IntVar(variable, name, *variable, description) 682 | c.flagCount++ 683 | return c 684 | } 685 | 686 | // IntsFlag - Adds an ints flag to the command 687 | func (c *Command) IntsFlag(name, description string, variable *[]int) *Command { 688 | c.flags.Var(newIntsValue(*variable, variable), name, description) 689 | c.flagCount++ 690 | return c 691 | } 692 | 693 | // Int8Flag - Adds an int8 flag to the command 694 | func (c *Command) Int8Flag(name, description string, variable *int8) *Command { 695 | c.flags.Var(newInt8Value(*variable, variable), name, description) 696 | c.flagCount++ 697 | return c 698 | } 699 | 700 | // Int8sFlag - Adds an int8 s flag to the command 701 | func (c *Command) Int8sFlag(name, description string, variable *[]int8) *Command { 702 | c.flags.Var(newInt8sValue(*variable, variable), name, description) 703 | c.flagCount++ 704 | return c 705 | } 706 | 707 | // Int16Flag - Adds an int16 flag to the command 708 | func (c *Command) Int16Flag(name, description string, variable *int16) *Command { 709 | c.flags.Var(newInt16Value(*variable, variable), name, description) 710 | c.flagCount++ 711 | return c 712 | } 713 | 714 | // Int16sFlag - Adds an int16s flag to the command 715 | func (c *Command) Int16sFlag(name, description string, variable *[]int16) *Command { 716 | c.flags.Var(newInt16sValue(*variable, variable), name, description) 717 | c.flagCount++ 718 | return c 719 | } 720 | 721 | // Int32Flag - Adds an int32 flag to the command 722 | func (c *Command) Int32Flag(name, description string, variable *int32) *Command { 723 | c.flags.Var(newInt32Value(*variable, variable), name, description) 724 | c.flagCount++ 725 | return c 726 | } 727 | 728 | // Int32sFlag - Adds an int32s flag to the command 729 | func (c *Command) Int32sFlag(name, description string, variable *[]int32) *Command { 730 | c.flags.Var(newInt32sValue(*variable, variable), name, description) 731 | c.flagCount++ 732 | return c 733 | } 734 | 735 | // Int64Flag - Adds an int64 flag to the command 736 | func (c *Command) Int64Flag(name, description string, variable *int64) *Command { 737 | c.flags.Int64Var(variable, name, *variable, description) 738 | c.flagCount++ 739 | return c 740 | } 741 | 742 | // Int64sFlag - Adds an int64s flag to the command 743 | func (c *Command) Int64sFlag(name, description string, variable *[]int64) *Command { 744 | c.flags.Var(newInt64sValue(*variable, variable), name, description) 745 | c.flagCount++ 746 | return c 747 | } 748 | 749 | // UintFlag - Adds an uint flag to the command 750 | func (c *Command) UintFlag(name, description string, variable *uint) *Command { 751 | c.flags.UintVar(variable, name, *variable, description) 752 | c.flagCount++ 753 | return c 754 | } 755 | 756 | // UintsFlag - Adds an uints flag to the command 757 | func (c *Command) UintsFlag(name, description string, variable *[]uint) *Command { 758 | c.flags.Var(newUintsValue(*variable, variable), name, description) 759 | c.flagCount++ 760 | return c 761 | } 762 | 763 | // Uint8Flag - Adds an uint8 flag to the command 764 | func (c *Command) Uint8Flag(name, description string, variable *uint8) *Command { 765 | c.flags.Var(newUint8Value(*variable, variable), name, description) 766 | c.flagCount++ 767 | return c 768 | } 769 | 770 | // Uint8sFlag - Adds an uint8 s flag to the command 771 | func (c *Command) Uint8sFlag(name, description string, variable *[]uint8) *Command { 772 | c.flags.Var(newUint8sValue(*variable, variable), name, description) 773 | c.flagCount++ 774 | return c 775 | } 776 | 777 | // Uint16Flag - Adds an uint16 flag to the command 778 | func (c *Command) Uint16Flag(name, description string, variable *uint16) *Command { 779 | c.flags.Var(newUint16Value(*variable, variable), name, description) 780 | c.flagCount++ 781 | return c 782 | } 783 | 784 | // Uint16sFlag - Adds an uint16s flag to the command 785 | func (c *Command) Uint16sFlag(name, description string, variable *[]uint16) *Command { 786 | c.flags.Var(newUint16sValue(*variable, variable), name, description) 787 | c.flagCount++ 788 | return c 789 | } 790 | 791 | // Uint32Flag - Adds an uint32 flag to the command 792 | func (c *Command) Uint32Flag(name, description string, variable *uint32) *Command { 793 | c.flags.Var(newUint32Value(*variable, variable), name, description) 794 | c.flagCount++ 795 | return c 796 | } 797 | 798 | // Uint32sFlag - Adds an uint32s flag to the command 799 | func (c *Command) Uint32sFlag(name, description string, variable *[]uint32) *Command { 800 | c.flags.Var(newUint32sValue(*variable, variable), name, description) 801 | c.flagCount++ 802 | return c 803 | } 804 | 805 | // UInt64Flag - Adds an uint64 flag to the command 806 | func (c *Command) UInt64Flag(name, description string, variable *uint64) *Command { 807 | c.flags.Uint64Var(variable, name, *variable, description) 808 | c.flagCount++ 809 | return c 810 | } 811 | 812 | // Uint64sFlag - Adds an uint64s flag to the command 813 | func (c *Command) Uint64sFlag(name, description string, variable *[]uint64) *Command { 814 | c.flags.Var(newUint64sValue(*variable, variable), name, description) 815 | c.flagCount++ 816 | return c 817 | } 818 | 819 | // Float64Flag - Adds a float64 flag to the command 820 | func (c *Command) Float64Flag(name, description string, variable *float64) *Command { 821 | c.flags.Float64Var(variable, name, *variable, description) 822 | c.flagCount++ 823 | return c 824 | } 825 | 826 | // Float32Flag - Adds a float32 flag to the command 827 | func (c *Command) Float32Flag(name, description string, variable *float32) *Command { 828 | c.flags.Var(newFloat32Value(*variable, variable), name, description) 829 | c.flagCount++ 830 | return c 831 | } 832 | 833 | // Float32sFlag - Adds a float32s flag to the command 834 | func (c *Command) Float32sFlag(name, description string, variable *[]float32) *Command { 835 | c.flags.Var(newFloat32sValue(*variable, variable), name, description) 836 | c.flagCount++ 837 | return c 838 | } 839 | 840 | // Float64sFlag - Adds a float64s flag to the command 841 | func (c *Command) Float64sFlag(name, description string, variable *[]float64) *Command { 842 | c.flags.Var(newFloat64sValue(*variable, variable), name, description) 843 | c.flagCount++ 844 | return c 845 | } 846 | 847 | type boolsFlagVar []bool 848 | 849 | func (f *boolsFlagVar) String() string { return fmt.Sprint([]bool(*f)) } 850 | 851 | func (f *boolsFlagVar) Set(value string) error { 852 | if value == "" { 853 | *f = append(*f, false) 854 | return nil 855 | } 856 | b, err := strconv.ParseBool(value) 857 | if err != nil { 858 | return err 859 | } 860 | *f = append(*f, b) 861 | return nil 862 | } 863 | 864 | func (f *boolsFlagVar) IsBoolFlag() bool { 865 | return true 866 | } 867 | 868 | func newBoolsValue(val []bool, p *[]bool) *boolsFlagVar { 869 | *p = val 870 | return (*boolsFlagVar)(p) 871 | } 872 | 873 | type stringsFlagVar []string 874 | 875 | func (f *stringsFlagVar) String() string { return fmt.Sprint([]string(*f)) } 876 | 877 | func (f *stringsFlagVar) Set(value string) error { 878 | *f = append(*f, value) 879 | return nil 880 | } 881 | 882 | func newStringsValue(val []string, p *[]string) *stringsFlagVar { 883 | *p = val 884 | return (*stringsFlagVar)(p) 885 | } 886 | 887 | type intsFlagVar []int 888 | 889 | func (f *intsFlagVar) String() string { return fmt.Sprint([]int(*f)) } 890 | 891 | func (f *intsFlagVar) Set(value string) error { 892 | i, err := strconv.Atoi(value) 893 | if err != nil { 894 | return err 895 | } 896 | *f = append(*f, i) 897 | return nil 898 | } 899 | 900 | func newIntsValue(val []int, p *[]int) *intsFlagVar { 901 | *p = val 902 | return (*intsFlagVar)(p) 903 | } 904 | 905 | type int8Value int8 906 | 907 | func newInt8Value(val int8, p *int8) *int8Value { 908 | *p = val 909 | return (*int8Value)(p) 910 | } 911 | 912 | func (f *int8Value) Set(value string) error { 913 | i, err := strconv.Atoi(value) 914 | if err != nil { 915 | return err 916 | } 917 | *f = int8Value(i) 918 | return nil 919 | } 920 | 921 | func (f *int8Value) String() string { return fmt.Sprint(int8(*f)) } 922 | 923 | type int8sFlagVar []int8 924 | 925 | func (f *int8sFlagVar) String() string { return fmt.Sprint([]int8(*f)) } 926 | 927 | func (f *int8sFlagVar) Set(value string) error { 928 | i, err := strconv.Atoi(value) 929 | if err != nil { 930 | return err 931 | } 932 | *f = append(*f, int8(i)) 933 | return nil 934 | } 935 | 936 | func newInt8sValue(val []int8, p *[]int8) *int8sFlagVar { 937 | *p = val 938 | return (*int8sFlagVar)(p) 939 | } 940 | 941 | type int16Value int16 942 | 943 | func newInt16Value(val int16, p *int16) *int16Value { 944 | *p = val 945 | return (*int16Value)(p) 946 | } 947 | 948 | func (f *int16Value) Set(value string) error { 949 | i, err := strconv.Atoi(value) 950 | if err != nil { 951 | return err 952 | } 953 | *f = int16Value(i) 954 | return nil 955 | } 956 | 957 | func (f *int16Value) String() string { return fmt.Sprint(int16(*f)) } 958 | 959 | type int16sFlagVar []int16 960 | 961 | func (f *int16sFlagVar) String() string { return fmt.Sprint([]int16(*f)) } 962 | 963 | func (f *int16sFlagVar) Set(value string) error { 964 | i, err := strconv.Atoi(value) 965 | if err != nil { 966 | return err 967 | } 968 | *f = append(*f, int16(i)) 969 | return nil 970 | } 971 | 972 | func newInt16sValue(val []int16, p *[]int16) *int16sFlagVar { 973 | *p = val 974 | return (*int16sFlagVar)(p) 975 | } 976 | 977 | type int32Value int32 978 | 979 | func newInt32Value(val int32, p *int32) *int32Value { 980 | *p = val 981 | return (*int32Value)(p) 982 | } 983 | 984 | func (f *int32Value) Set(value string) error { 985 | i, err := strconv.Atoi(value) 986 | if err != nil { 987 | return err 988 | } 989 | *f = int32Value(i) 990 | return nil 991 | } 992 | 993 | func (f *int32Value) String() string { return fmt.Sprint(int32(*f)) } 994 | 995 | type int32sFlagVar []int32 996 | 997 | func (f *int32sFlagVar) String() string { return fmt.Sprint([]int32(*f)) } 998 | 999 | func (f *int32sFlagVar) Set(value string) error { 1000 | i, err := strconv.Atoi(value) 1001 | if err != nil { 1002 | return err 1003 | } 1004 | *f = append(*f, int32(i)) 1005 | return nil 1006 | } 1007 | 1008 | func newInt32sValue(val []int32, p *[]int32) *int32sFlagVar { 1009 | *p = val 1010 | return (*int32sFlagVar)(p) 1011 | } 1012 | 1013 | type int64sFlagVar []int64 1014 | 1015 | func (f *int64sFlagVar) String() string { return fmt.Sprint([]int64(*f)) } 1016 | 1017 | func (f *int64sFlagVar) Set(value string) error { 1018 | i, err := strconv.ParseInt(value, 10, 64) 1019 | if err != nil { 1020 | return err 1021 | } 1022 | *f = append(*f, i) 1023 | return nil 1024 | } 1025 | 1026 | func newInt64sValue(val []int64, p *[]int64) *int64sFlagVar { 1027 | *p = val 1028 | return (*int64sFlagVar)(p) 1029 | } 1030 | 1031 | type uintsFlagVar []uint 1032 | 1033 | func (f *uintsFlagVar) String() string { 1034 | return fmt.Sprint([]uint(*f)) 1035 | } 1036 | 1037 | func (f *uintsFlagVar) Set(value string) error { 1038 | i, err := strconv.Atoi(value) 1039 | if err != nil { 1040 | return err 1041 | } 1042 | *f = append(*f, uint(i)) 1043 | return nil 1044 | } 1045 | 1046 | func newUintsValue(val []uint, p *[]uint) *uintsFlagVar { 1047 | *p = val 1048 | return (*uintsFlagVar)(p) 1049 | } 1050 | 1051 | type uint8FlagVar uint8 1052 | 1053 | func newUint8Value(val uint8, p *uint8) *uint8FlagVar { 1054 | *p = val 1055 | return (*uint8FlagVar)(p) 1056 | } 1057 | 1058 | func (f *uint8FlagVar) String() string { 1059 | return fmt.Sprint(uint8(*f)) 1060 | } 1061 | 1062 | func (f *uint8FlagVar) Set(value string) error { 1063 | i, err := strconv.Atoi(value) 1064 | if err != nil { 1065 | return err 1066 | } 1067 | *f = uint8FlagVar(i) 1068 | return nil 1069 | } 1070 | 1071 | type uint8sFlagVar []uint8 1072 | 1073 | func (f *uint8sFlagVar) String() string { 1074 | return fmt.Sprint([]uint8(*f)) 1075 | } 1076 | 1077 | func (f *uint8sFlagVar) Set(value string) error { 1078 | i, err := strconv.Atoi(value) 1079 | if err != nil { 1080 | return err 1081 | } 1082 | *f = append(*f, uint8(i)) 1083 | return nil 1084 | } 1085 | 1086 | func newUint8sValue(val []uint8, p *[]uint8) *uint8sFlagVar { 1087 | *p = val 1088 | return (*uint8sFlagVar)(p) 1089 | } 1090 | 1091 | type uint16FlagVar uint16 1092 | 1093 | func newUint16Value(val uint16, p *uint16) *uint16FlagVar { 1094 | *p = val 1095 | return (*uint16FlagVar)(p) 1096 | } 1097 | 1098 | func (f *uint16FlagVar) String() string { 1099 | return fmt.Sprint(uint16(*f)) 1100 | } 1101 | 1102 | func (f *uint16FlagVar) Set(value string) error { 1103 | i, err := strconv.Atoi(value) 1104 | if err != nil { 1105 | return err 1106 | } 1107 | *f = uint16FlagVar(i) 1108 | return nil 1109 | } 1110 | 1111 | type uint16sFlagVar []uint16 1112 | 1113 | func (f *uint16sFlagVar) String() string { 1114 | return fmt.Sprint([]uint16(*f)) 1115 | } 1116 | 1117 | func (f *uint16sFlagVar) Set(value string) error { 1118 | i, err := strconv.Atoi(value) 1119 | if err != nil { 1120 | return err 1121 | } 1122 | *f = append(*f, uint16(i)) 1123 | return nil 1124 | } 1125 | 1126 | func newUint16sValue(val []uint16, p *[]uint16) *uint16sFlagVar { 1127 | *p = val 1128 | return (*uint16sFlagVar)(p) 1129 | } 1130 | 1131 | type uint32FlagVar uint32 1132 | 1133 | func newUint32Value(val uint32, p *uint32) *uint32FlagVar { 1134 | *p = val 1135 | return (*uint32FlagVar)(p) 1136 | } 1137 | 1138 | func (f *uint32FlagVar) String() string { 1139 | return fmt.Sprint(uint32(*f)) 1140 | } 1141 | 1142 | func (f *uint32FlagVar) Set(value string) error { 1143 | i, err := strconv.Atoi(value) 1144 | if err != nil { 1145 | return err 1146 | } 1147 | *f = uint32FlagVar(i) 1148 | return nil 1149 | } 1150 | 1151 | type uint32sFlagVar []uint32 1152 | 1153 | func (f *uint32sFlagVar) String() string { 1154 | return fmt.Sprint([]uint32(*f)) 1155 | } 1156 | 1157 | func (f *uint32sFlagVar) Set(value string) error { 1158 | i, err := strconv.Atoi(value) 1159 | if err != nil { 1160 | return err 1161 | } 1162 | *f = append(*f, uint32(i)) 1163 | return nil 1164 | } 1165 | 1166 | func newUint32sValue(val []uint32, p *[]uint32) *uint32sFlagVar { 1167 | *p = val 1168 | return (*uint32sFlagVar)(p) 1169 | } 1170 | 1171 | type uint64sFlagVar []uint64 1172 | 1173 | func (f *uint64sFlagVar) String() string { return fmt.Sprint([]uint64(*f)) } 1174 | 1175 | func (f *uint64sFlagVar) Set(value string) error { 1176 | i, err := strconv.ParseUint(value, 10, 64) 1177 | if err != nil { 1178 | return err 1179 | } 1180 | *f = append(*f, i) 1181 | return nil 1182 | } 1183 | 1184 | func newUint64sValue(val []uint64, p *[]uint64) *uint64sFlagVar { 1185 | *p = val 1186 | return (*uint64sFlagVar)(p) 1187 | } 1188 | 1189 | type float32sFlagVar []float32 1190 | 1191 | func (f *float32sFlagVar) String() string { return fmt.Sprint([]float32(*f)) } 1192 | 1193 | func (f *float32sFlagVar) Set(value string) error { 1194 | i, err := strconv.ParseFloat(value, 64) 1195 | if err != nil { 1196 | return err 1197 | } 1198 | *f = append(*f, float32(i)) 1199 | return nil 1200 | } 1201 | 1202 | func newFloat32sValue(val []float32, p *[]float32) *float32sFlagVar { 1203 | *p = val 1204 | return (*float32sFlagVar)(p) 1205 | } 1206 | 1207 | type float32FlagVar float32 1208 | 1209 | func (f *float32FlagVar) String() string { return fmt.Sprint(float32(*f)) } 1210 | 1211 | func (f *float32FlagVar) Set(value string) error { 1212 | i, err := strconv.ParseFloat(value, 64) 1213 | if err != nil { 1214 | return err 1215 | } 1216 | *f = float32FlagVar(i) 1217 | return nil 1218 | } 1219 | 1220 | func newFloat32Value(val float32, p *float32) *float32FlagVar { 1221 | *p = val 1222 | return (*float32FlagVar)(p) 1223 | } 1224 | 1225 | type float64sFlagVar []float64 1226 | 1227 | func (f *float64sFlagVar) String() string { return fmt.Sprint([]float64(*f)) } 1228 | 1229 | func (f *float64sFlagVar) Set(value string) error { 1230 | i, err := strconv.ParseFloat(value, 64) 1231 | if err != nil { 1232 | return err 1233 | } 1234 | *f = append(*f, i) 1235 | return nil 1236 | } 1237 | 1238 | func newFloat64sValue(val []float64, p *[]float64) *float64sFlagVar { 1239 | *p = val 1240 | return (*float64sFlagVar)(p) 1241 | } 1242 | 1243 | // LongDescription - Sets the long description for the command 1244 | func (c *Command) LongDescription(longdescription string) *Command { 1245 | c.longdescription = longdescription 1246 | return c 1247 | } 1248 | 1249 | // OtherArgs - Returns the non-flag arguments passed to the subcommand. NOTE: This should only be called within the context of an action. 1250 | func (c *Command) OtherArgs() []string { 1251 | return c.flags.Args() 1252 | } 1253 | 1254 | func (c *Command) NewSubCommandFunction(name string, description string, fn interface{}) *Command { 1255 | result := c.NewSubCommand(name, description) 1256 | // use reflection to determine if this is a function 1257 | // if not, panic 1258 | t := reflect.TypeOf(fn) 1259 | if t.Kind() != reflect.Func { 1260 | panic("NewSubFunction '" + name + "' requires a function with the signature 'func(*struct) error'") 1261 | } 1262 | 1263 | // Check the function has 1 input ant it's a struct pointer 1264 | fnValue := reflect.ValueOf(fn) 1265 | if t.NumIn() != 1 { 1266 | panic("NewSubFunction '" + name + "' requires a function with the signature 'func(*struct) error'") 1267 | } 1268 | // Check the input is a struct pointer 1269 | if t.In(0).Kind() != reflect.Ptr { 1270 | panic("NewSubFunction '" + name + "' requires a function with the signature 'func(*struct) error'") 1271 | } 1272 | if t.In(0).Elem().Kind() != reflect.Struct { 1273 | panic("NewSubFunction '" + name + "' requires a function with the signature 'func(*struct) error'") 1274 | } 1275 | // Check only 1 output and it's an error 1276 | if t.NumOut() != 1 { 1277 | panic("NewSubFunction '" + name + "' requires a function with the signature 'func(*struct) error'") 1278 | } 1279 | if t.Out(0) != reflect.TypeOf((*error)(nil)).Elem() { 1280 | panic("NewSubFunction '" + name + "' requires a function with the signature 'func(*struct) error'") 1281 | } 1282 | flags := reflect.New(t.In(0).Elem()) 1283 | result.Action(func() error { 1284 | result := fnValue.Call([]reflect.Value{flags})[0].Interface() 1285 | if result != nil { 1286 | return result.(error) 1287 | } 1288 | return nil 1289 | }) 1290 | result.AddFlags(flags.Interface()) 1291 | return result 1292 | } 1293 | 1294 | func (c *Command) parsePositionalArgs(args []string) error { 1295 | for index, posArg := range args { 1296 | // Check the map for a field for this arg 1297 | key := strconv.Itoa(index + 1) 1298 | field, ok := c.positionalArgsMap[key] 1299 | if !ok { 1300 | continue 1301 | } 1302 | fieldType := field.Type() 1303 | switch fieldType.Kind() { 1304 | case reflect.Bool: 1305 | // set value of field to true 1306 | field.SetBool(true) 1307 | case reflect.String: 1308 | field.SetString(posArg) 1309 | case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: 1310 | value, err := strconv.ParseInt(posArg, 10, 64) 1311 | if err != nil { 1312 | return err 1313 | } 1314 | field.SetInt(value) 1315 | case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: 1316 | value, err := strconv.ParseUint(posArg, 10, 64) 1317 | if err != nil { 1318 | return err 1319 | } 1320 | field.SetUint(value) 1321 | case reflect.Float64, reflect.Float32: 1322 | value, err := strconv.ParseFloat(posArg, 64) 1323 | if err != nil { 1324 | return err 1325 | } 1326 | field.SetFloat(value) 1327 | case reflect.Slice: 1328 | c.addSliceField(field, posArg, c.sliceSeparator[key]) 1329 | default: 1330 | return errors.New("Unsupported type for positional argument: " + fieldType.Name()) 1331 | } 1332 | } 1333 | return nil 1334 | } 1335 | -------------------------------------------------------------------------------- /command_test.go: -------------------------------------------------------------------------------- 1 | package clir 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestCommand(t *testing.T) { 8 | c := &Command{} 9 | 10 | t.Run("Run NewCommand()", func(t *testing.T) { 11 | c = NewCommand("test", "test description") 12 | }) 13 | 14 | t.Run("Run setParentCommandPath()", func(t *testing.T) { 15 | c.setParentCommandPath("path") 16 | }) 17 | 18 | t.Run("Run setApp()", func(t *testing.T) { 19 | c.setApp(&Cli{}) 20 | }) 21 | 22 | t.Run("Run parseFlags()", func(t *testing.T) { 23 | err := c.parseFlags([]string{"test", "flags"}) 24 | t.Log(err) 25 | }) 26 | 27 | t.Run("Run run()", func(t *testing.T) { 28 | cl := NewCli("test", "description", "0") 29 | cl.rootCommand.run([]string{"test"}) 30 | 31 | cl.rootCommand.subCommandsMap["test"] = &Command{ 32 | name: "subcom", 33 | shortdescription: "short description", 34 | hidden: false, 35 | app: cl, 36 | } 37 | cl.rootCommand.run([]string{"test"}) 38 | 39 | cl.rootCommand.run([]string{"---"}) 40 | 41 | cl.rootCommand.run([]string{"-help"}) 42 | 43 | // cl.rootCommand.actionCallback = func() error { 44 | // println("Hello World!") 45 | // return nil 46 | // } 47 | // cl.rootCommand.run([]string{"test"}) 48 | 49 | cl.rootCommand.app.defaultCommand = &Command{ 50 | name: "subcom", 51 | shortdescription: "short description", 52 | hidden: false, 53 | app: cl, 54 | } 55 | cl.rootCommand.run([]string{"test"}) 56 | }) 57 | 58 | t.Run("Run Action()", func(t *testing.T) { 59 | c.Action(func() error { return nil }) 60 | 61 | }) 62 | 63 | t.Run("Run PrintHelp()", func(t *testing.T) { 64 | cl := NewCli("test", "description", "0") 65 | 66 | // co.shortdescription = "" 67 | cl.PrintHelp() 68 | 69 | cl.rootCommand.shortdescription = "test" 70 | cl.PrintHelp() 71 | 72 | cl.rootCommand.commandPath = "notTest" 73 | cl.PrintHelp() 74 | 75 | cl.rootCommand.longdescription = "" 76 | cl.PrintHelp() 77 | 78 | cl.rootCommand.longdescription = "test" 79 | cl.PrintHelp() 80 | 81 | mockCommand := &Command{ 82 | name: "test", 83 | shortdescription: "short description", 84 | hidden: true, 85 | longestSubcommand: len("test"), 86 | } 87 | cl.rootCommand.subCommands = append(cl.rootCommand.subCommands, mockCommand) 88 | cl.PrintHelp() 89 | 90 | mockCommand = &Command{ 91 | name: "subcom", 92 | shortdescription: "short description", 93 | hidden: false, 94 | app: cl, 95 | } 96 | cl.rootCommand.longestSubcommand = 10 97 | cl.rootCommand.subCommands = append(cl.rootCommand.subCommands, mockCommand) 98 | cl.PrintHelp() 99 | 100 | mockCommand = &Command{ 101 | name: "subcom", 102 | shortdescription: "short description", 103 | hidden: false, 104 | app: cl, 105 | } 106 | cl.rootCommand.longestSubcommand = 10 107 | cl.defaultCommand = mockCommand 108 | cl.rootCommand.subCommands = append(cl.rootCommand.subCommands, mockCommand) 109 | cl.PrintHelp() 110 | 111 | cl.rootCommand.flagCount = 3 112 | cl.PrintHelp() 113 | }) 114 | 115 | t.Run("Run isDefaultCommand()", func(t *testing.T) { 116 | c.isDefaultCommand() 117 | 118 | }) 119 | 120 | t.Run("Run isHidden()", func(t *testing.T) { 121 | c.isHidden() 122 | 123 | }) 124 | 125 | t.Run("Run Hidden()", func(t *testing.T) { 126 | c.Hidden() 127 | }) 128 | 129 | t.Run("Run NewSubCommand()", func(t *testing.T) { 130 | c.NewSubCommand("name", "description") 131 | 132 | }) 133 | 134 | t.Run("Run NewSubCommandInheritFlags()", func(t *testing.T) { 135 | inherit := false 136 | c.BoolFlag("inherit", "test inherence", &inherit) 137 | s := c.NewSubCommandInheritFlags("name", "description") 138 | if s.flags.Lookup("inherit") == nil { 139 | t.Error("flag inherence") 140 | t.Fail() 141 | } 142 | }) 143 | 144 | t.Run("Run inheritFlags()", func(t *testing.T) { 145 | inheritFlag := false 146 | c.BoolFlag("inheritFlag", "test inherence", &inheritFlag) 147 | s := c.NewSubCommand("name", "description") 148 | s.inheritFlags(c.flags) 149 | if s.flags.Lookup("inheritFlag") == nil { 150 | t.Error("flag inherence") 151 | t.Fail() 152 | } 153 | }) 154 | 155 | t.Run("Run AddCommand()", func(t *testing.T) { 156 | c.AddCommand(c) 157 | }) 158 | 159 | t.Run("Run StringFlag()", func(t *testing.T) { 160 | variable := "variable" 161 | c.StringFlag("name", "description", &variable) 162 | }) 163 | 164 | t.Run("Run IntFlag()", func(t *testing.T) { 165 | var variable int 166 | c.IntFlag("test", "description", &variable) 167 | }) 168 | 169 | t.Run("Run LongDescription()", func(t *testing.T) { 170 | c.LongDescription("name") 171 | }) 172 | } 173 | -------------------------------------------------------------------------------- /examples/basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/leaanthony/clir" 5 | ) 6 | 7 | func main() { 8 | 9 | // Create new cli 10 | cli := clir.NewCli("Basic", "A basic example", "v0.0.1") 11 | 12 | // Set long description 13 | cli.LongDescription("This app prints hello world") 14 | 15 | // Define action 16 | cli.Action(func() error { 17 | println("Hello World!") 18 | return nil 19 | }) 20 | 21 | // Run! 22 | cli.Run() 23 | 24 | } 25 | -------------------------------------------------------------------------------- /examples/chained/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/leaanthony/clir" 7 | ) 8 | 9 | func main() { 10 | 11 | // Vars 12 | var name string 13 | var age int 14 | var awesome bool 15 | 16 | // More chain than Fleetwood Mac 17 | clir.NewCli("Flags", "An example of using flags", "v0.0.1"). 18 | StringFlag("name", "Your name", &name). 19 | IntFlag("age", "Your age", &age). 20 | BoolFlag("awesome", "Are you awesome?", &awesome). 21 | Action(func() error { 22 | 23 | if name == "" { 24 | name = "Anonymous" 25 | } 26 | fmt.Printf("Hello %s!\n", name) 27 | 28 | if age > 0 { 29 | fmt.Printf("You are %d years old!\n", age) 30 | } 31 | 32 | if awesome { 33 | fmt.Println("You are awesome!") 34 | } 35 | 36 | return nil 37 | }).Run() 38 | 39 | } 40 | -------------------------------------------------------------------------------- /examples/custom-banner/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/leaanthony/clir" 5 | ) 6 | 7 | func customBanner(cli *clir.Cli) string { 8 | 9 | return ` 10 | ______ __ __ ______ _________ ______ ___ __ __ 11 | /_____/\ /_/\/_/\ /_____/\ /________/\/_____/\ /__//_//_/\ 12 | \:::__\/ \:\ \:\ \\::::_\/_\__.::.__\/\:::_ \ \\::\| \| \ \ 13 | \:\ \ __\:\ \:\ \\:\/___/\ \::\ \ \:\ \ \ \\:. \ \ 14 | \:\ \/_/\\:\ \:\ \\_::._\:\ \::\ \ \:\ \ \ \\:.\-/\ \ \ 15 | \:\_\ \ \\:\_\:\ \ /____\:\ \::\ \ \:\_\ \ \\. \ \ \ \ 16 | \_____\/ \_____\/ \_____\/ \__\/ \_____\/ \__\/ \__\/ 17 | ` + cli.Version() + " - " + cli.ShortDescription() 18 | } 19 | 20 | func main() { 21 | 22 | // Create new cli 23 | cli := clir.NewCli("Banner", "A custom banner example", "v0.0.1") 24 | 25 | cli.SetBannerFunction(customBanner) 26 | 27 | // Define action 28 | cli.Action(func() error { 29 | println("Hello World!") 30 | return nil 31 | }) 32 | 33 | // Run! 34 | cli.Run() 35 | 36 | } 37 | -------------------------------------------------------------------------------- /examples/custom-flag-error/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/leaanthony/clir" 7 | ) 8 | 9 | func customFlagError(cmdPath string, err error) error { 10 | return fmt.Errorf(`%s 11 | flag v0.0.1 - A custom error example 12 | 13 | Flags: 14 | 15 | --help 16 | Get help on the '%s' command`, err, cmdPath) 17 | } 18 | 19 | func main() { 20 | 21 | // Create new cli 22 | cli := clir.NewCli("flag", "A custom error example", "v0.0.1") 23 | 24 | cli.SetErrorFunction(customFlagError) 25 | 26 | cli.NewSubCommand("test", "Testing whether subcommand path returns correctly via err callback") 27 | 28 | // Run! 29 | if err := cli.Run(); err != nil { 30 | fmt.Println(err) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /examples/default/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/leaanthony/clir" 5 | ) 6 | 7 | func main() { 8 | 9 | // Create new cli 10 | cli := clir.NewCli("subcommands", "An example of subcommands", "v0.0.1") 11 | 12 | // Create an init subcommand 13 | init := cli.NewSubCommand("init", "Initialise the app") 14 | init.Action(func() error { 15 | println("I am initialising!") 16 | return nil 17 | }) 18 | 19 | // Create a test subcommand 20 | test := cli.NewSubCommand("test", "Test the app") 21 | test.Action(func() error { 22 | println("I am testing!") 23 | return nil 24 | }) 25 | 26 | // Set default command to run 27 | cli.DefaultCommand(init) 28 | 29 | // Run! 30 | cli.Run() 31 | 32 | } 33 | -------------------------------------------------------------------------------- /examples/flags-compact/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/leaanthony/clir" 7 | ) 8 | 9 | func main() { 10 | 11 | // Vars 12 | var name string 13 | var age int 14 | var awesome bool 15 | 16 | // Create new cli 17 | cli := clir.NewCli("Flags", "An example of using flags", "v0.0.1") 18 | 19 | // Define flags 20 | cli.StringFlag("name", "Your name", &name). 21 | IntFlag("age", "Your age", &age). 22 | BoolFlag("awesome", "Are you awesome?", &awesome) 23 | 24 | // Define action 25 | cli.Action(func() error { 26 | 27 | if name == "" { 28 | name = "Anonymous" 29 | } 30 | fmt.Printf("Hello %s!\n", name) 31 | 32 | if age > 0 { 33 | fmt.Printf("You are %d years old!\n", age) 34 | } 35 | 36 | if awesome { 37 | fmt.Println("You are awesome!") 38 | } 39 | 40 | return nil 41 | }) 42 | 43 | cli.Run() 44 | 45 | } 46 | -------------------------------------------------------------------------------- /examples/flags-function/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/leaanthony/clir" 7 | ) 8 | 9 | type EmbeddedFlags struct { 10 | Address string `flag:"address" description:"The address of the person" default:"123 Main Street"` 11 | } 12 | 13 | type AppFlags struct { 14 | EmbeddedFlags 15 | Name string `default:"Bob" pos:"1"` 16 | Age int `default:"20" pos:"2"` 17 | } 18 | 19 | func main() { 20 | 21 | // Create new cli 22 | cli := clir.NewCli("Flags", "An example of using flags", "v0.0.1") 23 | 24 | cli.NewSubCommandFunction("create", "Create a new person", createPerson) 25 | cli.Run("create", "bob", "20", "--address", "123 Main Street") 26 | } 27 | 28 | func createPerson(flags *AppFlags) error { 29 | fmt.Println("Name:", flags.Name) 30 | fmt.Println("Age:", flags.Age) 31 | fmt.Println("Address:", flags.Address) 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /examples/flags-positional/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/leaanthony/clir" 7 | ) 8 | 9 | type EmbeddedFlags struct { 10 | Address string `flag:"address" description:"The address of the person" default:"123 Main Street"` 11 | } 12 | 13 | type AppFlags struct { 14 | EmbeddedFlags 15 | Name string `default:"Bob"` 16 | Age int `default:"20"` 17 | } 18 | 19 | func main() { 20 | 21 | // Create new cli 22 | cli := clir.NewCli("Flags", "An example of using flags", "v0.0.1") 23 | 24 | cli.NewSubCommandFunction("create", "Create a new person", createPerson) 25 | cli.Run() 26 | 27 | } 28 | 29 | func createPerson(flags *AppFlags) error { 30 | fmt.Println("Name:", flags.Name) 31 | fmt.Println("Age:", flags.Age) 32 | fmt.Println("Address:", flags.Address) 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /examples/flags-slice/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/leaanthony/clir" 6 | "reflect" 7 | ) 8 | 9 | type Flags struct { 10 | String string `name:"string" description:"The string" pos:"1"` 11 | Strings []string `name:"strings" description:"The strings" pos:"2"` 12 | StringsDefault []string `name:"strings_default" description:"The strings default" default:"one|two|three" sep:"|" pos:"3"` 13 | 14 | Int int `name:"int" description:"The int" pos:"4"` 15 | Ints []int `name:"ints" description:"The ints" pos:"5"` 16 | IntsDefault []int `name:"ints_default" description:"The ints default" default:"3|4|5" sep:"|" pos:"6"` 17 | 18 | Int8 int8 `name:"int8" description:"The int8" pos:"7"` 19 | Int8s []int8 `name:"int8s" description:"The int8s" pos:"8"` 20 | Int8sDefault []int8 `name:"int8s_default" description:"The int8s default" default:"3,4,5" sep:"," pos:"9"` 21 | 22 | Int16 int16 `name:"int16" description:"The int16" pos:"10"` 23 | Int16s []int16 `name:"int16s" description:"The int16s" pos:"11"` 24 | Int16sDefault []int16 `name:"int16s_default" description:"The int16s default" default:"3,4,5" sep:"," pos:"12"` 25 | 26 | Int32 int32 `name:"int32" description:"The int32" pos:"13"` 27 | Int32s []int32 `name:"int32s" description:"The int32s" pos:"14"` 28 | Int32sDefault []int32 `name:"int32s_default" description:"The int32 default" default:"3,4,5" sep:"," pos:"15"` 29 | 30 | Int64 int64 `name:"int64" description:"The int64" pos:"16"` 31 | Int64s []int64 `name:"int64s" description:"The int64s" pos:"17"` 32 | Int64sDefault []int64 `name:"int64s_default" description:"The int64s default" default:"3,4,5" sep:"," pos:"18"` 33 | 34 | Uint uint `name:"uint" description:"The uint" pos:"19"` 35 | Uints []uint `name:"uints" description:"The uints" pos:"20"` 36 | UintsDefault []uint `name:"uints_default" description:"The uints default" default:"3,4,5" sep:"," pos:"21"` 37 | 38 | Uint8 uint8 `name:"uint8" description:"The uint8" pos:"22"` 39 | Uint8s []uint8 `name:"uint8s" description:"The uint8s" pos:"23"` 40 | Uint8sDefault []uint8 `name:"uint8s_default" description:"The uint8s default" default:"3,4,5" sep:"," pos:"24"` 41 | 42 | Uint16 uint16 `name:"uint16" description:"The uint16" pos:"25"` 43 | Uint16s []uint16 `name:"uint16s" description:"The uint16s" pos:"26"` 44 | Uint16sDefault []uint16 `name:"uint16s_default" description:"The uint16 default" default:"3,4,5" sep:"," pos:"27"` 45 | 46 | Uint32 uint32 `name:"uint32" description:"The uint32" pos:"28"` 47 | Uint32s []uint32 `name:"uint32s" description:"The uint32s" pos:"29"` 48 | Uint32sDefault []uint32 `name:"uint32s_default" description:"The uint32s default" default:"3,4,5" sep:"," pos:"30"` 49 | 50 | Uint64 uint64 `name:"uint64" description:"The uint64" pos:"31"` 51 | Uint64s []uint64 `name:"uint64s" description:"The uint64s" pos:"32"` 52 | Uint64sDefault []uint64 `name:"uint64s_default" description:"The uint64s default" default:"3,4,5" sep:"," pos:"33"` 53 | 54 | Float32 float32 `name:"float32" description:"The float32" pos:"34"` 55 | Float32s []float32 `name:"float32s" description:"The float32s" pos:"35"` 56 | Float32sDefault []float32 `name:"float32s_default" description:"The float32s default" default:"3|4|5" sep:"|" pos:"36"` 57 | 58 | Float64 float64 `name:"float64" description:"The float64" pos:"37"` 59 | Float64s []float64 `name:"float64s" description:"The float64s" pos:"38"` 60 | Float64sDefault []float64 `name:"float64s_default" description:"The float64s default" default:"3|4|5" sep:"|" pos:"39"` 61 | 62 | Bool bool `name:"bool" description:"The bool" pos:"40"` 63 | Bools []bool `name:"bools" description:"The bools" pos:"41"` 64 | BoolsDefault []bool `name:"bools_default" description:"The bools default" default:"false|true|false|true" sep:"|" pos:"42"` 65 | } 66 | 67 | func main() { 68 | 69 | // Create new cli 70 | cli := clir.NewCli("flagstruct", "An example of subcommands with flag inherence", "v0.0.1") 71 | 72 | // Create an init subcommand with flag inheritance 73 | init := cli.NewSubCommand("flag", "print default") 74 | 75 | flags := &Flags{ 76 | String: "zkep", 77 | Strings: []string{"one", "two", "three"}, 78 | Int: 1, 79 | Ints: []int{1, 2, 3}, 80 | Int8: int8(1), 81 | Int8s: []int8{1, 2, 3}, 82 | Int16: int16(1), 83 | Int16s: []int16{1, 2, 3}, 84 | Int32: int32(1), 85 | Int32s: []int32{1, 2, 3}, 86 | Int64: int64(1), 87 | Int64s: []int64{1, 2, 3}, 88 | Uint: uint(1), 89 | Uints: []uint{1, 2, 3}, 90 | Uint8: uint8(1), 91 | Uint8s: []uint8{1, 2, 3}, 92 | Uint16: uint16(1), 93 | Uint16s: []uint16{1, 2, 3}, 94 | Uint32: uint32(1), 95 | Uint32s: []uint32{1, 2, 3}, 96 | Uint64: uint64(1), 97 | Uint64s: []uint64{1, 2, 3}, 98 | Float32: float32(3.14), 99 | Float32s: []float32{1.1, 2.2, 3.3}, 100 | Float64: float64(3.14), 101 | Float64s: []float64{1.1, 2.2, 3.3}, 102 | Bool: false, 103 | Bools: []bool{true, false, false}, 104 | } 105 | init.AddFlags(flags) 106 | 107 | init.Action(func() error { 108 | 109 | println("string:", fmt.Sprintf("%#v", flags.String)) 110 | println("strings:", fmt.Sprintf("%#v", flags.Strings)) 111 | println("strings_default:", fmt.Sprintf("%#v", flags.StringsDefault)) 112 | println("\n") 113 | 114 | println("int:", fmt.Sprintf("%#v", flags.Int)) 115 | println("ints:", fmt.Sprintf("%#v", flags.Ints)) 116 | println("ints_default:", fmt.Sprintf("%#v", flags.IntsDefault)) 117 | println("\n") 118 | 119 | println("int8:", fmt.Sprintf("%#v", flags.Int8)) 120 | println("int8s:", fmt.Sprintf("%#v", flags.Int8s)) 121 | println("int8s_default:", fmt.Sprintf("%#v", flags.Int8sDefault)) 122 | println("\n") 123 | 124 | println("int16:", fmt.Sprintf("%#v", flags.Int16)) 125 | println("int16s:", fmt.Sprintf("%#v", flags.Int16s)) 126 | println("int16s_default:", fmt.Sprintf("%#v", flags.Int16sDefault)) 127 | println("\n") 128 | 129 | println("int32:", fmt.Sprintf("%#v", flags.Int32)) 130 | println("int32s:", fmt.Sprintf("%#v", flags.Int32s)) 131 | println("int32s_default:", fmt.Sprintf("%#v", flags.Int32sDefault)) 132 | println("\n") 133 | 134 | println("int64:", fmt.Sprintf("%#v", flags.Int64)) 135 | println("int64s:", fmt.Sprintf("%#v", flags.Int64s)) 136 | println("int64s_default:", fmt.Sprintf("%#v", flags.Int64sDefault)) 137 | println("\n") 138 | 139 | println("uint:", fmt.Sprintf("%#v", flags.Uint)) 140 | println("uints:", fmt.Sprintf("%#v", flags.Uints)) 141 | println("uints_default:", fmt.Sprintf("%#v", flags.UintsDefault)) 142 | println("\n") 143 | 144 | println("uint8:", fmt.Sprintf("%#v", flags.Uint8)) 145 | println("uint8s:", fmt.Sprintf("%#v", flags.Uint8s)) 146 | println("uint8s_default:", fmt.Sprintf("%#v", flags.Uint8sDefault)) 147 | println("\n") 148 | 149 | println("uint16:", fmt.Sprintf("%#v", flags.Uint16)) 150 | println("uint16s:", fmt.Sprintf("%#v", flags.Uint16s)) 151 | println("uint16s_default:", fmt.Sprintf("%#v", flags.Uint16sDefault)) 152 | println("\n") 153 | 154 | println("uint32:", fmt.Sprintf("%#v", flags.Uint32)) 155 | println("uint32s:", fmt.Sprintf("%#v", flags.Uint32s)) 156 | println("uint32s_default:", fmt.Sprintf("%#v", flags.Uint32sDefault)) 157 | println("\n") 158 | 159 | println("uint64:", fmt.Sprintf("%#v", flags.Uint)) 160 | println("uint64s:", fmt.Sprintf("%#v", flags.Uint64s)) 161 | println("uint64s_default:", fmt.Sprintf("%#v", flags.Uint64sDefault)) 162 | println("\n") 163 | 164 | println("float32:", fmt.Sprintf("%#v", flags.Float32)) 165 | println("float32s:", fmt.Sprintf("%#v", flags.Float32s)) 166 | println("float32s_default:", fmt.Sprintf("%#v", flags.Float32sDefault)) 167 | println("\n") 168 | 169 | println("float64:", fmt.Sprintf("%#v", flags.Float64)) 170 | println("float64s:", fmt.Sprintf("%#v", flags.Float64s)) 171 | println("float64s_default:", fmt.Sprintf("%#v", flags.Float64sDefault)) 172 | println("\n") 173 | 174 | println("bool:", fmt.Sprintf("%#v", flags.Bool)) 175 | println("bools:", fmt.Sprintf("%#v", flags.Bools)) 176 | println("bools_default:", fmt.Sprintf("%#v", flags.BoolsDefault)) 177 | println("\n") 178 | 179 | return nil 180 | }) 181 | 182 | // Run! 183 | if err := cli.Run("flag"); err != nil { 184 | panic(err) 185 | } 186 | 187 | cli.NewSubCommandFunction("positional", "test positional args", func(f *Flags) error { 188 | 189 | if f.String != "hello" { 190 | panic(fmt.Sprintf("expected 'hello', got '%v'", f.String)) 191 | } 192 | 193 | if !reflect.DeepEqual(f.Strings, []string{"zkep,hello,clir"}) { 194 | panic(fmt.Sprintf("expected 'zkep,hello,clir', got '%v'", f.Strings)) 195 | } 196 | 197 | if !reflect.DeepEqual(f.StringsDefault, []string{"zkep", "clir", "hello"}) { 198 | panic(fmt.Sprintf("expected '[zkep,clir,hello]', got '%v'", f.StringsDefault)) 199 | } 200 | 201 | println("string:", fmt.Sprintf("%#v", f.String)) 202 | println("strings:", fmt.Sprintf("%#v", f.Strings)) 203 | println("strings_default:", fmt.Sprintf("%#v", f.StringsDefault)) 204 | println("\n") 205 | 206 | return nil 207 | }) 208 | 209 | // Run! 210 | // The pos 3 slice separator is '|' in struct tag 211 | if err := cli.Run("positional", "hello", "zkep,hello,clir", "zkep|clir|hello"); err != nil { 212 | panic(err) 213 | } 214 | 215 | } 216 | -------------------------------------------------------------------------------- /examples/flags/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/leaanthony/clir" 7 | ) 8 | 9 | func main() { 10 | 11 | // Create new cli 12 | cli := clir.NewCli("Flags", "An example of using flags", "v0.0.1") 13 | 14 | // Name 15 | var name string 16 | cli.StringFlag("name", "Your name", &name) 17 | 18 | // Age 19 | var age int 20 | cli.IntFlag("age", "Your age", &age) 21 | 22 | // Awesome 23 | var awesome bool 24 | cli.BoolFlag("awesome", "Are you awesome?", &awesome) 25 | 26 | // Define action for the command 27 | cli.Action(func() error { 28 | 29 | if name == "" { 30 | name = "Anonymous" 31 | } 32 | fmt.Printf("Hello %s!\n", name) 33 | 34 | if age > 0 { 35 | fmt.Printf("You are %d years old!\n", age) 36 | } 37 | 38 | if awesome { 39 | fmt.Println("You are awesome!") 40 | } 41 | 42 | return nil 43 | }) 44 | 45 | cli.Run() 46 | 47 | } 48 | -------------------------------------------------------------------------------- /examples/flagstruct/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/leaanthony/clir" 5 | ) 6 | 7 | type Flags struct { 8 | Name string `name:"name" description:"The name of the person"` 9 | Age int `name:"age" description:"The age of the person"` 10 | } 11 | 12 | func main() { 13 | 14 | // Create new cli 15 | cli := clir.NewCli("flagstruct", "An example of subcommands with flag inherence", "v0.0.1") 16 | 17 | // Create an init subcommand with flag inheritance 18 | init := cli.NewSubCommand("create", "Create a person") 19 | person := &Flags{ 20 | Age: 20, 21 | } 22 | init.AddFlags(person) 23 | init.Action(func() error { 24 | println("Name:", person.Name, "Age:", person.Age) 25 | return nil 26 | }) 27 | 28 | // Run! 29 | if err := cli.Run(); err != nil { 30 | panic(err) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /examples/hidden/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/leaanthony/clir" 5 | ) 6 | 7 | func main() { 8 | 9 | // Create new cli 10 | cli := clir.NewCli("subcommands", "An example of subcommands", "v0.0.1") 11 | 12 | // Create an init subcommand 13 | init := cli.NewSubCommand("init", "Initialise the app") 14 | init.Action(func() error { 15 | println("I am initialising!") 16 | return nil 17 | }) 18 | 19 | // Create a test subcommand 20 | test := cli.NewSubCommand("test", "Test the app") 21 | test.Action(func() error { 22 | println("I am testing!") 23 | return nil 24 | }) 25 | 26 | // Make test hidden 27 | test.Hidden() 28 | 29 | // Run! 30 | cli.Run() 31 | 32 | } 33 | -------------------------------------------------------------------------------- /examples/nested-subcommands/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/leaanthony/clir" 5 | ) 6 | 7 | func main() { 8 | 9 | // Create new cli 10 | cli := clir.NewCli("Nested Subcommands", "An example of nested subcommands", "v0.0.1") 11 | 12 | // Create a top level subcommand 13 | // Run with: `nested-subcommand top` 14 | top := cli.NewSubCommand("top", "top level command") 15 | top.Action(func() error { 16 | println("I am the top-level command!") 17 | return nil 18 | }) 19 | 20 | // Create a middle subcommand on the top command 21 | // Run with: `nested-subcommand top middle` 22 | middle := top.NewSubCommand("middle", "middle level command") 23 | middle.Action(func() error { 24 | println("I am the middle-level command!") 25 | return nil 26 | }) 27 | 28 | // Create a bottom subcommand on the middle command 29 | // Run with: `nested-subcommand top middle bottom` 30 | bottom := middle.NewSubCommand("bottom", "bottom level command") 31 | bottom.Action(func() error { 32 | println("I am the bottom-level command!") 33 | return nil 34 | }) 35 | 36 | // Run! 37 | cli.Run() 38 | 39 | } 40 | -------------------------------------------------------------------------------- /examples/otherargs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/leaanthony/clir" 7 | ) 8 | 9 | func main() { 10 | 11 | // Create new cli 12 | cli := clir.NewCli("Other Args", "A basic example", "v0.0.1") 13 | 14 | // Set long description 15 | cli.LongDescription("This app shows additional arguments passed to your commands") 16 | 17 | // Name 18 | var name string 19 | cli.StringFlag("name", "Your name", &name) 20 | 21 | // Define action 22 | cli.Action(func() error { 23 | println("Your name is", name) 24 | fmt.Printf("The remaining arguments were: %+v\n", cli.OtherArgs()) 25 | return nil 26 | }) 27 | // Using a subcommand instead of a flag 28 | nameCommand := cli.NewSubCommand("namecommand", "Shows your name!") 29 | nameCommand.Action(func() error { 30 | fmt.Printf("The remaining arguments were: %+v\n", nameCommand.OtherArgs()) 31 | return nil 32 | }) 33 | 34 | // Run! 35 | cli.Run() 36 | 37 | } 38 | -------------------------------------------------------------------------------- /examples/subcommandinheritflags/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/leaanthony/clir" 5 | ) 6 | 7 | func main() { 8 | 9 | // Create new cli 10 | cli := clir.NewCli("subcommandinheritflags", "An example of subcommands with flag inherence", "v0.0.1") 11 | 12 | inheritFlag := false 13 | cli.BoolFlag("inherit", "flag to inherit", &inheritFlag) 14 | 15 | // Create an init subcommand with flag inheritance 16 | init := cli.NewSubCommandInheritFlags("init", "Initialise the app") 17 | init.Action(func() error { 18 | println("I am initializing!", "inherit flag:", inheritFlag) 19 | return nil 20 | }) 21 | 22 | // Run! 23 | if err := cli.Run(); err != nil { 24 | panic(err) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /examples/subcommands/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/leaanthony/clir" 5 | ) 6 | 7 | func main() { 8 | 9 | // Create new cli 10 | cli := clir.NewCli("subcommands", "An example of subcommands", "v0.0.1") 11 | 12 | // Create an init subcommand 13 | init := cli.NewSubCommand("init", "Initialise the app") 14 | init.Action(func() error { 15 | println("I am initialising!") 16 | return nil 17 | }) 18 | 19 | // Create a test subcommand that's hidden 20 | test := cli.NewSubCommand("test", "Test the app") 21 | test.Action(func() error { 22 | println("I am testing!") 23 | return nil 24 | }) 25 | 26 | // Run! 27 | cli.Run() 28 | 29 | } 30 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/leaanthony/clir 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leaanthony/clir/a8d9462bce754819cbd5a5596f22d68b3f318fb6/go.sum -------------------------------------------------------------------------------- /website/docs/examples/basic.md: -------------------------------------------------------------------------------- 1 | 2 | # Basic 3 | 4 | ```go 5 | --8<-- "../examples/basic/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/chained.md: -------------------------------------------------------------------------------- 1 | 2 | # Chained 3 | 4 | ```go 5 | --8<-- "../examples/chained/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/custom-banner.md: -------------------------------------------------------------------------------- 1 | 2 | # Custom Banner 3 | 4 | ```go 5 | --8<-- "../examples/custom-banner/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/custom-flag-error.md: -------------------------------------------------------------------------------- 1 | 2 | # Custom Flag Error 3 | 4 | ```go 5 | --8<-- "../examples/custom-flag-error/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/default.md: -------------------------------------------------------------------------------- 1 | 2 | # Default Command 3 | 4 | ```go 5 | --8<-- "../examples/default/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/flags-compact.md: -------------------------------------------------------------------------------- 1 | 2 | # Flags Compact 3 | 4 | ```go 5 | --8<-- "../examples/flags-compact/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/flags-function.md: -------------------------------------------------------------------------------- 1 | 2 | # Flags Function 3 | 4 | ```go 5 | --8<-- "../examples/flags-function/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/flags-positional.md: -------------------------------------------------------------------------------- 1 | 2 | # Flags Positional 3 | 4 | ```go 5 | --8<-- "../examples/flags-positional/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/flags-slice.md: -------------------------------------------------------------------------------- 1 | 2 | # Flags Slice 3 | 4 | ```go 5 | --8<-- "../examples/flags-slice/main.go" 6 | ``` -------------------------------------------------------------------------------- /website/docs/examples/flags.md: -------------------------------------------------------------------------------- 1 | # Flags 2 | 3 | ```go 4 | --8<-- "../examples/flags/main.go" 5 | ``` 6 | -------------------------------------------------------------------------------- /website/docs/examples/flagstruct.md: -------------------------------------------------------------------------------- 1 | 2 | # Flag Struct 3 | 4 | ```go 5 | --8<-- "../examples/flagstruct/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/hidden.md: -------------------------------------------------------------------------------- 1 | 2 | # Hidden 3 | 4 | ```go 5 | --8<-- "../examples/hidden/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/nested-subcommands.md: -------------------------------------------------------------------------------- 1 | 2 | # Nested Subcommands 3 | 4 | ```go 5 | --8<-- "../examples/nested-subcommands/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/otherargs.md: -------------------------------------------------------------------------------- 1 | 2 | # Other Args 3 | 4 | ```go 5 | --8<-- "../examples/otherargs/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/subcommandinheritflags.md: -------------------------------------------------------------------------------- 1 | 2 | # Subcommand Inherit Flags 3 | 4 | ```go 5 | --8<-- "../examples/subcommandinheritflags/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/examples/subcommands.md: -------------------------------------------------------------------------------- 1 | 2 | # Subcommands 3 | 4 | ```go 5 | --8<-- "../examples/subcommands/main.go" 6 | ``` 7 | -------------------------------------------------------------------------------- /website/docs/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "FAQ" 3 | --- 4 | 5 | # FAQ 6 | 7 | ## Why did you write this? 8 | 9 | Because other libraries did not easily do what I wanted. They were also pretty heavyweight and I wanted lean 'n' mean. 10 | 11 | ## What does Clîr mean? 12 | 13 | It's the [Welsh](https://en.wikipedia.org/wiki/Welsh_language) word for "Clear". It sounds almost identical but it's Cleeeer rather than Clee-ur. 14 | 15 | ## Will you support feature X? 16 | 17 | Probably not. 18 | 19 | #### What about double dashes? 20 | 21 | Probably not. 22 | 23 | #### Why not? 24 | 25 | Stability and Simplicity. -------------------------------------------------------------------------------- /website/docs/gettingstarted.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Getting Started" 3 | date: 2019-11-21T16:15:09+08:00 4 | draft: false 5 | weight: 10 6 | --- 7 | 8 | ## Installation 9 | 10 | `go get github.com/leaanthony/clir` 11 | 12 | ## Basic Usage 13 | 14 | ```go 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "log" 20 | 21 | "github.com/leaanthony/clir" 22 | ) 23 | 24 | func main() { 25 | 26 | // Create new cli 27 | cli := clir.NewCli("Flags", "A simple example", "v0.0.1") 28 | 29 | // Name 30 | name := "Anonymous" 31 | cli.StringFlag("name", "Your name", &name) 32 | 33 | // Define action for the command 34 | cli.Action(func() error { 35 | fmt.Printf("Hello %s!\n", name) 36 | return nil 37 | }) 38 | 39 | // Run the application 40 | err := cli.Run() 41 | if err != nil { 42 | // We had an error 43 | log.Fatal(err) 44 | } 45 | 46 | } 47 | ``` -------------------------------------------------------------------------------- /website/docs/guide/actions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Actions" 3 | date: 2019-11-21T16:15:09+08:00 4 | draft: false 5 | weight: 50 6 | chapter: false 7 | --- 8 | 9 | Actions define what code will be executed when your application is run. A basic example of this is: 10 | 11 | ```go 12 | package main 13 | 14 | import "github.com/leaanthony/clir" 15 | import "log" 16 | 17 | func main() { 18 | 19 | // Create the application 20 | cli := clir.NewCli("Actions", "A simple example", "v0.0.1") 21 | 22 | // Define main action 23 | cli.Action(func() error { 24 | println("Hello World!") 25 | return nil 26 | }) 27 | 28 | // Run application 29 | err := cli.Run() 30 | if err != nil { 31 | // We had an error 32 | log.Fatal(err) 33 | } 34 | } 35 | ``` 36 | 37 | Running this command will simply run our action: 38 | 39 | ```shell 40 | > actions 41 | Hello World! 42 | ``` 43 | 44 | To interact with the flags passed in, we simply use scoping: 45 | 46 | ```go 47 | package main 48 | 49 | import ( 50 | "fmt" 51 | "log" 52 | 53 | "github.com/leaanthony/clir" 54 | ) 55 | 56 | func main() { 57 | 58 | // Create the application 59 | cli := clir.NewCli("Actions", "A simple example", "v0.0.1") 60 | 61 | // Set our default name to "Anonymous" 62 | name := "Anonymous" 63 | cli.StringFlag("name", "Your name", &name) 64 | 65 | // Define action for the command 66 | cli.Action(func() error { 67 | fmt.Printf("Hello %s!\n", name) 68 | return nil 69 | }) 70 | 71 | // Run application 72 | err := cli.Run() 73 | if err != nil { 74 | // We had an error 75 | log.Fatal(err) 76 | } 77 | 78 | } 79 | ``` 80 | 81 | When we run this with no flags we get: 82 | ```shell 83 | > actions 84 | Hello Anonymous! 85 | ``` 86 | 87 | Passing in a name produces the expected output: 88 | ```shell 89 | > actions -name Debbie 90 | Hello Debbie! 91 | ``` 92 | 93 | ## API 94 | 95 | **Cli.Action(fn func() error)** 96 | 97 | Action binds the given function to the application. It is called when the application is executed. Any errors returned by actions are passed back to via the main `Cli.Run` method. 98 | 99 | Example: 100 | 101 | ```go 102 | package main 103 | 104 | import ( 105 | "fmt" 106 | "log" 107 | 108 | "github.com/leaanthony/clir" 109 | ) 110 | 111 | func main() { 112 | 113 | // Create the application 114 | cli := clir.NewCli("Actions", "A simple example", "v0.0.1") 115 | 116 | // Define action for the command 117 | cli.Action(func() error { 118 | return fmt.Errorf("I am an error") 119 | }) 120 | 121 | // We will receive our error here 122 | err := cli.Run() 123 | if err != nil { 124 | log.Fatal(err) 125 | } 126 | } 127 | ``` 128 | 129 | Running this will produce: 130 | 131 | ```shell 132 | > actions 133 | 2019/11/23 08:03:56 I am an error 134 | ``` -------------------------------------------------------------------------------- /website/docs/guide/cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "CLI" 3 | date: 2019-11-21T16:15:09+08:00 4 | draft: false 5 | weight: 30 6 | chapter: false 7 | --- 8 | 9 | The main entry point for a Clîr application is the Cli object. This is created using the `NewCli` command which takes an application name, description and optional version: 10 | 11 | ```go 12 | package main 13 | 14 | import "github.com/leaanthony/clir" 15 | import "log" 16 | 17 | func main() { 18 | 19 | // Create the application 20 | cli := clir.NewCli("Basic", "A basic example", "v0.0.1") 21 | 22 | // Run the application 23 | err := cli.Run() 24 | if err != nil { 25 | // We had an error 26 | log.Fatal(err) 27 | } 28 | } 29 | ``` 30 | 31 | When you run this app, you will get the default help text: 32 | 33 | ```shell 34 | > basic 35 | Basic v0.0.1 - A basic example 36 | 37 | Flags: 38 | 39 | -help 40 | Get help on the 'basic' command. 41 | 42 | ``` 43 | 44 | ### API 45 | 46 | **NewCli(name string, description string, version string) *Cli** 47 | 48 | The [NewCli](https://godoc.org/github.com/leaanthony/clir#NewCli) function creates a new Clîr application. 49 | 50 | **Cli.Run(args ...string) error** 51 | 52 | The [Run](https://godoc.org/github.com/leaanthony/clir#Cli.Run) method starts the application. By default it will use `os.Args`, though you are free to pass in arguments for testing purposes. Run returns an error, which may be handled appropriately. 53 | -------------------------------------------------------------------------------- /website/docs/guide/custombanner.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Custom Banner" 3 | date: 2019-11-21T16:15:09+08:00 4 | draft: false 5 | weight: 90 6 | chapter: false 7 | --- 8 | 9 | It is possible to provide your own banner by setting the Banner function that the application will call: 10 | 11 | ```go 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/leaanthony/clir" 18 | ) 19 | 20 | func customBanner(cli *clir.Cli) string { 21 | 22 | return ` 23 | ______ __ __ ______ _________ ______ ___ __ __ 24 | /_____/\ /_/\/_/\ /_____/\ /________/\/_____/\ /__//_//_/\ 25 | \:::__\/ \:\ \:\ \\::::_\/_\__.::.__\/\:::_ \ \\::\| \| \ \ 26 | \:\ \ __\:\ \:\ \\:\/___/\ \::\ \ \:\ \ \ \\:. \ \ 27 | \:\ \/_/\\:\ \:\ \\_::._\:\ \::\ \ \:\ \ \ \\:.\-/\ \ \ 28 | \:\_\ \ \\:\_\:\ \ /____\:\ \::\ \ \:\_\ \ \\. \ \ \ \ 29 | \_____\/ \_____\/ \_____\/ \__\/ \_____\/ \__\/ \__\/ 30 | ` + cli.Version() + " - " + cli.ShortDescription() 31 | } 32 | 33 | func main() { 34 | 35 | // Create new cli 36 | cli := clir.NewCli("Banner", "A custom banner example", "v0.0.1") 37 | 38 | // Set the custom banner 39 | cli.SetBannerFunction(customBanner) 40 | 41 | // Run the application 42 | err := cli.Run() 43 | if err != nil { 44 | // We had an error 45 | log.Fatal(err) 46 | } 47 | 48 | } 49 | ``` 50 | 51 | The `setBannerFunction` method expects a function with the signature `func (*clir.Cli) string`. 52 | -------------------------------------------------------------------------------- /website/docs/guide/flags.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Flags" 3 | --- 4 | 5 | Flags may be added to your application as follows: 6 | 7 | ```go 8 | package main 9 | 10 | import "github.com/leaanthony/clir" 11 | import "log" 12 | 13 | func main() { 14 | 15 | // Create the application 16 | cli := clir.NewCli("Flags", "A simple example", "v0.0.1") 17 | 18 | // Add a string flag 19 | var name string 20 | cli.StringFlag("name", "Your name", &name) 21 | 22 | // Add an int flag 23 | var age int 24 | cli.IntFlag("age", "Your age", &age) 25 | 26 | // Add a bool flag 27 | var awesome bool 28 | cli.BoolFlag("awesome", "Are you awesome?", &awesome) 29 | 30 | // Run the application 31 | err := cli.Run() 32 | if err != nil { 33 | // We had an error 34 | log.Fatal(err) 35 | } 36 | } 37 | ``` 38 | 39 | Running this command prints the default help text as expected: 40 | 41 | ```shell 42 | Flags v0.0.1 - A simple example 43 | 44 | Flags: 45 | 46 | -age int 47 | Your age 48 | -awesome 49 | Are you awesome? 50 | -help 51 | Get help on the 'flags' command. 52 | -name string 53 | Your name 54 | ``` 55 | 56 | ### Defining flags using a struct 57 | 58 | It's also possible to add flags using a struct. This is useful as you can easily 59 | add flags to your application without having to define them in multiple places: 60 | 61 | ```go 62 | package main 63 | 64 | import ( 65 | "github.com/leaanthony/clir" 66 | ) 67 | 68 | type Flags struct { 69 | Name string `name:"name" description:"The name of the person"` 70 | Age int `name:"age" description:"The age of the person"` 71 | } 72 | 73 | func main() { 74 | 75 | // Create new cli 76 | cli := clir.NewCli("flagstruct", "An example of subcommands with flag inherence", "v0.0.1") 77 | 78 | // Create an init subcommand with flag inheritance 79 | init := cli.NewSubCommand("create", "Create a person") 80 | person := &Flags{ 81 | Age: 20, 82 | } 83 | init.AddFlags(person) 84 | init.Action(func() error { 85 | println("Name:", person.Name, "Age:", person.Age) 86 | return nil 87 | }) 88 | 89 | // Run! 90 | if err := cli.Run(); err != nil { 91 | panic(err) 92 | } 93 | 94 | } 95 | ``` 96 | 97 | ### Defining flags using a struct with default values 98 | 99 | It's also possible to add flags using a struct with default values. This is useful as you can easily add flags to your application without having to define them in multiple places: 100 | 101 | ```go 102 | package main 103 | 104 | import ( 105 | "github.com/leaanthony/clir" 106 | ) 107 | 108 | type Flags struct { 109 | Name string `name:"name" description:"The name of the person" default:"John"` 110 | Age int `name:"age" description:"The age of the person" default:"20"` 111 | } 112 | 113 | func main() { 114 | 115 | // Create new cli 116 | cli := clir.NewCli("default", "An example of subcommands with flag inheritance", "v0.0.1") 117 | 118 | // Create an init subcommand with flag inheritance 119 | init := cli.NewSubCommand("create", "Create a person") 120 | person := &Flags{} 121 | init.AddFlags(person) 122 | init.Action(func() error { 123 | println("Name:", person.Name, "Age:", person.Age) 124 | return nil 125 | }) 126 | 127 | // Run! 128 | if err := cli.Run(); err != nil { 129 | panic(err) 130 | } 131 | 132 | } 133 | ``` 134 | 135 | ### Defining positional arguments 136 | 137 | It is possible to define positional arguments. These are arguments that are not 138 | flags and are defined in the order they are passed to the application. For 139 | example: 140 | 141 | 142 | ```go 143 | package main 144 | 145 | import ( 146 | "github.com/leaanthony/clir" 147 | ) 148 | 149 | type Flags struct { 150 | Name string `pos:"1" description:"The name of the person" default:"John"` 151 | Age int `pos:"2" description:"The age of the person" default:"20"` 152 | } 153 | 154 | func main() { 155 | 156 | // Create new cli 157 | cli := clir.NewCli("default", "An example of subcommands with positional args", "v0.0.1") 158 | 159 | // Create an init subcommand with flag inheritance 160 | init := cli.NewSubCommand("create", "Create a person") 161 | person := &Flags{} 162 | init.AddFlags(person) 163 | init.Action(func() error { 164 | println("Name:", person.Name, "Age:", person.Age) 165 | return nil 166 | }) 167 | 168 | // Run! 169 | if err := cli.Run("create", "bob", "30"); err != nil { 170 | panic(err) 171 | } 172 | 173 | } 174 | ``` 175 | 176 | ### API 177 | 178 | #### Cli.StringFlag(name string, description string, variable *string) 179 | 180 | The [StringFlag](https://godoc.org/github.com/leaanthony/clir#StringFlag) method defines a string flag for your Clîr application. 181 | 182 | For the example above, you would pass in a name as follows: 183 | 184 | ```shell 185 | > flags -name John 186 | ``` 187 | 188 | 189 | #### Cli.IntFlag(name string, description string, variable *int) 190 | 191 | The [IntFlag](https://godoc.org/github.com/leaanthony/clir#IntFlag) method defines an integer flag for your Clîr application. 192 | 193 | For the example above, you would pass in a value for age as follows: 194 | 195 | ```shell 196 | > flags -age 32 197 | ``` 198 | 199 | 200 | #### Cli.BoolFlag(name string, description string, variable *bool) 201 | 202 | The [BoolFlag](https://godoc.org/github.com/leaanthony/clir#BoolFlag) method defines a boolean flag for your Clîr application. 203 | 204 | For the example above, you would you were awesome by simply passing in the flag: 205 | 206 | ```shell 207 | > flags -awesome 208 | ``` 209 | 210 | #### Cli.AddFlags(config interface{}) 211 | 212 | The [AddFlags](https://godoc.org/github.com/leaanthony/clir#AddFlags) method defines flags for your Clîr application 213 | using a struct. It uses the `name` and `description` tags to define the flag name and description. 214 | If no `name` tag is given, the field name is used. If no `description` tag is given, the description will be blank. 215 | A struct pointer must be passed in otherwise the method will panic. -------------------------------------------------------------------------------- /website/docs/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction" 3 | date: 2019-11-21T16:15:09+08:00 4 | draft: false 5 | weight: 20 6 | --- 7 | 8 | # Introduction 9 | 10 | In this guide we will walk through the various parts of Clîr. We will cover: 11 | 12 | * The basic layout of a Clîr application 13 | * Adding flags to your app 14 | * Define actions that get run 15 | * Create SubCommands for additional functionality 16 | * Define a custom banner -------------------------------------------------------------------------------- /website/docs/guide/otherargs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Other Args" 3 | --- 4 | 5 | # Other Args 6 | 7 | Other arguments passed to your application are accessible within actions using `cli.OtherArgs()`: 8 | 9 | ```go 10 | package main 11 | 12 | import ( 13 | "fmt" 14 | 15 | "github.com/leaanthony/clir" 16 | ) 17 | 18 | func main() { 19 | 20 | // Create new cli 21 | cli := clir.NewCli("Other Args", "Access other arguments", "v0.0.1") 22 | 23 | // Set long description 24 | cli.LongDescription("This app shows how to access non-flag arguments") 25 | 26 | // Name 27 | var name string 28 | cli.StringFlag("name", "Your name", &name) 29 | 30 | // Define action 31 | cli.Action(func() error { 32 | println("Your name is", name) 33 | fmt.Printf("The remaining arguments were: %+v\n", cli.OtherArgs()) 34 | return nil 35 | }) 36 | 37 | // Run! 38 | cli.Run() 39 | 40 | } 41 | ``` 42 | 43 | Running this command prints the following: 44 | 45 | ```shell 46 | $ ./otherargs -name test other args 47 | Your name is test 48 | The remaining arguments were: [other args] 49 | ``` 50 | 51 | **Cli.OtherArgs() []string** 52 | 53 | The [OtherArgs](https://godoc.org/github.com/leaanthony/clir#Cli.OtherArgs) method returns all arguments to the application that are not handled by the defined flags. *NOTE*: This will only return correct values if accessed in an Action. 54 | -------------------------------------------------------------------------------- /website/docs/guide/prepostrun.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Pre/Post run" 3 | date: 2023-09-23T16:15:09+08:00 4 | draft: false 5 | weight: 100 6 | chapter: false 7 | --- 8 | 9 | The PreRun and PostRun methods allow you to specify custom functions that should be executed before and after running a command, respectively. These functions can be used for tasks such as setup, cleanup, or any other actions you want to perform before or after executing a command. 10 | 11 | PreRun 12 | 13 | The PreRun method is used to specify a function that should run before executing a command. The function you pass to PreRun takes a *Cli parameter, which represents the CLI application itself. You can use this function to perform any necessary setup or validation before running the command. 14 | 15 | PostRun 16 | The PostRun method is used to specify a function that should run after executing a command. Similar to PreRun, the function you pass to PostRun takes a *Cli parameter. You can use this function to perform any cleanup or post-processing tasks after the command execution. 17 | 18 | Example of postRun 19 | ```go 20 | func main() { 21 | cli := clir.NewCli("MyApp", "My CLI application") 22 | 23 | // Define a command 24 | cmd := cli.NewSubCommand("mycommand", "Description of mycommand") 25 | 26 | // Add flags, actions, and other configurations for the command 27 | 28 | // Define a PostRun function for the command 29 | cmd.PostRun(func(c *clir.Cli) error { 30 | // Perform cleanup or post-processing here 31 | fmt.Println("Running PostRun for 'mycommand'") 32 | return nil // Return an error if something goes wrong 33 | }) 34 | 35 | // Run the CLI application 36 | if err := cli.Run(os.Args[1:]...); err != nil { 37 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 38 | os.Exit(1) 39 | } 40 | } 41 | 42 | ``` -------------------------------------------------------------------------------- /website/docs/guide/subcommands.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "SubCommands" 3 | --- 4 | 5 | SubCommands allow you to add child commands to your main cli app. SubCommands may be nested so there's no limit to how you can structure your app. 6 | 7 | ### Creating Subcommands for your app 8 | 9 | ```go 10 | package main 11 | 12 | import ( 13 | "fmt" 14 | "log" 15 | 16 | "github.com/leaanthony/clir" 17 | ) 18 | 19 | func main() { 20 | 21 | // Create the application 22 | cli := clir.NewCli("SubCommand", "A simple example", "v0.0.1") 23 | 24 | // Create subcommand 25 | initCmd := cli.NewSubCommand("init", "Initialise a new Project") 26 | 27 | // Define action for the command 28 | initCmd.Action(func() error { 29 | fmt.Println("Initialising Project!") 30 | return nil 31 | }) 32 | 33 | // Run the application 34 | err := cli.Run() 35 | if err != nil { 36 | // We had an error 37 | log.Fatal(err) 38 | } 39 | 40 | } 41 | ``` 42 | 43 | Running this command will display the default help: 44 | 45 | ```shell 46 | > subcommand 47 | SubCommand v0.0.1 - A simple example 48 | 49 | Available commands: 50 | 51 | init Initialise a new Project 52 | 53 | Flags: 54 | 55 | -help 56 | Get help on the 'subcommand' command. 57 | ``` 58 | 59 | If we run `subcommand init`, we will get our output: 60 | 61 | ```shell 62 | > subcommand init 63 | Initialising Project! 64 | ``` 65 | 66 | Running `subcommand init -help` will give us the help message for that subcommand. 67 | 68 | ```shell 69 | > subcommand init --help 70 | SubCommand v0.0.1 - A simple example 71 | 72 | SubCommand init - Initialise a new Project 73 | Flags: 74 | 75 | -help 76 | Get help on the 'subcommand init' command. 77 | ``` 78 | 79 | If you wish to add more information to help messages, use the `Command.LongDescription()` method: 80 | 81 | ```go 82 | package main 83 | 84 | import ( 85 | "fmt" 86 | "log" 87 | 88 | "github.com/leaanthony/clir" 89 | ) 90 | 91 | func main() { 92 | 93 | // Create the application 94 | cli := clir.NewCli("SubCommand", "A simple example", "v0.0.1") 95 | 96 | // Create subcommand 97 | initCmd := cli.NewSubCommand("init", "Initialise a new Project") 98 | 99 | // More help 100 | initCmd.LongDescription("The init command initialises a new project in the current working directory.") 101 | 102 | // Define action for the command 103 | initCmd.Action(func() error { 104 | fmt.Println("Initialising Project!") 105 | return nil 106 | }) 107 | 108 | // Run the application 109 | err := cli.Run() 110 | if err != nil { 111 | // We had an error 112 | log.Fatal(err) 113 | } 114 | 115 | } 116 | ``` 117 | Then when we pass `-help`, we get more information: 118 | 119 | ```shell 120 | > subcommand init --help 121 | SubCommand v0.0.1 - A simple example 122 | 123 | SubCommand init - Initialise a new Project 124 | The init command initialises a new project in the current working directory. 125 | 126 | Flags: 127 | 128 | -help 129 | Get help on the 'subcommand init' command. 130 | ``` 131 | 132 | You can add as many subcommands as you like. You can even nest them! 133 | 134 | ### Nested SubCommands 135 | 136 | As Commands have (basically) the same API as the Cli object, we can do everything we did in the previous pages: Add Flags, Actions and SubCommands to any Command: 137 | 138 | ```go 139 | package main 140 | 141 | import ( 142 | "fmt" 143 | "log" 144 | 145 | "github.com/leaanthony/clir" 146 | ) 147 | 148 | func main() { 149 | 150 | // Create the application 151 | cli := clir.NewCli("SubCommand", "A simple example", "v0.0.1") 152 | 153 | // Create subcommand 154 | initCmd := cli.NewSubCommand("init", "Initialise a component") 155 | 156 | // Create a new "project" command below the "init" command 157 | projectCmd := initCmd.NewSubCommand("project", "Creates a new project") 158 | projectCmd.Action(func() error { 159 | fmt.Println("Initialising Project!") 160 | return nil 161 | }) 162 | 163 | // Run the application 164 | err := cli.Run() 165 | if err != nil { 166 | // We had an error 167 | log.Fatal(err) 168 | } 169 | 170 | } 171 | ``` 172 | 173 | Running `subcommand init` shows the following help: 174 | 175 | ```shell 176 | > subcommand init 177 | SubCommand v0.0.1 - A simple example 178 | 179 | SubCommand init - Initialise a component 180 | Available commands: 181 | 182 | project Creates a new project 183 | 184 | Flags: 185 | 186 | -help 187 | Get help on the 'subcommand init' command. 188 | 189 | ``` 190 | 191 | Whilst running `subcommand init project` will call the project command action: 192 | 193 | ```shell 194 | > subcommand init project 195 | Initialising Project! 196 | ``` 197 | 198 | ### Adding SubCommands 199 | 200 | It's possible to define SubCommands in isolation, then add them to a command later. To do this, we use the `Command.AddCommand` method: 201 | 202 | ```go 203 | package main 204 | 205 | import ( 206 | "fmt" 207 | "log" 208 | 209 | "github.com/leaanthony/clir" 210 | ) 211 | 212 | func newProjectCommand() *clir.Command { 213 | // Create a new Command 214 | result := clir.NewCommand("project", "Creates a new project") 215 | 216 | // Define the Action 217 | result.Action(func() error { 218 | fmt.Println("Initialising Project!") 219 | return nil 220 | }) 221 | 222 | return result 223 | } 224 | 225 | func main() { 226 | 227 | // Create the application 228 | cli := clir.NewCli("SubCommand", "A simple example", "v0.0.1") 229 | 230 | // Create subcommand 231 | initCmd := cli.NewSubCommand("init", "Initialise a component") 232 | 233 | // Create a new "project" command and add it to the "init" command 234 | initCmd.AddCommand(newProjectCommand()) 235 | 236 | // Run the application 237 | err := cli.Run() 238 | if err != nil { 239 | // We had an error 240 | log.Fatal(err) 241 | } 242 | 243 | } 244 | ``` 245 | 246 | This really helps with better code organisation. 247 | 248 | ### Adding Subcommands with Functions 249 | 250 | You can also add SubCommands using a function: 251 | 252 | ```go 253 | type AppFlags struct { 254 | Name string `name:"name" description:"The name of the person" default:"Bob"` 255 | Age int `name:"age" description:"The age of the person" default:"20"` 256 | } 257 | 258 | func main() { 259 | // Create new cli 260 | cli := clir.NewCli("Flags", "An example of using flags", "v0.0.1") 261 | 262 | cli.NewSubCommandFunction("create", "Create a new person", createPerson) 263 | cli.Run() 264 | } 265 | 266 | func createPerson(flags *AppFlags) error { 267 | fmt.Printf("%+v\n", flags) 268 | return nil 269 | } 270 | ``` 271 | 272 | The NewSubCommandFunction method takes a function that takes a pointer to the flags struct and returns an error. 273 | The function is called when the command is run and passes in the flags struct with the values set from the command line. 274 | 275 | ### Inheriting Flags 276 | 277 | The `NewSubCommandInheritFlags` method will create a subcommand in the usual way but will inherit all previously defined flags in the parent. 278 | 279 | ### Hidden SubCommands 280 | 281 | It's possible to hide subcommands by calling the `Hidden` method. This will omit the subcommand from any help text. 282 | 283 | ```go 284 | package main 285 | 286 | import ( 287 | "fmt" 288 | "log" 289 | 290 | "github.com/leaanthony/clir" 291 | ) 292 | 293 | func main() { 294 | 295 | // Create the application 296 | cli := clir.NewCli("SubCommand", "A simple example", "v0.0.1") 297 | 298 | // Create subcommand 299 | initCmd := cli.NewSubCommand("init", "Initialise a component") 300 | initCmd.Action(func() error { 301 | fmt.Println("Initialising") 302 | return nil 303 | }) 304 | 305 | // Create a hidden developer command 306 | devtoolsCommand := cli.NewSubCommand("dev", "Developer tools") 307 | devtoolsCommand.Action(func() error { 308 | fmt.Println("I'm a secret command") 309 | return nil 310 | }) 311 | devtoolsCommand.Hidden() 312 | 313 | // Run the application 314 | err := cli.Run() 315 | if err != nil { 316 | // We had an error 317 | log.Fatal(err) 318 | } 319 | 320 | } 321 | ``` 322 | 323 | The main help hides the command, but it is still possible to run it: 324 | 325 | ``` 326 | > subcommand 327 | SubCommand v0.0.1 - A simple example 328 | 329 | Available commands: 330 | 331 | init Initialise a component 332 | 333 | Flags: 334 | 335 | -help 336 | Get help on the 'subcommand' command. 337 | 338 | 339 | > subcommand dev 340 | I'm a secret command 341 | ``` 342 | 343 | ### Default Command 344 | 345 | If you would like the default action of your application to be a particular subcommand, you can set it using the `DefaultCommand` method: 346 | 347 | ```go 348 | package main 349 | 350 | import ( 351 | "fmt" 352 | "log" 353 | 354 | "github.com/leaanthony/clir" 355 | ) 356 | 357 | func main() { 358 | 359 | // Create the application 360 | cli := clir.NewCli("SubCommand", "A simple example", "v0.0.1") 361 | 362 | // Create subcommand 363 | initCmd := cli.NewSubCommand("init", "Initialise a component") 364 | initCmd.Action(func() error { 365 | fmt.Println("Initialising") 366 | return nil 367 | }) 368 | 369 | // Make init the default command 370 | cli.DefaultCommand(initCmd) 371 | 372 | // Run the application 373 | err := cli.Run() 374 | if err != nil { 375 | // We had an error 376 | log.Fatal(err) 377 | } 378 | 379 | } 380 | ``` 381 | 382 | Now when we run the application with no parameters, it will run the init subcommand: 383 | 384 | ```shell 385 | > subcommand 386 | Initialising 387 | ``` 388 | 389 | The help text also indicates that there is a default option: 390 | 391 | ``` 392 | > subcommand -help 393 | SubCommand v0.0.1 - A simple example 394 | 395 | Available commands: 396 | 397 | init Initialise a component [default] 398 | 399 | Flags: 400 | 401 | -help 402 | Get help on the 'subcommand' command. 403 | ``` 404 | 405 | ### API 406 | 407 | **NewCommand(name string, description string) *Command** 408 | 409 | Creates a new Command in isolation. It may be attached to the application through `AddCommand(cmd *Command)`. The returned Command may be further configured. 410 | 411 | **AddCommand(cmd *Command)** 412 | 413 | Attaches the given Command as a SubCommand. This API is valid for both Cli and Command. 414 | 415 | **Hidden()** 416 | 417 | Hides the command from help message. 418 | -------------------------------------------------------------------------------- /website/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Home" 3 | --- 4 | 10 | 11 |

12 | 13 | 14 |

15 | 16 |

17 | A Simple and Clear CLI library. Dependency free.
18 |

19 | 20 |
21 | 22 | 23 | Awesome 24 | 25 | CodeFactor 26 | 27 | 28 | 29 | 30 |
31 | 32 | ## Features 33 | 34 | * Nested Subcommands 35 | * Uses the standard library `flag` package 36 | * Auto-generated help 37 | * Custom banners 38 | * Hidden Subcommands 39 | * Default Subcommand 40 | * Dependency free 41 | 42 | ## Example 43 | 44 | ```go 45 | package main 46 | 47 | import ( 48 | "fmt" 49 | "log" 50 | 51 | "github.com/leaanthony/clir" 52 | ) 53 | 54 | func main() { 55 | 56 | // Create new cli 57 | cli := clir.NewCli("Flags", "A simple example", "v0.0.1") 58 | 59 | // Name 60 | name := "Anonymous" 61 | cli.StringFlag("name", "Your name", &name) 62 | 63 | // Define action for the command 64 | cli.Action(func() error { 65 | fmt.Printf("Hello %s!\n", name) 66 | return nil 67 | }) 68 | 69 | // Run the application 70 | err := cli.Run() 71 | if err != nil { 72 | // We had an error 73 | log.Fatal(err) 74 | } 75 | 76 | } 77 | ``` 78 | 79 | ## License Status 80 | 81 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fleaanthony%2Fclir.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fleaanthony%2Fclir?ref=badge_large) 82 | -------------------------------------------------------------------------------- /website/docs/static/clir_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leaanthony/clir/a8d9462bce754819cbd5a5596f22d68b3f318fb6/website/docs/static/clir_logo.png -------------------------------------------------------------------------------- /website/docs/static/clir_logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leaanthony/clir/a8d9462bce754819cbd5a5596f22d68b3f318fb6/website/docs/static/clir_logo_white.png -------------------------------------------------------------------------------- /website/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Clîr 2 | 3 | theme: 4 | name: material 5 | 6 | palette: 7 | # Palette toggle for light mode 8 | - media: "(prefers-color-scheme: light)" 9 | scheme: default 10 | toggle: 11 | icon: material/brightness-4 12 | name: Switch to dark mode 13 | 14 | # Palette toggle for dark mode 15 | - media: "(prefers-color-scheme: dark)" 16 | scheme: slate 17 | toggle: 18 | icon: material/brightness-7 19 | name: Switch to light mode 20 | 21 | features: 22 | - navigation.footer 23 | - content.action.edit 24 | - navigation.sections 25 | - content.code.copy 26 | 27 | icon: 28 | edit: material/pencil 29 | logo: material/console-line 30 | 31 | extra_css: 32 | - static/css/extra.css 33 | 34 | extra: 35 | homepage: https://clir.leaanthony.com 36 | version: 37 | provider: mike 38 | 39 | copyright: Copyright © 2020-Present Lea Anthony 40 | repo_url: https://github.com/leaanthony/clir 41 | 42 | markdown_extensions: 43 | - attr_list 44 | - md_in_html 45 | - pymdownx.highlight: 46 | anchor_linenums: true 47 | - pymdownx.inlinehilite 48 | - pymdownx.snippets 49 | - pymdownx.superfences 50 | 51 | nav: 52 | - index.md 53 | - gettingstarted.md 54 | - "Guide": 55 | - guide/index.md 56 | - guide/cli.md 57 | - guide/flags.md 58 | - guide/otherargs.md 59 | - guide/actions.md 60 | - guide/subcommands.md 61 | - guide/custombanner.md 62 | - Examples: 63 | - examples/basic.md 64 | - examples/chained.md 65 | - examples/custom-banner.md 66 | - examples/custom-flag-error.md 67 | - examples/default.md 68 | - examples/flags.md 69 | - examples/flags-compact.md 70 | - examples/flags-function.md 71 | - examples/flags-positional.md 72 | - examples/flags-slice.md 73 | - examples/flagstruct.md 74 | - examples/hidden.md 75 | - examples/nested-subcommands.md 76 | - examples/otherargs.md 77 | - examples/subcommandinheritflags.md 78 | - examples/subcommands.md 79 | - faq.md 80 | -------------------------------------------------------------------------------- /website/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material==9.0.6 2 | mkdocs-material-extensions==1.1.1 3 | --------------------------------------------------------------------------------