diff --git a/TODO.md b/TODO.md index ec4279e..3afedbb 100644 --- a/TODO.md +++ b/TODO.md @@ -16,6 +16,6 @@ Things I want to do to make this library as good as it can be and a better, simp - [ ] Write a package example doc - [ ] Make the help output as pretty as possible, see [clap] for inspiration as their help is so nice - [x] Implement something nice to do the whole `-- ` thing, maybe `ExtraArgs`? -- [ ] Thin wrapper around tabwriter to keep it consistent +- [x] Thin wrapper around tabwriter to keep it consistent [clap]: https://github.com/clap-rs/clap diff --git a/command.go b/command.go index f1e621c..19013c0 100644 --- a/command.go +++ b/command.go @@ -8,18 +8,10 @@ import ( "os" "slices" "strings" - "text/tabwriter" "unicode/utf8" "github.com/FollowTheProcess/cli/internal/flag" -) - -// TableWriter config, used for showing subcommands in help. -const ( - minWidth = 2 // Min cell width - tabWidth = 8 // Tab width in spaces - padding = 1 // Padding - padChar = '\t' // Char to pad with + "github.com/FollowTheProcess/cli/internal/table" ) // New builds and returns a new [Command]. @@ -474,9 +466,9 @@ func defaultHelp(cmd *Command) error { // Now show subcommands if len(cmd.subcommands) != 0 { s.WriteString("\n\nCommands:\n") - tab := tabwriter.NewWriter(s, minWidth, tabWidth, padding, padChar, tabwriter.AlignRight) + tab := table.New(s) for _, subcommand := range cmd.subcommands { - fmt.Fprintf(tab, " %s\t%s\n", subcommand.name, subcommand.short) + tab.Row(" %s\t%s\n", subcommand.name, subcommand.short) } if err := tab.Flush(); err != nil { return fmt.Errorf("could not format subcommands: %w", err) diff --git a/internal/flag/set.go b/internal/flag/set.go index 6c2a7a3..f6768e2 100644 --- a/internal/flag/set.go +++ b/internal/flag/set.go @@ -6,16 +6,9 @@ import ( "fmt" "slices" "strings" - "text/tabwriter" "unicode/utf8" -) -// TableWriter config, used for showing subcommands in help. -const ( - minWidth = 0 // Min cell width - tabWidth = 8 // Tab width in spaces - padding = 1 // Padding - padChar = '\t' // Char to pad with + "github.com/FollowTheProcess/cli/internal/table" ) // Set is a set of command line flags. @@ -197,8 +190,9 @@ func (s *Set) Usage() (string, error) { } slices.Sort(names) - tab := tabwriter.NewWriter(buf, minWidth, tabWidth, padding, padChar, tabwriter.TabIndent) - fmt.Fprintln(tab, "Short\tLong\tType\tDefault\tUsage") + tab := table.New(buf) + tab.Row("Short\tLong\tType\tDefault\tUsage\n") + for _, name := range names { entry := s.flags[name] var shorthand string @@ -215,7 +209,7 @@ func (s *Set) Usage() (string, error) { defaultValue = `""` } - fmt.Fprintf(tab, "%s\t--%s\t%s\t%s\t%s\n", shorthand, entry.Name, entry.Value.Type(), defaultValue, entry.Usage) + tab.Row("%s\t--%s\t%s\t%s\t%s\n", shorthand, entry.Name, entry.Value.Type(), defaultValue, entry.Usage) } if err := tab.Flush(); err != nil { diff --git a/internal/table/table.go b/internal/table/table.go new file mode 100644 index 0000000..950ae36 --- /dev/null +++ b/internal/table/table.go @@ -0,0 +1,38 @@ +// Package table implements a thin wrapper around [text/tabwriter] to keep +// formatting consistent across cli. +package table + +import ( + "fmt" + "io" + "text/tabwriter" +) + +// TableWriter config, used for showing subcommands in help. +const ( + minWidth = 2 // Min cell width + tabWidth = 8 // Tab width in spaces + padding = 1 // Padding + padChar = '\t' // Char to pad with +) + +// Table is a text table. +type Table struct { + tw *tabwriter.Writer // The underlying writer +} + +// New returns a new [Table], writing to w. +func New(w io.Writer) Table { + tw := tabwriter.NewWriter(w, minWidth, tabWidth, padding, padChar, tabwriter.TabIndent) + return Table{tw: tw} +} + +// Row adds a row to the [Table]. +func (t Table) Row(format string, a ...any) { + fmt.Fprintf(t.tw, format, a...) +} + +// Flush flushes the written data to the writer. +func (t Table) Flush() error { + return t.tw.Flush() +} diff --git a/internal/table/table_test.go b/internal/table/table_test.go new file mode 100644 index 0000000..33fc217 --- /dev/null +++ b/internal/table/table_test.go @@ -0,0 +1,45 @@ +package table_test + +import ( + "bytes" + "flag" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/FollowTheProcess/cli/internal/table" + "github.com/FollowTheProcess/test" +) + +var ( + debug = flag.Bool("debug", false, "Print debug output during tests") + update = flag.Bool("update", false, "Update golden files") +) + +func TestTable(t *testing.T) { + buf := &bytes.Buffer{} + + tab := table.New(buf) + + tab.Row("Col1\tCol2\tCol3\n") + tab.Row("val1\tval2\tval3\n") + tab.Row("val4\tval5\tval6\n") + + err := tab.Flush() + test.Ok(t, err) + + file := filepath.Join(test.Data(t), "table.txt") + + if *debug { + fmt.Printf("DEBUG (%s)\n_____\n\n%s\n", "TestTable", buf.String()) + } + + if *update { + t.Logf("Updating %s\n", file) + err := os.WriteFile(file, buf.Bytes(), os.ModePerm) + test.Ok(t, err) + } + + test.File(t, buf.String(), "table.txt") +} diff --git a/internal/table/testdata/table.txt b/internal/table/testdata/table.txt new file mode 100644 index 0000000..5e54a4c --- /dev/null +++ b/internal/table/testdata/table.txt @@ -0,0 +1,3 @@ +Col1 Col2 Col3 +val1 val2 val3 +val4 val5 val6