mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-17 12:31:06 +01:00
re-organized pattern matching code
This commit is contained in:
30
CHANGELOG.md
30
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).
|
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)
|
||||||
|
|||||||
2
TODO.md
2
TODO.md
@@ -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 = '#')
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
lib/io.go
14
lib/io.go
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user