re-organized pattern matching code

This commit is contained in:
2022-10-25 18:34:28 +02:00
parent 417faf3ff2
commit 066ddd0d98
8 changed files with 90 additions and 43 deletions

View File

@@ -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). 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 ## [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) [Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.10...v1.0.11)

View File

@@ -1,7 +1,5 @@
## Fixes to be implemented ## Fixes to be implemented
- refactor parser, there's some duplicate code, remove pattern from parser args
## Features to be implemented ## Features to be implemented
- add comment support (csf.NewReader().Comment = '#') - add comment support (csf.NewReader().Comment = '#')

View File

@@ -186,14 +186,15 @@ func (c *Config) ApplyDefaults() {
} }
} }
func (c *Config) PreparePattern() error { func (c *Config) PreparePattern(pattern string) error {
PatternR, err := regexp.Compile(c.Pattern) PatternR, err := regexp.Compile(pattern)
if err != nil { if err != nil {
return errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", c.Pattern, err)) return errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", c.Pattern, err))
} }
c.PatternR = PatternR c.PatternR = PatternR
c.Pattern = pattern
return nil return nil
} }

View File

@@ -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)
}
}
})
}
}

View File

@@ -74,15 +74,10 @@ func Execute() {
conf.PrepareModeFlags(modeflag) conf.PrepareModeFlags(modeflag)
conf.PrepareSortFlags(sortmode) conf.PrepareSortFlags(sortmode)
conf.DetermineColormode() conf.DetermineColormode()
if err := conf.PreparePattern(); err != nil {
return err
}
conf.ApplyDefaults() conf.ApplyDefaults()
// actual execution starts here // actual execution starts here
return lib.ProcessFiles(conf, args) return lib.ProcessFiles(&conf, args)
}, },
} }

View File

@@ -24,25 +24,29 @@ import (
"os" "os"
) )
func ProcessFiles(c cfg.Config, args []string) error { func ProcessFiles(c *cfg.Config, args []string) error {
fds, pattern, err := determineIO(&c, args) fds, pattern, err := determineIO(c, args)
if err != nil { if err != nil {
return err return err
} }
if err := c.PreparePattern(pattern); err != nil {
return err
}
for _, fd := range fds { for _, fd := range fds {
data, err := parseFile(c, fd, pattern) data, err := Parse(*c, fd)
if err != nil { if err != nil {
return err return err
} }
err = PrepareColumns(&c, &data) err = PrepareColumns(c, &data)
if err != nil { if err != nil {
return err return err
} }
printData(os.Stdout, c, &data) printData(os.Stdout, *c, &data)
} }
return nil return nil

View File

@@ -29,19 +29,25 @@ import (
"strings" "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. 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 var content io.Reader = input
data := Tabdata{} data := Tabdata{}
patternR, err := regexp.Compile(pattern) if len(c.Pattern) > 0 {
if err != nil {
return data, errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", pattern, err))
}
if len(pattern) > 0 {
scanner := bufio.NewScanner(input) scanner := bufio.NewScanner(input)
lines := []string{} lines := []string{}
hadFirst := false hadFirst := false
@@ -49,7 +55,7 @@ func parseCSV(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) {
line := strings.TrimSpace(scanner.Text()) line := strings.TrimSpace(scanner.Text())
if hadFirst { if hadFirst {
// don't match 1st line, it's the header // 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 // by default -v is false, so if a line does NOT
// match the pattern, we will ignore it. However, // match the pattern, we will ignore it. However,
// if the user specified -v, the matching is inverted, // 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. Parse tabular input.
*/ */
func parseFile(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) { func parseTabular(c cfg.Config, input io.Reader) (Tabdata, error) {
if len(c.Separator) == 1 {
return parseCSV(c, input, pattern)
}
data := Tabdata{} data := Tabdata{}
var scanner *bufio.Scanner var scanner *bufio.Scanner
hadFirst := false hadFirst := false
separate := regexp.MustCompile(c.Separator) 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) scanner = bufio.NewScanner(input)
@@ -142,8 +141,8 @@ func parseFile(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) {
} }
} else { } else {
// data processing // data processing
if len(pattern) > 0 { if len(c.Pattern) > 0 {
if patternR.MatchString(line) == c.InvertMatch { if c.PatternR.MatchString(line) == c.InvertMatch {
// by default -v is false, so if a line does NOT // by default -v is false, so if a line does NOT
// match the pattern, we will ignore it. However, // match the pattern, we will ignore it. However,
// if the user specified -v, the matching is inverted, // if the user specified -v, the matching is inverted,

View File

@@ -66,7 +66,7 @@ func TestParser(t *testing.T) {
t.Run(testname, func(t *testing.T) { t.Run(testname, func(t *testing.T) {
readFd := strings.NewReader(strings.TrimSpace(in.text)) readFd := strings.NewReader(strings.TrimSpace(in.text))
c := cfg.Config{Separator: in.separator} c := cfg.Config{Separator: in.separator}
gotdata, err := parseFile(c, readFd, "") gotdata, err := Parse(c, readFd)
if err != nil { if err != nil {
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata) t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata)
@@ -101,13 +101,6 @@ func TestParserPatternmatching(t *testing.T) {
pattern: "ig", pattern: "ig",
invert: true, invert: true,
}, },
{
entries: [][]string{
{"asd", "igig", "cxxxncnc"},
},
pattern: "[a-z",
want: true,
},
} }
for _, in := range input { for _, in := range input {
@@ -118,8 +111,10 @@ func TestParserPatternmatching(t *testing.T) {
c := cfg.Config{InvertMatch: tt.invert, Pattern: tt.pattern, c := cfg.Config{InvertMatch: tt.invert, Pattern: tt.pattern,
Separator: in.separator} Separator: in.separator}
_ = c.PreparePattern(tt.pattern)
readFd := strings.NewReader(strings.TrimSpace(in.text)) readFd := strings.NewReader(strings.TrimSpace(in.text))
gotdata, err := parseFile(c, readFd, tt.pattern) gotdata, err := Parse(c, readFd)
if err != nil { if err != nil {
if !tt.want { if !tt.want {
@@ -157,7 +152,7 @@ asd igig
readFd := strings.NewReader(strings.TrimSpace(table)) readFd := strings.NewReader(strings.TrimSpace(table))
c := cfg.Config{Separator: cfg.DefaultSeparator} c := cfg.Config{Separator: cfg.DefaultSeparator}
gotdata, err := parseFile(c, readFd, "") gotdata, err := Parse(c, readFd)
if err != nil { if err != nil {
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata) t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata)