Skip to content

Commit

Permalink
Merge pull request #20 from catatsuy/feature_add_not_filter
Browse files Browse the repository at this point in the history
Update filter process to handle not-filter expressions
catatsuy authored Mar 30, 2024
2 parents 64c230f + fa6dafb commit 41a7322
Showing 3 changed files with 104 additions and 23 deletions.
37 changes: 25 additions & 12 deletions cli/cli.go
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ type CLI struct {
replaceExpr string
isOverwrite bool
filters rawStrings
notFilters rawStrings
help bool
}

@@ -105,14 +106,20 @@ func (c *CLI) Run(args []string) int {
}
}

if len(c.filters) > 0 {
regexps, err := compileRegexps(c.filters)
if len(c.filters) > 0 || len(c.notFilters) > 0 {
filters, err := compileRegexps(c.filters)
if err != nil {
fmt.Fprintf(c.errStream, "Failed to compile regex patterns: %s\n", err)
return ExitCodeFail
}

err = c.filterProcess(regexps)
notFilters, err := compileRegexps(c.notFilters)
if err != nil {
fmt.Fprintf(c.errStream, "Failed to compile regex patterns: %s\n", err)
return ExitCodeFail
}

err = c.filterProcess(filters, notFilters)
if err != nil {
fmt.Fprintf(c.errStream, "Failed to process files: %s\n", err)
return ExitCodeFail
@@ -136,6 +143,7 @@ func (c *CLI) parseFlags(args []string) (*flag.FlagSet, error) {
flags.BoolVar(&c.isOverwrite, "overwrite", false, "overwrite the file in place")
flags.StringVar(&c.replaceExpr, "replace", "", `Replacement expression, e.g., "@search@replace@"`)
flags.Var(&c.filters, "filter", `Filter expression`)
flags.Var(&c.notFilters, "not-filter", `Not filter expression`)
flags.BoolVar(&c.help, "help", false, `Show help`)

flags.Usage = func() {
@@ -160,11 +168,11 @@ func (c *CLI) validateInput(flags *flag.FlagSet) error {
return fmt.Errorf("cannot use -overwrite option with stdin")
}

if len(c.replaceExpr) != 0 && len(c.filters) != 0 {
if len(c.replaceExpr) != 0 && (len(c.filters) != 0 || len(c.notFilters) != 0) {
return fmt.Errorf("cannot use -replace and -filter options together")
}

if len(c.filters) == 0 && len(c.replaceExpr) < 3 {
if (len(c.filters) == 0 && len(c.notFilters) == 0) && len(c.replaceExpr) < 3 {
return fmt.Errorf("invalid replace expression format. Use \"@search@replace@\"")
}

@@ -201,17 +209,13 @@ func (c *CLI) replaceProcess(searchPattern, replacement string) error {
return nil
}

func (c *CLI) filterProcess(regexps []*regexp.Regexp) error {
func (c *CLI) filterProcess(filters []*regexp.Regexp, notFilters []*regexp.Regexp) error {
scanner := bufio.NewScanner(c.inputStream)

for scanner.Scan() {
line := scanner.Text()

for _, re := range regexps {
if re.MatchString(line) {
fmt.Fprintln(c.outStream, line)
break
}
if matchesFilters(line, filters) || (len(notFilters) > 0 && !matchesFilters(line, notFilters)) {
fmt.Fprintln(c.outStream, line)
}
}

@@ -233,3 +237,12 @@ func compileRegexps(rawPatterns []string) ([]*regexp.Regexp, error) {
}
return regexps, nil
}

func matchesFilters(line string, regexps []*regexp.Regexp) bool {
for _, re := range regexps {
if re.MatchString(line) {
return true
}
}
return false
}
86 changes: 77 additions & 9 deletions cli/cli_test.go
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ func TestNewCLI(t *testing.T) {
}
}

func TestRun_success(t *testing.T) {
func TestRun_successProcess(t *testing.T) {
tests := map[string]struct {
args []string
input string
@@ -60,7 +60,7 @@ func TestRun_success(t *testing.T) {
}
}

func TestRun_help(t *testing.T) {
func TestRun_success(t *testing.T) {
testCases := []struct {
desc string
args []string
@@ -71,6 +71,36 @@ func TestRun_help(t *testing.T) {
args: []string{"purl", "-help"},
expectedCode: 0,
},
{
desc: "filter",
args: []string{"purl", "-filter", "search"},
expectedCode: 0,
},
{
desc: "multiple -filter",
args: []string{"purl", "-filter", "search", "-filter", "search2"},
expectedCode: 0,
},
{
desc: "replace",
args: []string{"purl", "-replace", "@search@replace@"},
expectedCode: 0,
},
{
desc: "-not-filter",
args: []string{"purl", "-not-filter", "not filter"},
expectedCode: 0,
},
{
desc: "multiple -not-filter",
args: []string{"purl", "-not-filter", "not filter", "-not-filter", "not filter2"},
expectedCode: 0,
},
{
desc: "provide -filter and -not-filter",
args: []string{"purl", "-filter", "filter", "-not-filter", "not filter2"},
expectedCode: 0,
},
}

for _, tc := range testCases {
@@ -214,33 +244,65 @@ func TestFilterProcess(t *testing.T) {
tests := []struct {
name string
input string
patterns []string
filters []string
notFilters []string
wantOutput string
}{
{
name: "SingleMatch",
input: "apple\nbanana\ncherry\n",
patterns: []string{"banana"},
filters: []string{"banana"},
wantOutput: "banana\n",
},
{
name: "MultipleMatches",
input: "apple\nbanana\ncherry\n",
patterns: []string{"apple", "cherry"},
filters: []string{"apple", "cherry"},
wantOutput: "apple\ncherry\n",
},
{
name: "NoMatch",
input: "apple\nbanana\ncherry\n",
patterns: []string{"date"},
filters: []string{"date"},
wantOutput: "",
},
{
name: "EmptyInput",
input: "",
patterns: []string{"apple"},
filters: []string{"apple"},
wantOutput: "",
},
{
name: "-not-filter: SingleMatch",
input: "apple\nbanana\ncherry\n",
notFilters: []string{"banana"},
wantOutput: "apple\ncherry\n",
},
{
name: "-not-filter: MultipleMatches",
input: "apple\nbanana\ncherry\n",
notFilters: []string{"apple", "cherry"},
wantOutput: "banana\n",
},
{
name: "-not-filter: NoMatch",
input: "apple\nbanana\ncherry\n",
notFilters: []string{"date"},
wantOutput: "apple\nbanana\ncherry\n",
},
{
name: "-not-filter: EmptyInput",
input: "",
notFilters: []string{"apple"},
wantOutput: "",
},
{
name: "provide filter and not filter",
input: "apple\nbanana\ncherry\n",
filters: []string{"apple"},
notFilters: []string{"cherry"},
wantOutput: "apple\nbanana\n",
},
}

for _, tt := range tests {
@@ -250,13 +312,19 @@ func TestFilterProcess(t *testing.T) {
cl := cli.NewCLI(outStream, errStream, inputStream)
inputStream.WriteString(tt.input)

regexps, err := cli.CompileRegexps(tt.patterns)
filters, err := cli.CompileRegexps(tt.filters)
if err != nil {
t.Errorf("CompileRegexps() error = %v", err)
return
}

notFilters, err := cli.CompileRegexps(tt.notFilters)
if err != nil {
t.Errorf("CompileRegexps() error = %v", err)
return
}

err = cl.FilterProcess(regexps)
err = cl.FilterProcess(filters, notFilters)
if err != nil {
t.Errorf("filterProcess() error = %v", err)
return
4 changes: 2 additions & 2 deletions cli/export_test.go
Original file line number Diff line number Diff line change
@@ -6,8 +6,8 @@ func (c *CLI) ReplaceProcess(searchPattern string, replacement string) error {
return c.replaceProcess(searchPattern, replacement)
}

func (c *CLI) FilterProcess(regexps []*regexp.Regexp) error {
return c.filterProcess(regexps)
func (c *CLI) FilterProcess(filters []*regexp.Regexp, notFilters []*regexp.Regexp) error {
return c.filterProcess(filters, notFilters)
}

func CompileRegexps(rawPatterns []string) ([]*regexp.Regexp, error) {

0 comments on commit 41a7322

Please sign in to comment.