This repository has been archived by the owner on Jul 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
214 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,17 @@ | ||
# Go CLI Library | ||
|
||
cli is a library for implementing sub-command based command-line interfaces | ||
in Go. cli is the library that powers the CLI for | ||
cli is a library for implementing powerful command-line interfaces in Go. | ||
cli is the library that powers the CLI for | ||
[Packer](https://github.com/mitchellh/packer) and | ||
[Serf](https://github.com/hashicorp/serf). | ||
|
||
## Features | ||
|
||
* Easy sub-command based CLIs: `cli foo`, `cli bar`, etc. | ||
|
||
* Automatic help generation for listing subcommands | ||
|
||
* Automatic help flag recognition of `-h`, `--help`, etc. | ||
|
||
* Use of Go interfaces/types makes augmenting various parts of the library a | ||
piece of cake. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log" | ||
) | ||
|
||
// Ui is an interface for interacting with the terminal, or "interface" | ||
// of a CLI. This abstraction doesn't have to be used, but helps provide | ||
// a simple, layerable way to manage user interactions. | ||
type Ui interface { | ||
// Output is called for normal standard output. | ||
Output(string) | ||
|
||
// Info is called for information related to the previous output. | ||
// In general this may be the exact same as Output, but this gives | ||
// Ui implementors some flexibility with output formats. | ||
Info(string) | ||
|
||
// Error is used for any error messages that might appear on standard | ||
// error. | ||
Error(string) | ||
} | ||
|
||
// BasicUi is an implementation of Ui that just outputs to the given | ||
// writer. | ||
type BasicUi struct { | ||
Writer io.Writer | ||
} | ||
|
||
func (u *BasicUi) Error(message string) { | ||
fmt.Fprint(u.Writer, message) | ||
fmt.Fprint(u.Writer, "\n") | ||
} | ||
|
||
func (u *BasicUi) Info(message string) { | ||
u.Output(message) | ||
} | ||
|
||
func (u *BasicUi) Output(message string) { | ||
fmt.Fprint(u.Writer, message) | ||
fmt.Fprint(u.Writer, "\n") | ||
} | ||
|
||
// PrefixedUi is an implementation of Ui that prefixes messages. | ||
type PrefixedUi struct { | ||
OutputPrefix string | ||
InfoPrefix string | ||
ErrorPrefix string | ||
Ui Ui | ||
} | ||
|
||
func (u *PrefixedUi) Error(message string) { | ||
if message != "" { | ||
message = fmt.Sprintf("%s%s", u.ErrorPrefix, message) | ||
} | ||
|
||
u.Ui.Error(message) | ||
} | ||
|
||
func (u *PrefixedUi) Info(message string) { | ||
if message != "" { | ||
message = fmt.Sprintf("%s%s", u.InfoPrefix, message) | ||
} | ||
|
||
u.Ui.Info(message) | ||
} | ||
|
||
func (u *PrefixedUi) Output(message string) { | ||
if message != "" { | ||
message = fmt.Sprintf("%s%s", u.OutputPrefix, message) | ||
} | ||
|
||
u.Ui.Output(message) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package cli | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"sync" | ||
) | ||
|
||
// MockUi is a mock UI that is used for tests and is exported publicly for | ||
// use in external tests if needed as well. | ||
type MockUi struct { | ||
ErrorWriter *bytes.Buffer | ||
OutputWriter *bytes.Buffer | ||
|
||
once sync.Once | ||
} | ||
|
||
func (u *MockUi) Error(message string) { | ||
u.once.Do(u.init) | ||
|
||
fmt.Fprint(u.ErrorWriter, message) | ||
fmt.Fprint(u.ErrorWriter, "\n") | ||
} | ||
|
||
func (u *MockUi) Info(message string) { | ||
u.Output(message) | ||
} | ||
|
||
func (u *MockUi) Output(message string) { | ||
u.once.Do(u.init) | ||
|
||
fmt.Fprint(u.OutputWriter, message) | ||
fmt.Fprint(u.OutputWriter, "\n") | ||
} | ||
|
||
func (u *MockUi) init() { | ||
u.ErrorWriter = new(bytes.Buffer) | ||
u.OutputWriter = new(bytes.Buffer) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package cli | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestMockUi_implements(t *testing.T) { | ||
var _ Ui = new(MockUi) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package cli | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
) | ||
|
||
func TestBasicUi_implements(t *testing.T) { | ||
var _ Ui = new(BasicUi) | ||
} | ||
|
||
func TestBasicUi_Error(t *testing.T) { | ||
writer := new(bytes.Buffer) | ||
ui := &BasicUi{Writer: writer} | ||
ui.Error("HELLO") | ||
|
||
if writer.String() != "HELLO\n" { | ||
t.Fatalf("bad: %s", writer.String()) | ||
} | ||
} | ||
|
||
func TestBasicUi_Output(t *testing.T) { | ||
writer := new(bytes.Buffer) | ||
ui := &BasicUi{Writer: writer} | ||
ui.Output("HELLO") | ||
|
||
if writer.String() != "HELLO\n" { | ||
t.Fatalf("bad: %s", writer.String()) | ||
} | ||
} | ||
|
||
func TestPrefixedUi_implements(t *testing.T) { | ||
var raw interface{} | ||
raw = &PrefixedUi{} | ||
if _, ok := raw.(Ui); !ok { | ||
t.Fatalf("should be a Ui") | ||
} | ||
} | ||
|
||
func TestPrefixedUiError(t *testing.T) { | ||
ui := new(MockUi) | ||
p := &PrefixedUi{ | ||
ErrorPrefix: "foo", | ||
Ui: ui, | ||
} | ||
|
||
p.Error("bar") | ||
if ui.ErrorWriter.String() != "foobar\n" { | ||
t.Fatalf("bad: %s", ui.ErrorWriter.String()) | ||
} | ||
} | ||
|
||
func TestPrefixedUiInfo(t *testing.T) { | ||
ui := new(MockUi) | ||
p := &PrefixedUi{ | ||
InfoPrefix: "foo", | ||
Ui: ui, | ||
} | ||
|
||
p.Info("bar") | ||
if ui.OutputWriter.String() != "foobar\n" { | ||
t.Fatalf("bad: %s", ui.OutputWriter.String()) | ||
} | ||
} | ||
|
||
func TestPrefixedUiOutput(t *testing.T) { | ||
ui := new(MockUi) | ||
p := &PrefixedUi{ | ||
OutputPrefix: "foo", | ||
Ui: ui, | ||
} | ||
|
||
p.Output("bar") | ||
if ui.OutputWriter.String() != "foobar\n" { | ||
t.Fatalf("bad: %s", ui.OutputWriter.String()) | ||
} | ||
} |