Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a very basic Command struct #1

Merged
merged 1 commit into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,47 @@ tasks:
default:
desc: List all available tasks
silent: true
cmd: task --list
cmds:
- task --list

tidy:
desc: Tidy dependencies in go.mod and go.sum
cmd: go mod tidy
cmds:
- go mod tidy

fmt:
desc: Run go fmt on all source files
cmd: go fmt ./...
preconditions:
- sh: command -v golines
msg: golines not installed, see https://github.com/segmentio/golines
cmds:
- go fmt ./...
- golines . --chain-split-dots --ignore-generated --write-output

test:
desc: Run the test suite
cmd: go test -race ./... {{ .CLI_ARGS }}
cmds:
- go test -race ./... {{ .CLI_ARGS }}

bench:
desc: Run all project benchmarks
cmd: go test ./... -run None -benchmem -bench . {{ .CLI_ARGS }}
cmds:
- go test ./... -run None -benchmem -bench . {{ .CLI_ARGS }}

lint:
desc: Run the linters and auto-fix if possible
cmd: golangci-lint run --fix
deps:
- fmt
cmds:
- golangci-lint run --fix
preconditions:
- sh: command -v golangci-lint
msg: golangci-lint not installed, see https://golangci-lint.run/usage/install/#local-installation

doc:
desc: Render the pkg docs locally
cmd: pkgsite -open
cmds:
- pkgsite -open
preconditions:
- sh: command -v pkgsite
msg: pkgsite not installed, run go install golang.org/x/pkgsite/cmd/pkgsite@latest
Expand All @@ -57,7 +70,8 @@ tasks:

sloc:
desc: Print lines of code
cmd: fd . -e go | xargs wc -l | sort -nr | head
cmds:
- fd . -e go | xargs wc -l | sort -nr | head

clean:
desc: Remove build artifacts and other clutter
Expand Down
7 changes: 0 additions & 7 deletions cli.go

This file was deleted.

16 changes: 0 additions & 16 deletions cli_test.go

This file was deleted.

68 changes: 68 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Package cli provides a tiny, simple and minimalistic CLI framework for building Go CLI tools.
package cli

import (
"fmt"
"io"

"github.com/spf13/pflag"
)

// Command represents a CLI command.
type Command struct {
// Run is the function actually implementing the command, the command and arguments to it, are passed into the function, flags
// are parsed out before the arguments are passed to Run, so `args` here are the command line arguments minus flags.
Run func(cmd *Command, args []string) error

// flags is the set of flags for this command.
flags *pflag.FlagSet

// Stdin is an [io.Reader] from which command input is read.
//
// It defaults to [os.Stdin] but can be overridden as desired e.g. for testing.
Stdin io.Reader

// Stdout is an [io.Writer] to which normal command output is written.
//
// It defaults to [os.Stdout] but can be overridden as desired e.g. for testing.
Stdout io.Writer

// Stderr is an [io.Writer] to which error command output is written.
//
// It defaults to [os.Stderr] but can be overridden as desired e.g. for testing.
Stderr io.Writer

// Name is the name of the command.
Name string

// Short is the one line summary for the command, shown inline in the -h/--help output.
Short string

// Long is the long form description for the command, shown when -h/--help is called on the command itself.
Long string

// Example is examples of how to use the command, free form text.
Example string
}

// Execute parses the flags and arguments, and invokes the Command's Run
// function, returning any error.
//
// The arguments should not include the command name.
//
// If the flags fail to parse, an error will be returned and the Run function
// will not be called.
//
// err := cmd.Execute(os.Args[1:])
func (c *Command) Execute(args []string) error {
if c.flags == nil {
c.flags = pflag.NewFlagSet(c.Name, pflag.ExitOnError)
}
if err := c.flags.Parse(args); err != nil {
return fmt.Errorf("failed to parse command flags: %w", err)

Check warning on line 62 in command.go

View check run for this annotation

Codecov / codecov/patch

command.go#L62

Added line #L62 was not covered by tests
}

argsWithoutFlags := c.flags.Args()

return c.Run(c, argsWithoutFlags)
}
33 changes: 33 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cli_test

import (
"bytes"
"fmt"
"testing"

"github.com/FollowTheProcess/cli"
"github.com/FollowTheProcess/test"
)

func TestExecute(t *testing.T) {
stderr := &bytes.Buffer{}
stdout := &bytes.Buffer{}

testCmd := &cli.Command{
Run: func(cmd *cli.Command, args []string) error {
fmt.Fprintf(cmd.Stdout, "Oooh look, it ran, here are some args: %v\n", args)
return nil
},
Stdout: stdout,
Stderr: stderr,
Name: "test",
Short: "A simple test command",
Long: "Much longer description blah blah blah",
}

err := testCmd.Execute([]string{"arg1", "arg2", "arg3"})
test.Ok(t, err)

want := "Oooh look, it ran, here are some args: [arg1 arg2 arg3]\n"
test.Equal(t, stdout.String(), want)
}
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
module github.com/FollowTheProcess/cli

go 1.22
go 1.22

require (
github.com/FollowTheProcess/test v0.9.0
github.com/spf13/pflag v1.0.5
)

require github.com/google/go-cmp v0.6.0 // indirect
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/FollowTheProcess/test v0.9.0 h1:X7/cqUi6qwbB8Z8PGHI5Hk4RJJF/wRZ+3Mko3QlL/VU=
github.com/FollowTheProcess/test v0.9.0/go.mod h1:9oxZcKkTAgz3bZMiHPtYCytdPcvICS+AAp5mzZzB2oA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
Loading