photoprism/pkg/fs/ignore.go

174 lines
4.0 KiB
Go

package fs
import (
"errors"
"fmt"
"path/filepath"
"strings"
)
type IgnoreLogFunc func(fileName string)
// IgnoreItem represents a file name pattern to be ignored.
type IgnoreItem struct {
Dir string
Pattern string
}
// NewIgnoreItem returns a pointer to a new IgnoreItem instance.
func NewIgnoreItem(dir, pattern string, caseSensitive bool) IgnoreItem {
if caseSensitive {
return IgnoreItem{Dir: dir + PathSeparator, Pattern: pattern}
} else {
return IgnoreItem{Dir: strings.ToLower(dir) + PathSeparator, Pattern: strings.ToLower(pattern)}
}
}
// Ignore returns true if the file name "base" in the directory "dir" should be ignored.
func (i IgnoreItem) Ignore(dir, base string) bool {
if !strings.HasPrefix(dir+PathSeparator, i.Dir) {
// different directory prefix: don't look any further
return false
} else if i.Pattern == base {
// file name is the same as pattern (no wildcard)
return true
}
if strings.ContainsRune(i.Pattern, filepath.Separator) {
base = filepath.Join(RelName(dir, i.Dir), base)
}
if ignore, err := filepath.Match(i.Pattern, base); ignore && err == nil {
return true
}
return false
}
// IgnoreList represents a list of name patterns to be ignored.
type IgnoreList struct {
Log IgnoreLogFunc
items []IgnoreItem
hiddenFiles []string
ignoredFiles []string
configFiles map[string][]string
configFile string
ignoreHidden bool
caseSensitive bool
}
// NewIgnoreList returns a pointer to a new IgnoreList instance.
func NewIgnoreList(configFile string, ignoreHidden bool, caseSensitive bool) *IgnoreList {
return &IgnoreList{
configFile: configFile,
ignoreHidden: ignoreHidden,
caseSensitive: caseSensitive,
configFiles: make(map[string][]string),
}
}
// Hidden returns hidden files that were ignored.
func (l *IgnoreList) Hidden() []string {
return l.hiddenFiles
}
// Ignored returns files that were ignored in addition to hidden files.
func (l *IgnoreList) Ignored() []string {
return l.ignoredFiles
}
// AppendItems adds items to the list of ignored items.
func (l *IgnoreList) AppendItems(dir string, patterns []string) error {
if dir == "" {
return errors.New("empty directory name")
}
for _, pattern := range patterns {
if pattern != "" && !strings.HasPrefix(pattern, "#") {
l.items = append(l.items, NewIgnoreItem(dir, pattern, l.caseSensitive))
}
}
return nil
}
// ConfigFile adds items in fileName to the list of ignored items.
func (l *IgnoreList) ConfigFile(fileName string) error {
items, err := ReadLines(fileName)
if err != nil {
return err
}
l.configFiles[fileName] = items
return l.AppendItems(filepath.Dir(fileName), items)
}
// Dir adds the ignore file in dirName to the list of ignored items.
func (l *IgnoreList) Dir(dir string) error {
if dir == "" {
return errors.New("empty directory name")
}
if l.configFile == "" {
return errors.New("empty ignore file name")
}
fileName := filepath.Join(dir, l.configFile)
if _, ok := l.configFiles[fileName]; ok {
return nil
}
if !FileExists(fileName) {
return fmt.Errorf("found no %s file", l.configFile)
}
return l.ConfigFile(fileName)
}
// Ignore returns true if the file name should be ignored.
func (l *IgnoreList) Ignore(fileName string) bool {
dir := filepath.Dir(fileName)
base := filepath.Base(fileName)
if l.caseSensitive == false {
dir = strings.ToLower(dir)
base = strings.ToLower(base)
}
if l.configFile != "" && base == l.configFile {
_ = l.ConfigFile(fileName)
return true
}
for _, item := range l.items {
if item.Ignore(dir, base) {
l.ignoredFiles = append(l.ignoredFiles, fileName)
if l.Log != nil {
l.Log(fileName)
}
return true
}
}
if l.ignoreHidden && FileNameHidden(fileName) {
l.hiddenFiles = append(l.hiddenFiles, fileName)
return true
}
return false
}
// Reset resets ignored and hidden files.
func (l *IgnoreList) Reset() {
l.items = []IgnoreItem{}
l.hiddenFiles = []string{}
l.ignoredFiles = []string{}
l.configFiles = make(map[string][]string)
}