From 2eea15fb90c790aae82606df710a30c9e41832e1 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Fri, 22 Nov 2024 21:47:16 +1100 Subject: [PATCH] refactor glob matcher (& improve performance) --- Sources/dpk-cli/GlobMatcher.swift | 42 +++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/Sources/dpk-cli/GlobMatcher.swift b/Sources/dpk-cli/GlobMatcher.swift index d60132a..56b550e 100644 --- a/Sources/dpk-cli/GlobMatcher.swift +++ b/Sources/dpk-cli/GlobMatcher.swift @@ -7,32 +7,48 @@ import Foundation import ArgumentParser struct GlobMatcher: PatternMatcher { - private let _patterns: [String] + private let _patterns: [Pattern] private let _flags: Int32 init(patterns: [String], ignoreCase: Bool) throws(ArgumentParser.ExitCode) { - self._patterns = patterns + // Quick hack to make matching without explicit globs easier + let globChars = CharacterSet(charactersIn: "*?[]") + self._patterns = patterns.map { pattern in + if pattern.unicodeScalars.contains(where: globChars.contains) { + .wildcard(glob: pattern) + } else { + .globless(match: pattern) + } + } self._flags = ignoreCase ? FNM_CASEFOLD : 0 } func match(_ field: String) -> Bool { for pattern in self._patterns { - // Quick hack to make matching without explicit globs easier - if pattern.rangeOfCharacter(from: .init(charactersIn: "*?[]")) == nil { + switch pattern { + case .globless(let match): if self._flags & FNM_CASEFOLD != 0 { - return field.localizedCaseInsensitiveContains(pattern) + return field.localizedCaseInsensitiveContains(match) } else { - return field.contains(pattern) + return field.contains(match) } + case .wildcard(let glob): + let res = fnmatch(glob, field, self._flags) + if res == FNM_NOMATCH { + continue + } else if res == 0 { + return true + } + fatalError("fnmatch error \(res)") } - let res = fnmatch(pattern, field, self._flags) - if res == FNM_NOMATCH { - continue - } else if res == 0 { - return true - } - fatalError("fnmatch error \(res)") } return false } } + +private extension GlobMatcher { + enum Pattern { + case wildcard(glob: String) + case globless(match: String) + } +}