From 066ddd0d981979b71bf0f40e39900a33a0911bc2 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Tue, 25 Oct 2022 18:34:28 +0200 Subject: [PATCH] re-organized pattern matching code --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ TODO.md | 2 -- cfg/config.go | 5 +++-- cfg/config_test.go | 25 +++++++++++++++++++++++++ cmd/root.go | 7 +------ lib/io.go | 14 +++++++++----- lib/parser.go | 35 +++++++++++++++++------------------ lib/parser_test.go | 15 +++++---------- 8 files changed, 90 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f78b5e..4d33995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,36 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org). +## [v1.0.12](https://github.com/TLINDEN/tablizer/tree/v1.0.12) - 2022-10-25 + +[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.11...v1.0.12) + +### Added + +- Added support to parse CSV input + +- Added CSV output support + +- Added support for environment variables + +### Changed + +- We do not use the generated help message anymore, instead we use the + usage from the manpage, which we have to maintain anyway. It looks + better and has flag groups, which cobra is still lacking as of this + writing. + +- More refactoring and re-organization, runtime configuration now + lives in the cfg module. + + +### Fixed + +- Fixed [Bug #5](https://github.com/TLINDEN/tablizer/issues/5), where + matches have not been highlighted correctly in some rare cases. + + + ## [v1.0.11](https://github.com/TLINDEN/tablizer/tree/v1.0.11) - 2022-10-19 [Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.10...v1.0.11) diff --git a/TODO.md b/TODO.md index 4977f90..69ed0ab 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,5 @@ ## Fixes to be implemented -- refactor parser, there's some duplicate code, remove pattern from parser args - ## Features to be implemented - add comment support (csf.NewReader().Comment = '#') diff --git a/cfg/config.go b/cfg/config.go index b6a8bb3..0233ab1 100644 --- a/cfg/config.go +++ b/cfg/config.go @@ -186,14 +186,15 @@ func (c *Config) ApplyDefaults() { } } -func (c *Config) PreparePattern() error { - PatternR, err := regexp.Compile(c.Pattern) +func (c *Config) PreparePattern(pattern string) error { + PatternR, err := regexp.Compile(pattern) if err != nil { return errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", c.Pattern, err)) } c.PatternR = PatternR + c.Pattern = pattern return nil } diff --git a/cfg/config_test.go b/cfg/config_test.go index 150ed25..de7424c 100644 --- a/cfg/config_test.go +++ b/cfg/config_test.go @@ -76,3 +76,28 @@ func TestPrepareSortFlags(t *testing.T) { }) } } + +func TestPreparePattern(t *testing.T) { + var tests = []struct { + pattern string + wanterr bool + }{ + {"[A-Z]+", false}, + {"[a-z", true}, + } + + for _, tt := range tests { + testname := fmt.Sprintf("PreparePattern-pattern-%s-wanterr-%t", tt.pattern, tt.wanterr) + t.Run(testname, func(t *testing.T) { + c := Config{} + + err := c.PreparePattern(tt.pattern) + + if err != nil { + if !tt.wanterr { + t.Errorf("PreparePattern returned error: %s", err) + } + } + }) + } +} diff --git a/cmd/root.go b/cmd/root.go index 828e129..4499114 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -74,15 +74,10 @@ func Execute() { conf.PrepareModeFlags(modeflag) conf.PrepareSortFlags(sortmode) conf.DetermineColormode() - - if err := conf.PreparePattern(); err != nil { - return err - } - conf.ApplyDefaults() // actual execution starts here - return lib.ProcessFiles(conf, args) + return lib.ProcessFiles(&conf, args) }, } diff --git a/lib/io.go b/lib/io.go index 6a2cd1c..1af9059 100644 --- a/lib/io.go +++ b/lib/io.go @@ -24,25 +24,29 @@ import ( "os" ) -func ProcessFiles(c cfg.Config, args []string) error { - fds, pattern, err := determineIO(&c, args) +func ProcessFiles(c *cfg.Config, args []string) error { + fds, pattern, err := determineIO(c, args) if err != nil { return err } + if err := c.PreparePattern(pattern); err != nil { + return err + } + for _, fd := range fds { - data, err := parseFile(c, fd, pattern) + data, err := Parse(*c, fd) if err != nil { return err } - err = PrepareColumns(&c, &data) + err = PrepareColumns(c, &data) if err != nil { return err } - printData(os.Stdout, c, &data) + printData(os.Stdout, *c, &data) } return nil diff --git a/lib/parser.go b/lib/parser.go index 031ef32..efafa3c 100644 --- a/lib/parser.go +++ b/lib/parser.go @@ -29,19 +29,25 @@ import ( "strings" ) +/* + Parser switch +*/ +func Parse(c cfg.Config, input io.Reader) (Tabdata, error) { + if len(c.Separator) == 1 { + return parseCSV(c, input) + } + + return parseTabular(c, input) +} + /* Parse CSV input. */ -func parseCSV(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) { +func parseCSV(c cfg.Config, input io.Reader) (Tabdata, error) { var content io.Reader = input data := Tabdata{} - patternR, err := regexp.Compile(pattern) - if err != nil { - return data, errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", pattern, err)) - } - - if len(pattern) > 0 { + if len(c.Pattern) > 0 { scanner := bufio.NewScanner(input) lines := []string{} hadFirst := false @@ -49,7 +55,7 @@ func parseCSV(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) { line := strings.TrimSpace(scanner.Text()) if hadFirst { // don't match 1st line, it's the header - if patternR.MatchString(line) == c.InvertMatch { + if c.PatternR.MatchString(line) == c.InvertMatch { // by default -v is false, so if a line does NOT // match the pattern, we will ignore it. However, // if the user specified -v, the matching is inverted, @@ -94,20 +100,13 @@ func parseCSV(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) { /* Parse tabular input. */ -func parseFile(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) { - if len(c.Separator) == 1 { - return parseCSV(c, input, pattern) - } +func parseTabular(c cfg.Config, input io.Reader) (Tabdata, error) { data := Tabdata{} var scanner *bufio.Scanner hadFirst := false separate := regexp.MustCompile(c.Separator) - patternR, err := regexp.Compile(pattern) - if err != nil { - return data, errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", pattern, err)) - } scanner = bufio.NewScanner(input) @@ -142,8 +141,8 @@ func parseFile(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) { } } else { // data processing - if len(pattern) > 0 { - if patternR.MatchString(line) == c.InvertMatch { + if len(c.Pattern) > 0 { + if c.PatternR.MatchString(line) == c.InvertMatch { // by default -v is false, so if a line does NOT // match the pattern, we will ignore it. However, // if the user specified -v, the matching is inverted, diff --git a/lib/parser_test.go b/lib/parser_test.go index 2243ecf..b1e5017 100644 --- a/lib/parser_test.go +++ b/lib/parser_test.go @@ -66,7 +66,7 @@ func TestParser(t *testing.T) { t.Run(testname, func(t *testing.T) { readFd := strings.NewReader(strings.TrimSpace(in.text)) c := cfg.Config{Separator: in.separator} - gotdata, err := parseFile(c, readFd, "") + gotdata, err := Parse(c, readFd) if err != nil { t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata) @@ -101,13 +101,6 @@ func TestParserPatternmatching(t *testing.T) { pattern: "ig", invert: true, }, - { - entries: [][]string{ - {"asd", "igig", "cxxxncnc"}, - }, - pattern: "[a-z", - want: true, - }, } for _, in := range input { @@ -118,8 +111,10 @@ func TestParserPatternmatching(t *testing.T) { c := cfg.Config{InvertMatch: tt.invert, Pattern: tt.pattern, Separator: in.separator} + _ = c.PreparePattern(tt.pattern) + readFd := strings.NewReader(strings.TrimSpace(in.text)) - gotdata, err := parseFile(c, readFd, tt.pattern) + gotdata, err := Parse(c, readFd) if err != nil { if !tt.want { @@ -157,7 +152,7 @@ asd igig readFd := strings.NewReader(strings.TrimSpace(table)) c := cfg.Config{Separator: cfg.DefaultSeparator} - gotdata, err := parseFile(c, readFd, "") + gotdata, err := Parse(c, readFd) if err != nil { t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata)