Skip to content

Commit

Permalink
initial filters implementation, wip
Browse files Browse the repository at this point in the history
  • Loading branch information
patinthehat committed May 14, 2024
1 parent ffe534b commit 5f25c38
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 4 deletions.
25 changes: 24 additions & 1 deletion app/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/jessevdk/go-flags"
. "github.com/permafrost-dev/eget/lib/appflags"
"github.com/permafrost-dev/eget/lib/assets"
. "github.com/permafrost-dev/eget/lib/assets"
"github.com/permafrost-dev/eget/lib/data"
"github.com/permafrost-dev/eget/lib/detectors"
Expand Down Expand Up @@ -120,6 +121,23 @@ func (app *Application) Run() *ReturnStatus {

finder := app.getFinder()
findResult := app.getFindResult(finder)

if len(app.Opts.Filters) > 0 {
var temp []assets.Asset = []assets.Asset{}

for _, filter := range app.Opts.Filters {
for _, a := range findResult.Assets {
if filter.Apply(a) {
temp = append(temp, a)
}
}
}
findResult.Assets = temp
if len(temp) == 0 {
findResult.Error = fmt.Errorf("no assets found matching filters")
}
}

app.cacheTarget(&finder, &findResult)

if shouldReturn, returnStatus := app.shouldReturn(findResult.Error); shouldReturn {
Expand Down Expand Up @@ -283,7 +301,7 @@ func (app *Application) selectFromMultipleAssets(candidates []Asset, err error)
choiceStr := fmt.Sprintf("%s", choices[choice-1])

result := candidates[choice-1]
result.Filters = filters.FilenameToAssetFilters(choiceStr)
result.Filters = utilities.FilenameToAssetFilters(choiceStr)

return result, nil

Expand Down Expand Up @@ -422,6 +440,11 @@ func (app *Application) ProcessFlags(errorHandler ProcessFlagsErrorHandlerFunc)
app.Opts.Verbose = app.cli.Verbose != nil && *app.cli.Verbose
app.Opts.NoProgress = app.cli.NoProgress != nil && *app.cli.NoProgress

app.Opts.Filters = []*filters.Filter{}
if app.cli.Filters != nil {
app.Opts.Filters = filters.NewParser().ParseDefinitions(*app.cli.Filters)
}

return target, nil
}

Expand Down
4 changes: 4 additions & 0 deletions lib/appflags/flags.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package appflags

import "github.com/permafrost-dev/eget/lib/filters"

type Flags struct {
Tag string
Prerelease bool
Expand All @@ -19,6 +21,7 @@ type Flags struct {
NoInteraction bool
Verbose bool
NoProgress bool
Filters []*filters.Filter
}

type CliFlags struct {
Expand All @@ -43,4 +46,5 @@ type CliFlags struct {
NoInteraction bool `long:"no-interaction" description:"do not prompt for user input"`
Verbose *bool `short:"v" long:"verbose" description:"show verbose output"`
NoProgress *bool `long:"no-progress" description:"do not show download progress"`
Filters *string `short:"F" long:"filter" description:"filter assets using functions like 'all', 'any', 'none', 'has', 'ext'"`
}
43 changes: 43 additions & 0 deletions lib/filters/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package filters

import (
"strings"

"github.com/permafrost-dev/eget/lib/assets"
)

type FilterHandler func(assets.Asset, []string) bool

type FilterAction byte

const (
FilterActionInclude FilterAction = iota
FilterActionExclude FilterAction = iota
)

type Filter struct {
Name string
Handler FilterHandler
Action FilterAction
Args []string
Definition string
}

func NewFilter(name string, handler FilterHandler, action FilterAction, args ...string) *Filter {
return &Filter{
Name: name,
Handler: handler,
Action: action,
Args: args,
Definition: name + "(" + strings.Join(args, ",") + ")",
}
}

func (f *Filter) Apply(item assets.Asset) bool {
return f.Handler(item, f.Args)
}

func (f *Filter) WithArgs(args ...string) *Filter {
f.Args = args
return f
}
68 changes: 68 additions & 0 deletions lib/filters/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package filters

import (
"path"
"strings"

"github.com/permafrost-dev/eget/lib/assets"
)

var FilterMap = map[string]*Filter{
"all": NewFilter("all", allHandler, FilterActionInclude),
"any": NewFilter("any", anyHandler, FilterActionInclude),
"ext": NewFilter("ext", extensionHandler, FilterActionInclude),
"none": NewFilter("none", noneHandler, FilterActionExclude),
"has": NewFilter("has", hasHandler, FilterActionInclude),
}

var anyHandler FilterHandler = func(item assets.Asset, args []string) bool {
for _, arg := range args {
if strings.EqualFold(item.Name, arg) {
return true
}
}

return false
}

var allHandler FilterHandler = func(item assets.Asset, args []string) bool {
for _, arg := range args {
if !strings.EqualFold(item.Name, arg) {
return false
}
}

return true
}

var hasHandler FilterHandler = func(item assets.Asset, args []string) bool {
for _, arg := range args {
if !strings.EqualFold(item.Name, arg) {
return false
}
}

return true
}

var noneHandler FilterHandler = func(item assets.Asset, args []string) bool {
for _, arg := range args {
if strings.EqualFold(item.Name, arg) {
return false
}
}

return true
}

var extensionHandler FilterHandler = func(item assets.Asset, args []string) bool {
extension := path.Ext(item.Name)

for _, arg := range args {
if strings.EqualFold(extension, arg) {
return true
}
}

return false
}
53 changes: 53 additions & 0 deletions lib/filters/handlers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package filters_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/permafrost-dev/eget/lib/assets"
. "github.com/permafrost-dev/eget/lib/filters"
)

var anyHandler = FilterMap["any"].Handler
var allHandler = FilterMap["all"].Handler
var hasHandler = FilterMap["has"].Handler
var noneHandler = FilterMap["none"].Handler
var extensionHandler = FilterMap["ext"].Handler

var _ = Describe("Filters", func() {
Context("Handlers", func() {
asset1 := assets.Asset{Name: "file1.txt"}
asset2 := assets.Asset{Name: "file2.exe"}

It("anyHandler should return true if any argument matches the asset name", func() {
Expect(anyHandler(asset1, []string{"file1.txt", "file2.exe"})).To(BeTrue())
Expect(anyHandler(asset2, []string{"file1.txt", "file2.exe"})).To(BeTrue())
Expect(anyHandler(asset1, []string{"file2.exe"})).To(BeFalse())
})

It("allHandler should return true if all arguments match the asset name", func() {
Expect(allHandler(asset1, []string{"file1.txt", "file1.txt"})).To(BeTrue())
Expect(allHandler(asset2, []string{"file2.exe", "file2.exe"})).To(BeTrue())
Expect(allHandler(asset1, []string{"file1.txt", "file2.exe"})).To(BeFalse())
})

It("hasHandler should return true if any argument matches the asset name", func() {
Expect(hasHandler(asset1, []string{"file1.txt"})).To(BeTrue())
Expect(hasHandler(asset2, []string{"file2.exe"})).To(BeTrue())
Expect(hasHandler(asset1, []string{"file2.exe"})).To(BeFalse())
})

It("noneHandler should return true if no arguments match the asset name", func() {
Expect(noneHandler(asset1, []string{"file2.exe"})).To(BeTrue())
Expect(noneHandler(asset2, []string{"file1.txt"})).To(BeTrue())
Expect(noneHandler(asset1, []string{"file1.txt"})).To(BeFalse())
})

It("extensionHandler should return true if the extension matches the asset name", func() {
Expect(extensionHandler(asset1, []string{".txt"})).To(BeTrue())
Expect(extensionHandler(asset2, []string{".exe"})).To(BeTrue())
Expect(extensionHandler(asset1, []string{".exe"})).To(BeFalse())
})
})

})
55 changes: 55 additions & 0 deletions lib/filters/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package filters

import (
"regexp"
"strings"
)

type Parser struct {
}

func NewParser() *Parser {
return &Parser{}
}

func (p *Parser) ParseDefinitions(definitions string) []*Filter {
defs := strings.Split(definitions, ";")

filters := make([]*Filter, 0)

for _, def := range defs {
filter := p.ParseDefinition(def)
if filter != nil {
filters = append(filters, filter)
}
}

return filters
}

// ParseDefinition parses a string definition of a filter, like "always(abc,def)" or "never(.deb)"
// and returns a Filter struct with the appropriate values set.
func (p *Parser) ParseDefinition(definition string) *Filter {
re := regexp.MustCompile(`([a-zA-Z]+)\((.*)\)`)
matches := re.FindStringSubmatch(definition)

if len(matches) < 3 {
return nil
}

name := matches[1]
argStr := matches[2]
args := strings.Split(argStr, ",")

if FilterMap[name] == nil {
return nil
}

return &Filter{
Name: FilterMap[name].Name,
Handler: FilterMap[name].Handler,
Action: FilterMap[name].Action,
Args: args,
Definition: definition,
}
}
72 changes: 72 additions & 0 deletions lib/filters/parser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package filters_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/permafrost-dev/eget/lib/assets"
. "github.com/permafrost-dev/eget/lib/filters"
)

var _ = Describe("Filters", func() {
Context("NewFilter", func() {
It("should create a new filter with the given parameters", func() {
handler := func(a assets.Asset, args []string) bool { return true }
filter := NewFilter("test", handler, FilterActionInclude, "arg1", "arg2")

Expect(filter.Name).To(Equal("test"))
Expect(filter.Action).To(Equal(FilterActionInclude))
Expect(filter.Args).To(Equal([]string{"arg1", "arg2"}))
Expect(filter.Definition).To(Equal("test(arg1,arg2)"))
})
})

Context("Filter Apply", func() {
It("should apply the handler to the asset", func() {
handler := func(a assets.Asset, args []string) bool { return a.Name == "test" }
filter := NewFilter("test", handler, FilterActionInclude)
asset := assets.Asset{Name: "test"}

Expect(filter.Apply(asset)).To(BeTrue())
})
})

Context("Filter WithArgs", func() {
It("should set the arguments and return the filter", func() {
handler := func(a assets.Asset, args []string) bool { return true }
filter := NewFilter("test", handler, FilterActionInclude)
filter.WithArgs("newArg1", "newArg2")

Expect(filter.Args).To(Equal([]string{"newArg1", "newArg2"}))
})
})

Context("Parser", func() {
parser := NewParser()

It("should parse multiple definitions", func() {
definitions := "all(file1.txt);none(file2.exe)"
filters := parser.ParseDefinitions(definitions)

Expect(filters).To(HaveLen(2))
Expect(filters[0].Name).To(Equal("all"))
Expect(filters[1].Name).To(Equal("none"))
})

It("should parse a single definition", func() {
definition := "all(file1.txt)"
filter := parser.ParseDefinition(definition)

Expect(filter).NotTo(BeNil())
Expect(filter.Name).To(Equal("all"))
Expect(filter.Args).To(Equal([]string{"file1.txt"}))
})

It("should return nil for invalid definitions", func() {
definition := "invalid()"
filter := parser.ParseDefinition(definition)

Expect(filter).To(BeNil())
})
})
})
2 changes: 1 addition & 1 deletion lib/filters/filters.go → lib/utilities/filters.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package filters
package utilities

import (
"regexp"
Expand Down
4 changes: 2 additions & 2 deletions lib/filters/filters_test.go → lib/utilities/filters_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package filters_test
package utilities_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

. "github.com/permafrost-dev/eget/lib/filters"
. "github.com/permafrost-dev/eget/lib/utilities"
)

var _ = Describe("Filters", func() {
Expand Down

0 comments on commit 5f25c38

Please sign in to comment.