Documentation
¶
Overview ¶
Package match provides object-key pattern matching for gonimbus workflows.
API stability: Stable. Breaking changes to exported symbols or documented behavior follow the Library API protocol in docs/api-stability.md.
Package match provides pattern matching for cloud object keys using doublestar semantics with prefix derivation for efficient listing.
Index ¶
- Constants
- Variables
- func DerivePrefix(pattern string) string
- func DerivePrefixes(patterns []string) []string
- func EnsureTrailingSlash(key string) string
- func FormatSize(bytes int64) string
- func HasEmptyPrefix(patterns []string) bool
- func HasTrailingSlash(key string) bool
- func IsGlobPattern(pattern string) bool
- func IsHidden(key string) bool
- func JoinKeySegments(segments []string) string
- func Normalize(s string) string
- func NormalizeKey(key string) string
- func NormalizePattern(pattern string) string
- func ParseDate(s string) (time.Time, error)
- func ParseSize(s string) (int64, error)
- func SplitKeySegments(key string) []string
- type CompositeFilter
- type Config
- type DateFilter
- type DateFilterConfig
- type Filter
- type FilterConfig
- type Matcher
- type PatternError
- type RegexFilter
- type SizeFilter
- type SizeFilterConfig
Constants ¶
const ( Byte int64 = 1 // Base-10 (SI) units KB int64 = 1000 MB int64 = 1000 * KB GB int64 = 1000 * MB TB int64 = 1000 * GB // Base-2 (IEC) units KiB int64 = 1024 MiB int64 = 1024 * KiB GiB int64 = 1024 * MiB TiB int64 = 1024 * GiB )
Size unit multipliers.
Variables ¶
var ( ErrInvalidSize = errors.New("invalid size value") ErrInvalidDate = errors.New("invalid date value") ErrInvalidRegex = errors.New("invalid regex pattern") ErrUnsupportedFilter = errors.New("unsupported filter") )
Filter errors.
var ( // ErrNoIncludes is returned when no include patterns are provided. ErrNoIncludes = errors.New("at least one include pattern is required") // ErrInvalidPattern is returned when a pattern cannot be compiled. ErrInvalidPattern = errors.New("invalid glob pattern") )
Errors returned by Matcher operations.
Functions ¶
func DerivePrefix ¶
DerivePrefix extracts the longest static prefix from a glob pattern.
The prefix is the portion of the pattern before any unescaped glob metacharacter. Escaped metacharacters (\*, \?, \[, \{) are treated as literals and included in the prefix. This prefix can be used to efficiently filter cloud storage listings.
Examples:
"data/2024/**/*.parquet" → "data/2024/"
"*.json" → ""
"logs/app-{a,b}/*.log" → "logs/"
"exact/path/file.txt" → "exact/path/file.txt"
"data/[0-9]*/*.csv" → "data/"
"prefix/" → "prefix/"
"data/file\*.txt" → "data/file*.txt" (escaped * is literal)
"data/\[backup\]/*.log" → "data/[backup]/" (escaped brackets are literal)
func DerivePrefixes ¶
DerivePrefixes extracts prefixes from multiple patterns and deduplicates them.
The returned prefixes are:
- Derived from each include pattern
- Deduplicated (parent prefixes subsume children)
- Sorted for deterministic ordering
Examples:
["data/2024/**", "data/2025/**"] → ["data/2024/", "data/2025/"] ["data/**", "data/2024/**"] → ["data/"] (parent subsumes child) ["**/*.json"] → [""] (empty = full listing)
func EnsureTrailingSlash ¶
EnsureTrailingSlash adds a trailing slash if not present. Returns empty string unchanged.
func FormatSize ¶
FormatSize formats bytes as human-readable string using base-2 units.
func HasEmptyPrefix ¶
HasEmptyPrefix returns true if any derived prefix is empty. An empty prefix means a full bucket listing is required. This is a scale concern and callers may want to warn users.
func HasTrailingSlash ¶
HasTrailingSlash returns true if the key ends with a slash. This typically indicates a prefix/directory rather than a file.
func IsGlobPattern ¶
IsGlobPattern returns true if the pattern contains unescaped glob metacharacters.
This is escape-aware: literal metacharacters escaped with backslash (\*, \?, \[, \{) are not considered glob characters. This allows matching filenames that contain literal asterisks, question marks, or brackets.
Examples:
"data/**/*.parquet" → true (unescaped glob) "data/file\*.txt" → false (escaped asterisk is literal) "data/file?.csv" → true (unescaped question mark) "path/to/file.txt" → false (no metacharacters)
func IsHidden ¶
IsHidden returns true if any path segment starts with a dot.
Hidden segments follow Unix convention where files/directories starting with '.' are considered hidden.
The key is matched as-is without normalization, using '/' as separator.
Examples:
"path/to/file.txt" → false ".hidden/file.txt" → true "path/.hidden/file.txt" → true "path/to/.gitignore" → true "path/to/file.txt." → false (dot at end is not hidden)
func JoinKeySegments ¶
JoinKeySegments joins path segments into a key using / as separator.
func Normalize ¶
Normalize is deprecated. Use NormalizeKey for keys or NormalizePattern for patterns.
This function exists for backward compatibility and calls NormalizePattern. New code should use the specific functions.
func NormalizeKey ¶
NormalizeKey returns a cloud object key unchanged.
Cloud storage keys (S3, GCS) are opaque strings where any character is valid, including /, //, leading /, *, ?, etc. We preserve key identity exactly as returned by the provider.
This function exists for documentation and potential future options. Currently it returns the key unchanged.
func NormalizePattern ¶
NormalizePattern converts a user-provided glob pattern to canonical form.
Normalization rules:
- Unescaped backslashes converted to forward slashes (Windows compat)
- Escaped backslashes and glob metacharacters preserved (\*, \?, \[, etc.)
- Leading slash, trailing slash, and // sequences preserved
This allows Windows users to write patterns like "data\2024\**\*.parquet" while preserving escape semantics for literal matching.
Examples:
"data/2024/**" → "data/2024/**" (unchanged) "data\2024\**" → "data/2024/**" (backslash → slash) "data/file\*.txt" → "data/file\*.txt" (escape preserved) "data\\backup\\*" → "data/backup/*" (unescaped \ → /) "/data/2024/**" → "/data/2024/**" (leading slash preserved) "data//2024/**" → "data//2024/**" (// preserved)
func ParseDate ¶
ParseDate parses an ISO 8601 date or datetime string.
Supported formats:
- Date only: "2024-01-15" (interpreted as start of day UTC)
- Datetime: "2024-01-15T10:30:00Z"
- Datetime with offset: "2024-01-15T10:30:00+05:00"
All times are normalized to UTC for comparison.
func ParseSize ¶
ParseSize parses a human-readable size string.
Supported formats:
- Raw bytes: "1024", "104857600"
- Base-10 (SI): "1KB", "100MB", "1GB" (1KB = 1000 bytes)
- Base-2 (IEC): "1KiB", "100MiB", "1GiB" (1KiB = 1024 bytes)
- Case insensitive: "1kb", "1KB", "1Kb" all work
Note: KB/MB/GB use base-10 (SI standard), KiB/MiB/GiB use base-2 (IEC).
func SplitKeySegments ¶
SplitKeySegments splits a key into path segments using / as separator. Empty segments are preserved (for keys with // sequences).
Types ¶
type CompositeFilter ¶
type CompositeFilter struct {
// contains filtered or unexported fields
}
CompositeFilter combines multiple filters with AND semantics. All filters must pass for the object to match.
func NewCompositeFilter ¶
func NewCompositeFilter(filters ...Filter) *CompositeFilter
NewCompositeFilter creates a composite filter from the given filters. Nil filters are ignored. Returns nil if no non-nil filters provided.
func NewFilterFromConfig ¶
func NewFilterFromConfig(cfg *FilterConfig) (*CompositeFilter, error)
NewFilterFromConfig creates a CompositeFilter from FilterConfig. Returns nil if no filters are configured.
func (*CompositeFilter) Filters ¶
func (f *CompositeFilter) Filters() []Filter
Filters returns the underlying filters.
func (*CompositeFilter) Match ¶
func (f *CompositeFilter) Match(obj *provider.ObjectSummary) bool
Match returns true if all filters pass.
func (*CompositeFilter) RequiresEnrichment ¶
func (f *CompositeFilter) RequiresEnrichment() bool
RequiresEnrichment returns true if any filter requires enrichment.
func (*CompositeFilter) String ¶
func (f *CompositeFilter) String() string
String returns a human-readable description.
type Config ¶
type Config struct {
// Includes are glob patterns that objects must match (at least one).
// Required: at least one include pattern must be specified.
Includes []string
// Excludes are glob patterns that objects must not match (any).
// Optional: if empty, no excludes are applied.
Excludes []string
// IncludeHidden controls whether hidden files are matched.
// Hidden files have path segments starting with '.'.
// Default: false (hidden files are excluded).
IncludeHidden bool
}
Config configures a Matcher.
type DateFilter ¶
type DateFilter struct {
// contains filtered or unexported fields
}
DateFilter filters objects by modification date range.
func NewDateFilter ¶
func NewDateFilter(cfg *DateFilterConfig) (*DateFilter, error)
NewDateFilter creates a date filter from config. Returns nil if no date constraints are specified.
func (*DateFilter) Match ¶
func (f *DateFilter) Match(obj *provider.ObjectSummary) bool
Match returns true if object modification time is within range.
func (*DateFilter) RequiresEnrichment ¶
func (f *DateFilter) RequiresEnrichment() bool
RequiresEnrichment returns false - lastModified is available from List.
func (*DateFilter) String ¶
func (f *DateFilter) String() string
String returns a human-readable description.
type DateFilterConfig ¶
type DateFilterConfig struct {
// After filters to objects modified at or after this time (inclusive).
// Supports ISO 8601: "2024-01-15" or "2024-01-15T10:30:00Z".
After string `json:"after,omitempty" yaml:"after,omitempty"`
// Before filters to objects modified before this time (exclusive end).
// Supports ISO 8601: "2024-01-15" or "2024-01-15T10:30:00Z".
Before string `json:"before,omitempty" yaml:"before,omitempty"`
}
DateFilterConfig specifies date range constraints.
type Filter ¶
type Filter interface {
// Match returns true if the object passes the filter.
Match(obj *provider.ObjectSummary) bool
// RequiresEnrichment returns true if filter needs HEAD metadata.
// Size, date, and regex filters do not require enrichment.
// ContentType filters do require enrichment.
RequiresEnrichment() bool
// String returns a human-readable description of the filter.
String() string
}
Filter evaluates whether an object passes filter criteria.
Filters operate on ObjectSummary data available from List operations (key, size, lastModified). ContentType filtering requires enrichment via HEAD calls and is handled separately.
type FilterConfig ¶
type FilterConfig struct {
// Size specifies min/max size constraints.
Size *SizeFilterConfig `json:"size,omitempty" yaml:"size,omitempty"`
// Modified specifies date range constraints.
Modified *DateFilterConfig `json:"modified,omitempty" yaml:"modified,omitempty"`
// ContentType specifies allowed MIME types (requires enrichment).
ContentType []string `json:"content_type,omitempty" yaml:"content_type,omitempty"`
// KeyRegex is a regex pattern applied to object keys after glob matching.
KeyRegex string `json:"key_regex,omitempty" yaml:"key_regex,omitempty"`
}
FilterConfig holds filter criteria from manifest or CLI flags.
type Matcher ¶
type Matcher struct {
// contains filtered or unexported fields
}
Matcher evaluates patterns against cloud object keys.
A Matcher is configured with include and exclude patterns:
- Include patterns: object must match at least one
- Exclude patterns: object must not match any
The Matcher is safe for concurrent use after creation.
func New ¶
New creates a new Matcher from the given configuration.
Patterns are normalized to handle Windows-style backslash separators while preserving escape sequences for literal glob metacharacters.
Returns an error if:
- No include patterns are provided
- Any pattern is invalid (cannot be compiled)
func (*Matcher) ExcludePatterns ¶
ExcludePatterns returns the raw exclude patterns.
func (*Matcher) HasEmptyPrefix ¶
HasEmptyPrefix returns true if any prefix is empty (requires full listing).
func (*Matcher) IncludePatterns ¶
IncludePatterns returns the raw include patterns.
func (*Matcher) Match ¶
Match returns true if the key matches the include/exclude patterns.
A key matches if:
- It matches at least one include pattern
- It does not match any exclude pattern
- It is not hidden (unless IncludeHidden is true)
Keys are matched as-is (not normalized) since cloud storage keys are opaque strings where any character is valid.
func (*Matcher) Prefixes ¶
Prefixes returns the deduplicated list prefixes for efficient listing.
These prefixes can be used to filter cloud storage list operations, reducing the number of objects that need to be retrieved and evaluated.
An empty string in the result means at least one pattern requires a full bucket listing (no prefix filter possible).
type PatternError ¶
PatternError wraps pattern-related errors with context.
func (*PatternError) Error ¶
func (e *PatternError) Error() string
func (*PatternError) Unwrap ¶
func (e *PatternError) Unwrap() error
type RegexFilter ¶
type RegexFilter struct {
// contains filtered or unexported fields
}
RegexFilter filters objects by key pattern.
func NewRegexFilter ¶
func NewRegexFilter(pattern string) (*RegexFilter, error)
NewRegexFilter creates a regex filter from pattern string. Returns nil if pattern is empty.
func (*RegexFilter) Match ¶
func (f *RegexFilter) Match(obj *provider.ObjectSummary) bool
Match returns true if object key matches the regex.
func (*RegexFilter) RequiresEnrichment ¶
func (f *RegexFilter) RequiresEnrichment() bool
RequiresEnrichment returns false - key is available from List.
func (*RegexFilter) String ¶
func (f *RegexFilter) String() string
String returns a human-readable description.
type SizeFilter ¶
type SizeFilter struct {
// contains filtered or unexported fields
}
SizeFilter filters objects by size range.
func NewSizeFilter ¶
func NewSizeFilter(cfg *SizeFilterConfig) (*SizeFilter, error)
NewSizeFilter creates a size filter from config. Returns nil if no size constraints are specified.
func (*SizeFilter) Match ¶
func (f *SizeFilter) Match(obj *provider.ObjectSummary) bool
Match returns true if object size is within the configured range.
func (*SizeFilter) RequiresEnrichment ¶
func (f *SizeFilter) RequiresEnrichment() bool
RequiresEnrichment returns false - size is available from List.
func (*SizeFilter) String ¶
func (f *SizeFilter) String() string
String returns a human-readable description.
type SizeFilterConfig ¶
type SizeFilterConfig struct {
// Min is the minimum size (inclusive). Supports human-readable: "1KB", "100MiB".
Min string `json:"min,omitempty" yaml:"min,omitempty"`
// Max is the maximum size (inclusive). Supports human-readable: "1GB", "100MiB".
Max string `json:"max,omitempty" yaml:"max,omitempty"`
}
SizeFilterConfig specifies size constraints.