package archive import ( "path" "strings" ) // Matcher проверяет абсолютные пути (со слешами) по списку исключений. // // - шаблон без glob-символов — префикс: "/opt/x/pgdata" исключает и сам каталог, и всё внутри; // - шаблон с "/" и glob-символами — сопоставляется с полным путём ("/etc/x-ui/x-ui.db*"); // - шаблон без "/" — с именем файла ("*.swp"). type Matcher struct { patterns []string } func NewMatcher(patterns ...[]string) *Matcher { m := &Matcher{} for _, ps := range patterns { for _, p := range ps { p = strings.TrimSpace(p) if p == "" { continue } if p != "/" { p = strings.TrimSuffix(p, "/") } m.patterns = append(m.patterns, p) } } return m } func (m *Matcher) Add(p ...string) { m.patterns = append(m.patterns, NewMatcher(p).patterns...) } func (m *Matcher) Match(abs string) bool { if m == nil { return false } for _, p := range m.patterns { if !strings.ContainsAny(p, "*?[") { if abs == p || strings.HasPrefix(abs, p+"/") { return true } continue } if strings.Contains(p, "/") { if ok, _ := path.Match(p, abs); ok { return true } continue } if ok, _ := path.Match(p, path.Base(abs)); ok { return true } } return false } // Covers сообщает, попадает ли abs под один из путей (точное совпадение или вложенность). func Covers(paths []string, abs string) bool { for _, p := range paths { p = strings.TrimSuffix(p, "/") if abs == p || strings.HasPrefix(abs, p+"/") { return true } } return false }