From 975955236fa700dda7823148c9aa80bd1b5d871c Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Fri, 3 Jun 2022 10:29:06 +1000 Subject: [PATCH] Added support for TSV files This uses a similar parser as CSV files, except configured for tabs. Have also fixed the configured parser to ignore column counts. --- README.md | 8 ++++++-- main.go | 22 ++++++++++++++++++++-- modelsource.go | 38 ++++++++++++++++++++++++++------------ 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index b527ab2..6c1151a 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,14 @@ go get github.com/lmika/ted ## Usage ``` -ted +ted [FLAGS] FILE ``` -Can either be a new CSV file, or an existing CSV file. +Flags: + +- `-c ` the format that the file is in. Either `csv` or `tsv` files are supported. Default is `csv` + +File can either be a new file, or an existing file. TED is similar to Vim in that it is modal. After opening a file, the editor starts off in view mode, which permits navigating around. diff --git a/main.go b/main.go index 6aca219..5d7acf0 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,14 @@ package main import ( - "github.com/lmika/ted/ui" "flag" "fmt" + "github.com/lmika/ted/ui" "os" ) func main() { + var flagCodec = flag.String("c", "csv", "file codec to use") flag.Parse() if flag.NArg() == 0 { fmt.Fprintln(os.Stderr, "usage: ted FILENAME") @@ -20,8 +21,14 @@ func main() { } defer uiManager.Close() + codecBuilder, hasCodec := codecModelSourceBuilders[*flagCodec] + if !hasCodec { + fmt.Fprintf(os.Stderr, "unrecognised codec: %v", *flagCodec) + os.Exit(1) + } + frame := NewFrame(uiManager) - session := NewSession(uiManager, frame, CsvFileModelSource{flag.Arg(0)}) + session := NewSession(uiManager, frame, codecBuilder(flag.Arg(0))) session.LoadFromSource() uiManager.SetRootComponent(frame.RootComponent()) @@ -29,3 +36,14 @@ func main() { uiManager.Loop() } + +type codecModelSourceBuilder func(filename string) ModelSource + +var codecModelSourceBuilders = map[string]codecModelSourceBuilder{ + "csv": func(filename string) ModelSource { + return NewCsvFileModelSource(filename, CsvFileModelSourceOptions{Comma: ','}) + }, + "tsv": func(filename string) ModelSource { + return NewCsvFileModelSource(filename, CsvFileModelSourceOptions{Comma: '\t'}) + }, +} diff --git a/modelsource.go b/modelsource.go index 9b5c4fe..cde8efa 100644 --- a/modelsource.go +++ b/modelsource.go @@ -1,10 +1,10 @@ package main import ( - "path/filepath" - "os" "encoding/csv" "io" + "os" + "path/filepath" ) // ModelSource is a source of models. At a minimum, it must be able to read models. @@ -26,22 +26,34 @@ type WritableModelSource interface { // A model source backed by a CSV file type CsvFileModelSource struct { - Filename string + filename string + options CsvFileModelSourceOptions +} + +type CsvFileModelSourceOptions struct { + Comma rune +} + +func NewCsvFileModelSource(filename string, options CsvFileModelSourceOptions) CsvFileModelSource { + return CsvFileModelSource{ + filename: filename, + options: options, + } } // Describes the source func (s CsvFileModelSource) String() string { - return filepath.Base(s.Filename) + return filepath.Base(s.filename) } // Read the model from the given source func (s CsvFileModelSource) Read() (Model, error) { // Check if the file exists. If not, return an empty model - if _, err := os.Stat(s.Filename); os.IsNotExist(err) { + if _, err := os.Stat(s.filename); os.IsNotExist(err) { return NewSingleCellStdModel(), nil } - f, err := os.Open(s.Filename) + f, err := os.Open(s.filename) if err != nil { return nil, err } @@ -49,12 +61,13 @@ func (s CsvFileModelSource) Read() (Model, error) { model := new(StdModel) r := csv.NewReader(f) + r.Comma = s.options.Comma + r.FieldsPerRecord = -1 for { record, err := r.Read() if err == io.EOF { break - } - if err != nil { + } else if err != nil { return nil, err } @@ -66,19 +79,20 @@ func (s CsvFileModelSource) Read() (Model, error) { } func (s CsvFileModelSource) Write(m Model) error { - f, err := os.Create(s.Filename) + f, err := os.Create(s.filename) if err != nil { return err } w := csv.NewWriter(f) + w.Comma = s.options.Comma rows, cols := m.Dimensions() for r := 0; r < rows; r++ { - record := make([]string, cols) // Reuse the record slice + record := make([]string, cols) // Reuse the record slice for c := 0; c < cols; c++ { - record[c] = m.CellValue(r, c) + record[c] = m.CellValue(r, c) } if err := w.Write(record); err != nil { f.Close() @@ -93,4 +107,4 @@ func (s CsvFileModelSource) Write(m Model) error { } return f.Close() -} \ No newline at end of file +}