diff --git a/clitable/README.adoc b/clitable/README.adoc index 874a9dd..cc56c8f 100644 --- a/clitable/README.adoc +++ b/clitable/README.adoc @@ -6,7 +6,9 @@ Pretty print Data as tables on the command line. The csvtable cli tool allows printing CSV data as a table: - go install github.com/DavidGamba/dgtools/clitable/cmd/csvtable + go install github.com/DavidGamba/dgtools/clitable/cmd/csvtable@v0.3.0 + +To parse TSV data use the `--tsv` flag. == Library @@ -41,6 +43,14 @@ r := strings.NewReader("Hello,1\nWorld,2\n") clitable.NewTablePrinter().FprintCSVReader(os.Stdout, r) ---- +For TSV from an `io.Reader`: + +[source, go] +---- +r := strings.NewReader("Hello 1\nWorld 2\n") +clitable.NewTablePrinter().Separator('\t').FprintCSVReader(os.Stdout, r) +---- + ---- ┌───────┬───┐ │ Hello │ 1 │ diff --git a/clitable/changelog.adoc b/clitable/changelog.adoc index 173363d..094c515 100644 --- a/clitable/changelog.adoc +++ b/clitable/changelog.adoc @@ -1,6 +1,10 @@ = Changelog :toc: +== v0.3.0: New feature + +* Add support for TSV data. + == v0.2.0: New feature * Add CSV Printer diff --git a/clitable/clitable.go b/clitable/clitable.go index 62770e3..8671034 100644 --- a/clitable/clitable.go +++ b/clitable/clitable.go @@ -34,6 +34,7 @@ var Logger = log.New(ioutil.Discard, "clitable DEBUG ", log.LstdFlags) type TablePrinter struct { tableConfig tableConfig + separator rune } type tableConfig struct { @@ -91,6 +92,11 @@ func (tp *TablePrinter) HasHeader(b bool) *TablePrinter { return tp } +func (tp *TablePrinter) Separator(c rune) *TablePrinter { + tp.separator = c + return tp +} + func (tp *TablePrinter) SetStyle(s Style) *TablePrinter { tp.tableConfig.Style = s @@ -207,13 +213,19 @@ func (tp *TablePrinter) FprintCSVReader(w io.Writer, r io.Reader) error { // TODO: I am sure I can make this simpler and maybe even not necessary var readerCopy bytes.Buffer reader := io.TeeReader(r, &readerCopy) - t := CSVTable{reader} + t := CSVTable{ + Reader: reader, + Separator: tp.separator, + } tableInfo, err := GetTableInfo(t) if err != nil { return err } Logger.Printf("tableInfo: %s\n", tableInfo) - t = CSVTable{&readerCopy} + t = CSVTable{ + Reader: &readerCopy, + Separator: tp.separator, + } return tp.fprint(os.Stdout, t, tableInfo) } diff --git a/clitable/cmd/csvtable/main.go b/clitable/cmd/csvtable/main.go index 3222c1b..91183b4 100644 --- a/clitable/cmd/csvtable/main.go +++ b/clitable/cmd/csvtable/main.go @@ -9,11 +9,10 @@ /* Package csvtable provides a tool to view csv files on the cmdline. - ┌──┬──┐ - │ │ │ - ├──┼──┤ - └──┴──┘ - + ┌──┬──┐ + │ │ │ + ├──┼──┤ + └──┴──┘ */ package main @@ -23,16 +22,13 @@ import ( "io/ioutil" "log" "os" + "runtime/debug" + "time" "github.com/DavidGamba/dgtools/clitable" "github.com/DavidGamba/go-getoptions" ) -// BuildMetadata - Provides the metadata part of the version information. -// -// go build -ldflags="-X main.BuildMetadata=`date +'%Y%m%d%H%M%S'`.`git rev-parse --short HEAD`" -var BuildMetadata = "dev" - const semVersion = "0.3.0" var logger = log.New(ioutil.Discard, "main DEBUG ", log.LstdFlags) @@ -52,6 +48,7 @@ func main() { opt.Bool("help", false, opt.Alias("?")) opt.Bool("debug", false) opt.Bool("version", false, opt.Alias("V")) + opt.Bool("tsv", false) header := opt.Bool("no-header", true) opt.HelpSynopsisArgs("") remaining, err := opt.Parse(os.Args[1:]) @@ -65,7 +62,12 @@ func main() { os.Exit(1) } if opt.Called("version") { - fmt.Printf("Version: %s+%s\n", semVersion, BuildMetadata) + v, err := version(semVersion) + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) + os.Exit(1) + } + fmt.Printf("Version: %s\n", v) os.Exit(0) } if opt.Called("debug") { @@ -98,9 +100,44 @@ func main() { defer fh.Close() reader = fh } - err = clitable.NewTablePrinter().HasHeader(*header).FprintCSVReader(os.Stdout, reader) + tp := clitable.NewTablePrinter() + if opt.Called("tsv") { + tp.Separator('\t') + } + err = tp.HasHeader(*header).FprintCSVReader(os.Stdout, reader) if err != nil { fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) os.Exit(1) } } + +func version(semVersion string) (string, error) { + var revision, timeStr, modified string + info, ok := debug.ReadBuildInfo() + if ok { + for _, s := range info.Settings { + switch s.Key { + case "vcs.revision": + revision = s.Value + case "vcs.time": + vcsTime := s.Value + date, err := time.Parse("2006-01-02T15:04:05Z", vcsTime) + if err != nil { + return "", fmt.Errorf("failed to parse time: %w", err) + } + timeStr = date.Format("20060102_150405") + case "vcs.modified": + if s.Value == "true" { + modified = "modified" + } + } + } + } + if revision != "" && timeStr != "" { + semVersion += fmt.Sprintf("+%s.%s", revision, timeStr) + if modified != "" { + semVersion += fmt.Sprintf(".%s", modified) + } + } + return semVersion, nil +} diff --git a/clitable/table.go b/clitable/table.go index 3361229..b185a65 100644 --- a/clitable/table.go +++ b/clitable/table.go @@ -48,19 +48,23 @@ func SimpleRowIterator(data [][]string) <-chan Row { // CSVTable - Implements the table interface from an io.Reader to CSV data. type CSVTable struct { - Reader io.Reader + Reader io.Reader + Separator rune } // RowIterator - Implements the Table interface. func (t CSVTable) RowIterator() <-chan Row { - return CSVRowIterator(t.Reader) + return CSVRowIterator(t.Reader, t.Separator) } // CSVRowIterator - -func CSVRowIterator(reader io.Reader) <-chan Row { +func CSVRowIterator(reader io.Reader, separator rune) <-chan Row { c := make(chan Row) go func() { r := csv.NewReader(reader) + if separator != 0 { + r.Comma = separator + } r.FieldsPerRecord = -1 for { record, err := r.Read()