add reverse glob and regex filters
This commit is contained in:
parent
cc1523034b
commit
6794b3a9b2
|
@ -14,8 +14,10 @@ import (
|
|||
type Filter struct {
|
||||
on, before, after time.Time
|
||||
glob, pattern string
|
||||
unglob, unpattern string
|
||||
filenames []string
|
||||
matcher *regexp.Regexp
|
||||
unmatcher *regexp.Regexp
|
||||
}
|
||||
|
||||
func (f *Filter) On() time.Time { return f.on }
|
||||
|
@ -47,6 +49,14 @@ func (f *Filter) Match(filename string, modified time.Time) bool {
|
|||
return false
|
||||
}
|
||||
}
|
||||
if f.has_unregex() && f.unmatcher.MatchString(filename) {
|
||||
return false
|
||||
}
|
||||
if f.unglob != "" {
|
||||
if match, err := filepath.Match(f.unglob, filename); err != nil || match {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(f.filenames) > 0 && !slices.Contains(f.filenames, filename) {
|
||||
return false
|
||||
}
|
||||
|
@ -61,10 +71,19 @@ func (f *Filter) SetPattern(pattern string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (f *Filter) SetUnPattern(unpattern string) error {
|
||||
var err error
|
||||
f.unpattern = unpattern
|
||||
f.unmatcher, err = regexp.Compile(f.unpattern)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *Filter) Blank() bool {
|
||||
t := time.Time{}
|
||||
return !f.has_regex() &&
|
||||
!f.has_unregex() &&
|
||||
f.glob == "" &&
|
||||
f.unglob == "" &&
|
||||
f.after.Equal(t) &&
|
||||
f.before.Equal(t) &&
|
||||
f.on.Equal(t) &&
|
||||
|
@ -72,13 +91,18 @@ func (f *Filter) Blank() bool {
|
|||
}
|
||||
|
||||
func (f *Filter) String() string {
|
||||
var m string
|
||||
var m, unm string
|
||||
if f.matcher != nil {
|
||||
m = f.matcher.String()
|
||||
}
|
||||
return fmt.Sprintf("on:'%s' before:'%s' after:'%s' glob:'%s' regex:'%s' filenames:'%v'",
|
||||
if f.unmatcher != nil {
|
||||
unm = f.unmatcher.String()
|
||||
}
|
||||
return fmt.Sprintf("on:'%s' before:'%s' after:'%s' glob:'%s' regex:'%s' unglob:'%s' unregex:'%s' filenames:'%v'",
|
||||
f.on, f.before, f.after,
|
||||
f.glob, m, f.filenames,
|
||||
f.glob, m,
|
||||
f.unglob, unm,
|
||||
f.filenames,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -89,45 +113,59 @@ func (f *Filter) has_regex() bool {
|
|||
return f.matcher.String() != ""
|
||||
}
|
||||
|
||||
func New(o, b, a, g, p string, names ...string) (*Filter, error) {
|
||||
// o b a g p
|
||||
func (f *Filter) has_unregex() bool {
|
||||
if f.unmatcher == nil {
|
||||
return false
|
||||
}
|
||||
return f.unmatcher.String() != ""
|
||||
}
|
||||
|
||||
func New(on, before, after, glob, pattern, unglob, unpattern string, names ...string) (*Filter, error) {
|
||||
var (
|
||||
err error
|
||||
now = time.Now()
|
||||
)
|
||||
|
||||
f := &Filter{
|
||||
glob: g,
|
||||
glob: glob,
|
||||
unglob: unglob,
|
||||
filenames: append([]string{}, names...),
|
||||
}
|
||||
|
||||
if o != "" {
|
||||
on, err := anytime.Parse(o, now)
|
||||
if on != "" {
|
||||
o, err := anytime.Parse(on, now)
|
||||
if err != nil {
|
||||
return &Filter{}, err
|
||||
}
|
||||
f.on = on
|
||||
f.on = o
|
||||
}
|
||||
|
||||
if a != "" {
|
||||
after, err := anytime.Parse(a, now)
|
||||
if after != "" {
|
||||
a, err := anytime.Parse(after, now)
|
||||
if err != nil {
|
||||
return &Filter{}, err
|
||||
}
|
||||
f.after = after
|
||||
f.after = a
|
||||
}
|
||||
|
||||
if b != "" {
|
||||
before, err := anytime.Parse(b, now)
|
||||
if before != "" {
|
||||
b, err := anytime.Parse(before, now)
|
||||
if err != nil {
|
||||
return &Filter{}, err
|
||||
}
|
||||
f.before = before
|
||||
f.before = b
|
||||
}
|
||||
|
||||
err = f.SetPattern(p)
|
||||
err = f.SetPattern(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = f.SetUnPattern(unpattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return f, err
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func same_day(a, b time.Time) bool {
|
||||
|
|
|
@ -22,6 +22,7 @@ var (
|
|||
|
||||
type testholder struct {
|
||||
pattern, glob string
|
||||
unpattern, unglob string
|
||||
before, after, on string
|
||||
filenames []string
|
||||
good, bad []singletest
|
||||
|
@ -47,7 +48,7 @@ func testmatch(t *testing.T, testers []testholder) {
|
|||
err error
|
||||
)
|
||||
for _, tester := range testers {
|
||||
f, err = New(tester.on, tester.before, tester.after, tester.glob, tester.pattern, tester.filenames...)
|
||||
f, err = New(tester.on, tester.before, tester.after, tester.glob, tester.pattern, tester.unglob, tester.unpattern, tester.filenames...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -261,6 +262,46 @@ func TestFilterGlob(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestFilterUnMatch(t *testing.T) {
|
||||
testmatch(t, []testholder{
|
||||
{
|
||||
unpattern: "^ss_.*\\.zip",
|
||||
good: blanktime("hello.zip", "ss_potato.png", "sss.zip"),
|
||||
bad: blanktime("ss_ost_flac.zip", "ss_guide.zip", "ss_controls.zip"),
|
||||
},
|
||||
{
|
||||
unpattern: "^h.*o$",
|
||||
good: blanktime("hi", "test", "hellO", "Hello", "oh hello there"),
|
||||
bad: blanktime("hello", "hippo", "how about some pasta with alfredo"),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilterUnGlob(t *testing.T) {
|
||||
testmatch(t, []testholder{
|
||||
{
|
||||
unglob: "*.txt",
|
||||
good: blanktime("test.md", "test.go", "test.tar.gz", "testxt", "test.text"),
|
||||
bad: blanktime("test.txt", "alsotest.txt"),
|
||||
},
|
||||
{
|
||||
unglob: "*.tar.*",
|
||||
good: blanktime("test.tar", "test.txt", "test.targz", "test.tgz"),
|
||||
bad: blanktime("test.tar.gz", "test.tar.xz", "test.tar.zst", "test.tar.bz2"),
|
||||
},
|
||||
{
|
||||
unglob: "pot*o",
|
||||
good: blanktime("salad", "test", "alsotest"),
|
||||
bad: blanktime("potato", "potdonkeyo", "potesto"),
|
||||
},
|
||||
{
|
||||
unglob: "t?st",
|
||||
good: blanktime("best", "fast", "most", "past"),
|
||||
bad: blanktime("test", "tast", "tfst", "tnst"),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilterFilenames(t *testing.T) {
|
||||
testmatch(t, []testholder{
|
||||
{
|
||||
|
@ -282,6 +323,12 @@ func TestFilterFilenames(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFilterMultipleParameters(t *testing.T) {
|
||||
y, m, d := now.Date()
|
||||
threepm := time.Date(y, m, d, 15, 0, 0, 0, time.Local)
|
||||
tenpm := time.Date(y, m, d, 22, 0, 0, 0, time.Local)
|
||||
twoam := time.Date(y, m, d, 2, 0, 0, 0, time.Local)
|
||||
sevenam := time.Date(y, m, d, 7, 0, 0, 0, time.Local)
|
||||
|
||||
testmatch(t, []testholder{
|
||||
{
|
||||
pattern: "[Tt]est",
|
||||
|
@ -318,16 +365,29 @@ func TestFilterMultipleParameters(t *testing.T) {
|
|||
on: "today",
|
||||
after: "two weeks ago",
|
||||
before: "one week ago",
|
||||
good: blankfilename(now, time.Date(now.Year(), now.Month(), now.Day(), 18, 42, 0, 0, time.Local), time.Date(now.Year(), now.Month(), now.Day(), 8, 17, 33, 0, time.Local)),
|
||||
good: blankfilename(now, twoam, sevenam, threepm, tenpm),
|
||||
bad: blankfilename(yesterday, oneweekago, onemonthago, oneyearago),
|
||||
},
|
||||
{
|
||||
unpattern: ".*\\.(jpg|png)",
|
||||
on: "today",
|
||||
good: []singletest{
|
||||
{filename: "test.txt", modified: now},
|
||||
{filename: "hello.md", modified: tenpm},
|
||||
},
|
||||
bad: []singletest{
|
||||
{filename: "test.png", modified: now},
|
||||
{filename: "test.jpg", modified: twoam},
|
||||
{filename: "hello.md", modified: twomonthsago},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilterBlank(t *testing.T) {
|
||||
var f *Filter
|
||||
t.Run("new", func(t *testing.T) {
|
||||
f, _ = New("", "", "", "", "")
|
||||
f, _ = New("", "", "", "", "", "", "")
|
||||
if !f.Blank() {
|
||||
t.Fatalf("filter isn't blank? %s", f)
|
||||
}
|
||||
|
@ -351,6 +411,12 @@ func TestFilterNotBlank(t *testing.T) {
|
|||
{
|
||||
glob: "*test*",
|
||||
},
|
||||
{
|
||||
unpattern: ".*\\.(jpg|png)",
|
||||
},
|
||||
{
|
||||
unglob: "*.jpg",
|
||||
},
|
||||
{
|
||||
before: "yesterday",
|
||||
after: "one week ago",
|
||||
|
@ -369,7 +435,7 @@ func TestFilterNotBlank(t *testing.T) {
|
|||
|
||||
for _, tester := range testers {
|
||||
t.Run("notblank"+tester.String(), func(t *testing.T) {
|
||||
f, _ = New(tester.on, tester.before, tester.after, tester.glob, tester.pattern, tester.filenames...)
|
||||
f, _ = New(tester.on, tester.before, tester.after, tester.glob, tester.pattern, tester.unglob, tester.unpattern, tester.filenames...)
|
||||
if f.Blank() {
|
||||
t.Fatalf("filter is blank?? %s", f)
|
||||
}
|
||||
|
|
15
main.go
15
main.go
|
@ -29,6 +29,7 @@ var (
|
|||
loglvl string
|
||||
f *filter.Filter
|
||||
o, b, a, g, p string
|
||||
ung, unp string
|
||||
workdir string
|
||||
recursive bool
|
||||
termwidth int
|
||||
|
@ -59,7 +60,7 @@ var (
|
|||
before_commands = func(ctx *cli.Context) (err error) {
|
||||
// setup filter
|
||||
if f == nil {
|
||||
f, err = filter.New(o, b, a, g, p, ctx.Args().Slice()...)
|
||||
f, err = filter.New(o, b, a, g, p, ung, unp, ctx.Args().Slice()...)
|
||||
}
|
||||
log.Debugf("filter: %s", f.String())
|
||||
return
|
||||
|
@ -227,6 +228,18 @@ var (
|
|||
Aliases: []string{"g"},
|
||||
Destination: &g,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "not-match",
|
||||
Usage: "operate on files not matching regex `PATTERN`",
|
||||
Aliases: []string{"M"},
|
||||
Destination: &unp,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "not-glob",
|
||||
Usage: "operate on files not matching `GLOB`",
|
||||
Aliases: []string{"G"},
|
||||
Destination: &ung,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "on",
|
||||
Usage: "operate on files modified on `DATE`",
|
||||
|
|
Loading…
Reference in a new issue