match

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

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

View Source
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

View Source
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.

View Source
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

func DerivePrefix(pattern string) string

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

func DerivePrefixes(patterns []string) []string

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

func EnsureTrailingSlash(key string) string

EnsureTrailingSlash adds a trailing slash if not present. Returns empty string unchanged.

func FormatSize

func FormatSize(bytes int64) string

FormatSize formats bytes as human-readable string using base-2 units.

func HasEmptyPrefix

func HasEmptyPrefix(patterns []string) bool

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

func HasTrailingSlash(key string) bool

HasTrailingSlash returns true if the key ends with a slash. This typically indicates a prefix/directory rather than a file.

func IsGlobPattern

func IsGlobPattern(pattern string) bool

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

func IsHidden(key string) bool

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

func JoinKeySegments(segments []string) string

JoinKeySegments joins path segments into a key using / as separator.

func Normalize

func Normalize(s string) string

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

func NormalizeKey(key string) string

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

func NormalizePattern(pattern string) string

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

func ParseDate(s string) (time.Time, error)

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

func ParseSize(s string) (int64, error)

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

func SplitKeySegments(key string) []string

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

func New(cfg Config) (*Matcher, error)

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

func (m *Matcher) ExcludePatterns() []string

ExcludePatterns returns the raw exclude patterns.

func (*Matcher) HasEmptyPrefix

func (m *Matcher) HasEmptyPrefix() bool

HasEmptyPrefix returns true if any prefix is empty (requires full listing).

func (*Matcher) IncludePatterns

func (m *Matcher) IncludePatterns() []string

IncludePatterns returns the raw include patterns.

func (*Matcher) Match

func (m *Matcher) Match(key string) bool

Match returns true if the key matches the include/exclude patterns.

A key matches if:

  1. It matches at least one include pattern
  2. It does not match any exclude pattern
  3. 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

func (m *Matcher) Prefixes() []string

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

type PatternError struct {
	Pattern string
	Err     error
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL