lib/ignore: Refactor out result type (#9343)

This commit is contained in:
Jakob Borg
2024-01-13 18:58:23 +01:00
committed by GitHub
parent 36e08f8eee
commit e041877488
12 changed files with 155 additions and 87 deletions
+8 -4
View File
@@ -6,7 +6,11 @@
package ignore
import "time"
import (
"time"
"github.com/syncthing/syncthing/lib/ignore/ignoreresult"
)
type nower interface {
Now() time.Time
@@ -20,7 +24,7 @@ type cache struct {
}
type cacheEntry struct {
result Result
result ignoreresult.R
access int64 // Unix nanosecond count. Sufficient until the year 2262.
}
@@ -39,7 +43,7 @@ func (c *cache) clean(d time.Duration) {
}
}
func (c *cache) get(key string) (Result, bool) {
func (c *cache) get(key string) (ignoreresult.R, bool) {
entry, ok := c.entries[key]
if ok {
entry.access = clock.Now().UnixNano()
@@ -48,7 +52,7 @@ func (c *cache) get(key string) (Result, bool) {
return entry.result, ok
}
func (c *cache) set(key string, result Result) {
func (c *cache) set(key string, result ignoreresult.R) {
c.entries[key] = cacheEntry{result, time.Now().UnixNano()}
}
+3 -1
View File
@@ -9,6 +9,8 @@ package ignore
import (
"testing"
"time"
"github.com/syncthing/syncthing/lib/ignore/ignoreresult"
)
func TestCache(t *testing.T) {
@@ -28,7 +30,7 @@ func TestCache(t *testing.T) {
// Set and check some items
c.set("true", resultInclude|resultDeletable)
c.set("true", ignoreresult.IgnoredDeletable)
c.set("false", 0)
res, ok = c.get("true")
+23 -60
View File
@@ -18,28 +18,13 @@ import (
"github.com/gobwas/glob"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore/ignoreresult"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/sha256"
"github.com/syncthing/syncthing/lib/sync"
)
const (
resultNotMatched Result = 0
resultInclude Result = 1 << iota
resultDeletable = 1 << iota
resultFoldCase = 1 << iota
)
var defaultResult Result = resultInclude
func init() {
if build.IsDarwin || build.IsWindows {
defaultResult |= resultFoldCase
}
}
// A ParseError signifies an error with contents of an ignore file,
// including I/O errors on included files. An I/O error on the root level
// ignore file is not a ParseError.
@@ -70,18 +55,18 @@ func parseError(err error) error {
type Pattern struct {
pattern string
match glob.Glob
result Result
result ignoreresult.R
}
func (p Pattern) String() string {
ret := p.pattern
if p.result&resultInclude != resultInclude {
if !p.result.IsIgnored() {
ret = "!" + ret
}
if p.result&resultFoldCase == resultFoldCase {
if p.result.IsCaseFolded() {
ret = "(?i)" + ret
}
if p.result&resultDeletable == resultDeletable {
if p.result.IsDeletable() {
ret = "(?d)" + ret
}
return ret
@@ -101,20 +86,6 @@ func (p Pattern) allowsSkippingIgnoredDirs() bool {
return !strings.Contains(strings.TrimSuffix(p.pattern, "**"), "**")
}
type Result uint8
func (r Result) IsIgnored() bool {
return r&resultInclude == resultInclude
}
func (r Result) IsDeletable() bool {
return r.IsIgnored() && r&resultDeletable == resultDeletable
}
func (r Result) IsCaseFolded() bool {
return r&resultFoldCase == resultFoldCase
}
// The ChangeDetector is responsible for determining if files have changed
// on disk. It gets told to Remember() files (name and modtime) and will
// then get asked if a file has been Seen() (i.e., Remember() has been
@@ -253,16 +224,24 @@ func (m *Matcher) parseLocked(r io.Reader, file string) error {
return err
}
func (m *Matcher) Match(file string) (result Result) {
if file == "." {
return resultNotMatched
// Match matches the patterns plus temporary and internal files.
func (m *Matcher) Match(file string) (result ignoreresult.R) {
switch {
case fs.IsTemporary(file):
return ignoreresult.Ignored
case fs.IsInternal(file):
return ignoreresult.Ignored
case file == ".":
return ignoreresult.NotIgnored
}
m.mut.Lock()
defer m.mut.Unlock()
if len(m.patterns) == 0 {
return resultNotMatched
return ignoreresult.NotIgnored
}
if m.matches != nil {
@@ -295,7 +274,7 @@ func (m *Matcher) Match(file string) (result Result) {
}
// Default to not matching.
return resultNotMatched
return ignoreresult.NotIgnored
}
// Lines return a list of the unprocessed lines in .stignore at last load
@@ -348,22 +327,6 @@ func (m *Matcher) clean(d time.Duration) {
}
}
// ShouldIgnore returns true when a file is temporary, internal or ignored
func (m *Matcher) ShouldIgnore(filename string) bool {
switch {
case fs.IsTemporary(filename):
return true
case fs.IsInternal(filename):
return true
case m.Match(filename).IsIgnored():
return true
}
return false
}
func (m *Matcher) SkipIgnoredDirs() bool {
m.mut.Lock()
defer m.mut.Unlock()
@@ -414,7 +377,7 @@ func loadParseIncludeFile(filesystem fs.Filesystem, file string, cd ChangeDetect
// isNotExist is considered "ok" in a sense of that a folder doesn't have to act
// upon it. This is because it is allowed for .stignore to not exist. However,
// included ignore files are not allowed to be missing and these errors should be
// acted upon on. So we don't perserve the error chain here and manually set an
// acted upon on. So we don't preserve the error chain here and manually set an
// error instead, if the file is missing.
if fs.IsNotExist(err) {
err = errors.New("file not found")
@@ -431,7 +394,7 @@ func loadParseIncludeFile(filesystem fs.Filesystem, file string, cd ChangeDetect
func parseLine(line string) ([]Pattern, error) {
pattern := Pattern{
result: defaultResult,
result: ignoreresult.Ignored,
}
// Allow prefixes to be specified in any order, but only once.
@@ -441,14 +404,14 @@ func parseLine(line string) ([]Pattern, error) {
if strings.HasPrefix(line, "!") && !seenPrefix[0] {
seenPrefix[0] = true
line = line[1:]
pattern.result ^= resultInclude
pattern.result = pattern.result.ToggleIgnored()
} else if strings.HasPrefix(line, "(?i)") && !seenPrefix[1] {
seenPrefix[1] = true
pattern.result |= resultFoldCase
pattern.result = pattern.result.WithFoldCase()
line = line[4:]
} else if strings.HasPrefix(line, "(?d)") && !seenPrefix[2] {
seenPrefix[2] = true
pattern.result |= resultDeletable
pattern.result = pattern.result.WithDeletable()
line = line[4:]
} else {
break
+2 -1
View File
@@ -17,6 +17,7 @@ import (
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore/ignoreresult"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/rand"
)
@@ -415,7 +416,7 @@ func TestCommentsAndBlankLines(t *testing.T) {
}
}
var result Result
var result ignoreresult.R
func BenchmarkMatch(b *testing.B) {
testFs := newTestFS()
+75
View File
@@ -0,0 +1,75 @@
// Copyright (C) 2024 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
// Package result provides the result type for ignore matching. This is a
// separate package in order to break import cycles.
package ignoreresult
const (
NotIgnored R = 0
// `Ignored` is defined in platform specific files
IgnoredDeletable = Ignored | deletableBit
)
const (
// Private definitions of the bits that make up the result value
ignoreBit R = 1 << iota
deletableBit
foldCaseBit
)
type R uint8
// IsIgnored returns true if the result is ignored.
func (r R) IsIgnored() bool {
return r&ignoreBit != 0
}
// IsDeletable returns true if the result is ignored and deletable.
func (r R) IsDeletable() bool {
return r.IsIgnored() && r&deletableBit != 0
}
// IsCaseFolded returns true if the result was a case-insensitive match.
func (r R) IsCaseFolded() bool {
return r&foldCaseBit != 0
}
// ToggleIgnored returns a copy of the result with the ignored bit toggled.
func (r R) ToggleIgnored() R {
return r ^ ignoreBit
}
// WithDeletable returns a copy of the result with the deletable bit set.
func (r R) WithDeletable() R {
return r | deletableBit
}
// WithFoldCase returns a copy of the result with the fold case bit set.
func (r R) WithFoldCase() R {
return r | foldCaseBit
}
// String returns a human readable representation of the result flags.
func (r R) String() string {
var s string
if r&ignoreBit != 0 {
s += "i"
} else {
s += "-"
}
if r&deletableBit != 0 {
s += "d"
} else {
s += "-"
}
if r&foldCaseBit != 0 {
s += "f"
} else {
s += "-"
}
return s
}
@@ -0,0 +1,11 @@
// Copyright (C) 2024 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
//go:build windows || darwin
package ignoreresult
const Ignored = ignoreBit | foldCaseBit
@@ -0,0 +1,11 @@
// Copyright (C) 2024 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
//go:build !windows && !darwin
package ignoreresult
const Ignored = ignoreBit