You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ntool/nfs/finder/matchers.go

290 lines
6.6 KiB
Go

package finder
import (
"git.noahlan.cn/noahlan/ntool/nfs"
"git.noahlan.cn/noahlan/ntool/nmath"
"git.noahlan.cn/noahlan/ntool/nstr"
"git.noahlan.cn/noahlan/ntool/ntime"
"path"
"regexp"
"strings"
"time"
)
// ------------------ built in filters ------------------
// MatchFile only allow file path.
var MatchFile = MatcherFunc(func(el Elem) bool {
return !el.IsDir()
})
// MatchDir only allow dir path.
var MatchDir = MatcherFunc(func(el Elem) bool {
return el.IsDir()
})
// StartWithDot match dot file/dir. eg: ".gitignore"
func StartWithDot() MatcherFunc {
return func(el Elem) bool {
name := el.Name()
return len(name) > 0 && name[0] == '.'
}
}
// MatchDotFile match dot filename. eg: ".idea"
func MatchDotFile() MatcherFunc {
return func(el Elem) bool {
return !el.IsDir() && el.Name()[0] == '.'
}
}
// MatchDotDir match dot dirname. eg: ".idea"
func MatchDotDir() MatcherFunc {
return func(el Elem) bool {
return el.IsDir() && el.Name()[0] == '.'
}
}
// MatchExt match filepath by given file ext.
//
// Usage:
//
// f := NewFinder('path/to/dir')
// f.Add(MatchExt(".go"))
// f.Not(MatchExt(".md"))
func MatchExt(exts ...string) MatcherFunc { return MatchExts(exts) }
// MatchExts filter filepath by given file ext.
func MatchExts(exts []string) MatcherFunc {
return func(el Elem) bool {
elExt := path.Ext(el.Name())
for _, ext := range exts {
if ext == elExt {
return true
}
}
return false
}
}
// MatchName match filepath by given names.
//
// Usage:
//
// f := NewFinder('path/to/dir')
// f.Not(MatchName("README.md", "*_test.go"))
func MatchName(names ...string) MatcherFunc { return MatchNames(names) }
// MatchNames match filepath by given names.
func MatchNames(names []string) MatcherFunc {
return func(el Elem) bool {
elName := el.Name()
for _, name := range names {
if name == elName || nfs.PathMatch(name, elName) {
return true
}
}
return false
}
}
// MatchPrefix match filepath by check given prefixes.
//
// Usage:
//
// f := NewFinder('path/to/dir')
// f.Add(finder.MatchPrefix("app_", "README"))
func MatchPrefix(prefixes ...string) MatcherFunc { return MatchPrefixes(prefixes) }
// MatchPrefixes match filepath by check given prefixes.
func MatchPrefixes(prefixes []string) MatcherFunc {
return func(el Elem) bool {
for _, pfx := range prefixes {
if strings.HasPrefix(el.Name(), pfx) {
return true
}
}
return false
}
}
// MatchSuffix match filepath by check path has suffixes.
//
// Usage:
//
// f := NewFinder('path/to/dir')
// f.Add(finder.MatchSuffix("util.go", "en.md"))
// f.Not(finder.MatchSuffix("_test.go", ".log"))
func MatchSuffix(suffixes ...string) MatcherFunc { return MatchSuffixes(suffixes) }
// MatchSuffixes match filepath by check path has suffixes.
func MatchSuffixes(suffixes []string) MatcherFunc {
return func(el Elem) bool {
for _, sfx := range suffixes {
if strings.HasSuffix(el.Path(), sfx) {
return true
}
}
return false
}
}
// MatchPath match file/dir by given sub paths.
//
// Usage:
//
// f := NewFinder('path/to/dir')
// f.Add(MatchPath("need/path"))
func MatchPath(subPaths []string) MatcherFunc { return MatchPaths(subPaths) }
// MatchPaths match file/dir by given sub paths.
func MatchPaths(subPaths []string) MatcherFunc {
return func(el Elem) bool {
for _, subPath := range subPaths {
if strings.Contains(el.Path(), subPath) {
return true
}
}
return false
}
}
// GlobMatch file/dir name by given patterns.
//
// Usage:
//
// f := NewFinder('path/to/dir')
// f.AddFilter(GlobMatch("*_test.go"))
func GlobMatch(patterns ...string) MatcherFunc { return GlobMatches(patterns) }
// GlobMatches file/dir name by given patterns.
func GlobMatches(patterns []string) MatcherFunc {
return func(el Elem) bool {
for _, pattern := range patterns {
if ok, _ := path.Match(pattern, el.Name()); ok {
return true
}
}
return false
}
}
// RegexMatch match name by given regex pattern
//
// Usage:
//
// f := NewFinder('path/to/dir')
// f.AddFilter(RegexMatch(`[A-Z]\w+`))
func RegexMatch(pattern string) MatcherFunc {
reg := regexp.MustCompile(pattern)
return func(el Elem) bool {
return reg.MatchString(el.Name())
}
}
// NameLike exclude filepath by given name match.
func NameLike(patterns ...string) MatcherFunc { return NameLikes(patterns) }
// NameLikes filter filepath by given name match.
func NameLikes(patterns []string) MatcherFunc {
return func(el Elem) bool {
for _, pattern := range patterns {
if nstr.LikeMatch(pattern, el.Name()) {
return true
}
}
return false
}
}
//
// ----------------- built in file info filters -----------------
//
// MatchMtime match file by modify time.
//
// Note: if time is zero, it will be ignored.
//
// Usage:
//
// f := NewFinder('path/to/dir')
// // -600 seconds to now(last 10 minutes)
// f.AddFile(MatchMtime(timex.NowAddSec(-600), timex.ZeroTime))
// // before 600 seconds(before 10 minutes)
// f.AddFile(MatchMtime(timex.ZeroTime, timex.NowAddSec(-600)))
func MatchMtime(start, end time.Time) MatcherFunc {
return MatchModTime(start, end)
}
// MatchModTime filter file by modify time.
func MatchModTime(start, end time.Time) MatcherFunc {
return func(el Elem) bool {
if el.IsDir() {
return false
}
fi, err := el.Info()
if err != nil {
return false
}
return ntime.InRange(fi.ModTime(), start, end)
}
}
var timeNumReg = regexp.MustCompile(`(-?\d+)`)
// HumanModTime filter file by modify time string.
//
// Usage:
//
// f := EmptyFinder()
// f.AddFilter(HumanModTime(">10m")) // before 10 minutes
// f.AddFilter(HumanModTime("<10m")) // latest 10 minutes, to Now
func HumanModTime(expr string) MatcherFunc {
opt := &ntime.ParseRangeOpt{AutoSort: true}
// convert > to <, < to >
expr = nstr.Replaces(expr, map[string]string{">": "<", "<": ">"})
expr = timeNumReg.ReplaceAllStringFunc(expr, func(s string) string {
if s[0] == '-' {
return s
}
return "-" + s
})
start, end, err := ntime.ParseRange(expr, opt)
if err != nil {
panic(err)
}
return MatchModTime(start, end)
}
// FileSize match file by file size. unit: byte
func FileSize(min, max uint64) MatcherFunc { return SizeRange(min, max) }
// SizeRange match file by file size. unit: byte
func SizeRange(min, max uint64) MatcherFunc {
return func(el Elem) bool {
if el.IsDir() {
return false
}
fi, err := el.Info()
if err != nil {
return false
}
return nmath.InUintRange(uint64(fi.Size()), min, max)
}
}
// HumanSize match file by file size string. eg: ">1k", "<2m", "1g~3g"
func HumanSize(expr string) MatcherFunc {
min, max, err := nstr.ParseSizeRange(expr, nil)
if err != nil {
panic(err)
}
return SizeRange(min, max)
}