hako/internal/archival/rules/engine.go

67 lines
1.8 KiB
Go

package rules
import (
"context"
"fmt"
"log/slog"
)
// Engine evaluates rules against URL metadata
type Engine struct {
config *RuleConfig
logger *slog.Logger
}
// NewEngine creates a new rule engine with the given configuration
func NewEngine(config *RuleConfig) *Engine {
return &Engine{
config: config,
logger: slog.Default(),
}
}
// NewEngineWithLogger creates a new rule engine with the given configuration and logger
func NewEngineWithLogger(config *RuleConfig, logger *slog.Logger) *Engine {
return &Engine{
config: config,
logger: logger,
}
}
// Evaluate evaluates rules against URL metadata and returns archivers
// Returns archivers from the first matching rule, or default archiver if no rules match
func (e *Engine) Evaluate(ctx context.Context, metadata *URLMetadata) ([]ArchiverConfig, error) {
if e.config == nil {
return nil, fmt.Errorf("rule engine: configuration is nil")
}
// Validate all rules
for i, rule := range e.config.Rules {
if err := rule.IsValid(); err != nil {
return nil, fmt.Errorf("rule engine: rule %d is invalid: %w", i, err)
}
}
// Evaluate rules in order
for i, rule := range e.config.Rules {
archivers, err := rule.Matches(ctx, metadata)
if err != nil {
return nil, fmt.Errorf("rule engine: error evaluating rule %d: %w", i, err)
}
// If rule matched (returned archivers, including empty slice), use them and STOP
if archivers != nil {
e.logger.Info("Rule matched, stopping evaluation", "ruleIndex", i, "archiverCount", len(archivers))
return archivers, nil
}
}
// No rules matched, use default archivers
if len(e.config.DefaultArchivers) == 0 {
return nil, fmt.Errorf("rule engine: no rules matched and no default archivers configured")
}
e.logger.Info("No rules matched, using default archivers", "archivers", e.config.DefaultArchivers)
return e.config.DefaultArchivers, nil
}