Documentation
¶
Overview ¶
Package gorulesengine provides a powerful and flexible rules engine for Go. It allows you to define business rules in JSON or code, evaluate complex conditions, and trigger events based on dynamic facts.
Index ¶
- Constants
- func DefaultPathResolver(value interface{}, path string) (interface{}, error)
- func RegisterOperator(opType OperatorType, operator Operator)
- type Almanac
- func (a *Almanac) AddEvent(event Event, outcome string)
- func (a *Almanac) AddFact(id FactID, valueOrMethod interface{}, opts ...FactOption) error
- func (a *Almanac) AddResult(result RuleResult)
- func (a *Almanac) GetEvents(outcome string) []Event
- func (a *Almanac) GetFactValue(factID FactID, params map[string]interface{}, path string) (interface{}, error)
- func (a *Almanac) GetFactValueFromCache(factID FactID) (interface{}, bool)
- func (a *Almanac) GetFacts() map[FactID]*Fact
- func (a *Almanac) GetOptions() map[string]interface{}
- func (a *Almanac) GetResults() []RuleResult
- func (a *Almanac) TraversePath(data interface{}, path string) (interface{}, error)
- type AlmanacError
- type AlmanacOption
- type Condition
- type ConditionError
- type ConditionNode
- type ConditionSet
- type ConditionType
- type ContainsOperator
- type Engine
- func (e *Engine) AddFact(fact *Fact)
- func (e *Engine) AddRule(rule *Rule)
- func (e *Engine) On(eventType string, handler EventHandler)
- func (e *Engine) OnFailure(handler EventHandler)
- func (e *Engine) OnSuccess(handler EventHandler)
- func (e *Engine) RegisterCallback(name string, handler EventHandler)
- func (e *Engine) Run(almanac *Almanac) ([]RuleResult, error)
- type EqualOperator
- type ErrorType
- type Event
- type EventHandler
- type EventHandlers
- type Fact
- func (f *Fact) Calculate(params map[string]interface{}) interface{}
- func (f *Fact) FactType() string
- func (f *Fact) GetCacheKey() (string, error)
- func (f *Fact) GetOption(key string) (interface{}, bool)
- func (f *Fact) HasOption(key string) bool
- func (f *Fact) ID() FactID
- func (f *Fact) IsDynamic() bool
- func (f *Fact) ValueOrMethod() interface{}
- type FactError
- type FactID
- type FactOption
- type GreaterThanInclusiveOperator
- type GreaterThanOperator
- type InOperator
- type LessThanInclusiveOperator
- type LessThanOperator
- type NotContainsOperator
- type NotEqualOperator
- type NotInOperator
- type Operator
- type OperatorError
- type OperatorType
- type PathResolver
- type Rule
- type RuleEngineError
- type RuleError
- type RuleResult
Constants ¶
const ( // EventSuccess represents a success event type for rules that matched. EventSuccess = "success" // EventFailure represents a failure event type for rules that did not match. EventFailure = "failure" )
const AlmanacOptionKeyAllowUndefinedFacts = "allowUndefinedFacts"
AlmanacOptionKeyAllowUndefinedFacts is the option key for allowing undefined facts.
const ConstantFact = "__constant_fact__"
ConstantFact identifies a fact with a static, pre-defined value.
const DynamicFact = "__dynamic_fact__"
DynamicFact identifies a fact that computes its value dynamically using a function.
const FactOptionKeyCache = "cache"
FactOptionKeyCache is the key for the caching option in fact options.
const FactOptionKeyPriority = "priority"
FactOptionKeyPriority is the key for the priority option in fact options.
Variables ¶
This section is empty.
Functions ¶
func DefaultPathResolver ¶
DefaultPathResolver implements JSONPath resolution for accessing nested fact values. Example: "$.user.profile.age" accesses deeply nested data.
func RegisterOperator ¶
func RegisterOperator(opType OperatorType, operator Operator)
RegisterOperator registers a custom operator in the global operator registry. This allows you to extend the engine with custom comparison logic.
Example:
type StartsWithOperator struct{}
func (o *StartsWithOperator) Evaluate(factValue, compareValue interface{}) (bool, error) {
str, ok1 := factValue.(string)
prefix, ok2 := compareValue.(string)
if !ok1 || !ok2 {
return false, fmt.Errorf("both values must be strings")
}
return strings.HasPrefix(str, prefix), nil
}
gorulesengine.RegisterOperator("starts_with", &StartsWithOperator{})
Types ¶
type Almanac ¶
type Almanac struct {
// contains filtered or unexported fields
}
Almanac stores facts and their computed values during rule evaluation. It maintains a cache for fact values, tracks events, and manages rule results. Almanac is thread-safe for concurrent access.
func NewAlmanac ¶
func NewAlmanac(facts []*Fact, opts ...AlmanacOption) *Almanac
NewAlmanac creates a new Almanac instance with the provided facts and options. The almanac is initialized with default settings including undefined fact handling.
Example:
almanac := gorulesengine.NewAlmanac([]*gorulesengine.Fact{})
almanac.AddFact("age", 25)
almanac.AddFact("country", "FR")
func (*Almanac) AddFact ¶
func (a *Almanac) AddFact(id FactID, valueOrMethod interface{}, opts ...FactOption) error
AddFact adds a fact to the almanac. The valueOrMethod can be either a static value or a function for dynamic facts. Optional FactOptions can be provided to configure caching and priority.
Example:
// Static fact
almanac.AddFact("age", 25)
// Dynamic fact
almanac.AddFact("temperature", func(params map[string]interface{}) interface{} {
return fetchTemperature()
})
func (*Almanac) AddResult ¶
func (a *Almanac) AddResult(result RuleResult)
AddResult adds a rule result to the almanac
func (*Almanac) GetFactValue ¶
func (a *Almanac) GetFactValue(factID FactID, params map[string]interface{}, path string) (interface{}, error)
GetFactValue retrieves the value of a fact by its ID. For dynamic facts, params can be passed to the computation function. The path parameter allows accessing nested values using JSONPath.
Example:
// Simple fact access
age, _ := almanac.GetFactValue("age", nil, "")
// Nested access with JSONPath
city, _ := almanac.GetFactValue("user", nil, "$.address.city")
func (*Almanac) GetFactValueFromCache ¶
GetFactValueFromCache retrieves a fact value directly from the cache
func (*Almanac) GetOptions ¶
GetOptions returns the almanac options
func (*Almanac) GetResults ¶
func (a *Almanac) GetResults() []RuleResult
GetResults returns all rule results from the almanac
func (*Almanac) TraversePath ¶
TraversePath is a helper to traverse nested structures based on a path expression. It uses the configured PathResolver to access nested values within complex data structures.
type AlmanacError ¶
type AlmanacError struct {
Payload string // Context about what was being accessed
Err error // Underlying error
}
AlmanacError represents an error that occurred while accessing or managing facts in the almanac.
func (*AlmanacError) Error ¶
func (e *AlmanacError) Error() string
Error methods to convert to RuleEngineError
func (*AlmanacError) Unwrap ¶
func (e *AlmanacError) Unwrap() error
Unwrap returns the wrapped error
type AlmanacOption ¶
type AlmanacOption func(*Almanac)
AlmanacOption defines a functional option for configuring an Almanac.
func AllowUndefinedFacts ¶
func AllowUndefinedFacts() AlmanacOption
AllowUndefinedFacts configures the almanac to return nil instead of errors for undefined facts. This is useful when you want to gracefully handle missing data.
type Condition ¶
type Condition struct {
Fact FactID `json:"fact"` // The fact identifier to evaluate
Operator OperatorType `json:"operator"` // The comparison operator to use
Value interface{} `json:"value"` // The expected value to compare against
Path string `json:"path,omitempty"` // Optional JSONPath to access nested fact values
Params map[string]interface{} `json:"params,omitempty"` // Optional parameters for dynamic facts
}
Condition represents a single condition that compares a fact value against an expected value using an operator. Conditions can optionally use JSONPath to access nested values within facts.
Example:
condition := &gorulesengine.Condition{
Fact: "age",
Operator: "greater_than",
Value: 18,
}
type ConditionError ¶
type ConditionError struct {
Condition Condition // The condition that failed
Err error // Underlying error
}
ConditionError represents an error that occurred while evaluating a condition.
func (*ConditionError) Error ¶
func (e *ConditionError) Error() string
Error methods to convert to RuleEngineError
func (*ConditionError) Unwrap ¶
func (e *ConditionError) Unwrap() error
Unwrap returns the wrapped error
type ConditionNode ¶
type ConditionNode struct {
Condition *Condition // A single condition to evaluate
SubSet *ConditionSet // A nested set of conditions
}
ConditionNode represents either a single Condition or a nested ConditionSet. This allows for recursive nesting of conditions to build complex boolean expressions.
func (*ConditionNode) UnmarshalJSON ¶
func (n *ConditionNode) UnmarshalJSON(data []byte) error
UnmarshalJSON implements custom JSON unmarshaling for ConditionNode. It attempts to unmarshal either a Condition or a ConditionSet from the JSON data.
type ConditionSet ¶
type ConditionSet struct {
All []ConditionNode `json:"all,omitempty"` // All conditions must be true (AND)
Any []ConditionNode `json:"any,omitempty"` // At least one condition must be true (OR)
None []ConditionNode `json:"none,omitempty"` // No conditions must be true (NOT)
}
ConditionSet represents a group of conditions combined with logical operators (all/any/none). ConditionSets can be nested to create complex boolean logic.
Example:
conditionSet := gorulesengine.ConditionSet{
All: []gorulesengine.ConditionNode{
{Condition: &condition1},
{Condition: &condition2},
},
}
type ConditionType ¶
type ConditionType string
ConditionType represents the type of logical operator for combining conditions.
const All ConditionType = "all"
All represents a logical AND - all conditions must be true.
const Any ConditionType = "any"
Any represents a logical OR - at least one condition must be true.
const None ConditionType = "none"
None represents a logical NOT - no conditions must be true.
type ContainsOperator ¶
type ContainsOperator struct{}
ContainsOperator checks if factValue contains compareValue (for strings and arrays).
func (*ContainsOperator) Evaluate ¶
func (o *ContainsOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if factValue contains compareValue. For strings, checks substring containment. For arrays/slices, checks element presence.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is the core rules engine that manages rules, facts, and event handlers. It evaluates rules against facts and triggers events when rules match.
func (*Engine) On ¶
func (e *Engine) On(eventType string, handler EventHandler)
On registers a handler for a specific event type. When a rule triggers an event of this type, the handler will be invoked. Multiple handlers can be registered for the same event type.
func (*Engine) OnFailure ¶
func (e *Engine) OnFailure(handler EventHandler)
OnFailure registers a global handler that is called for every failed rule evaluation. Multiple failure handlers can be registered and will all be invoked in order.
func (*Engine) OnSuccess ¶
func (e *Engine) OnSuccess(handler EventHandler)
OnSuccess registers a global handler that is called for every successful rule evaluation. Multiple success handlers can be registered and will all be invoked in order.
func (*Engine) RegisterCallback ¶
func (e *Engine) RegisterCallback(name string, handler EventHandler)
RegisterCallback registers a named callback that can be referenced by rules. Callbacks are invoked when rules succeed or fail, as specified in the rule's OnSuccess or OnFailure fields.
func (*Engine) Run ¶
func (e *Engine) Run(almanac *Almanac) ([]RuleResult, error)
Run executes all rules in the engine against the provided almanac. Rules are evaluated in priority order (higher priority first). Returns a slice of RuleResults containing the outcome of each rule evaluation. If any error occurs during evaluation, execution stops and the error is returned.
type EqualOperator ¶
type EqualOperator struct{}
EqualOperator checks if two values are equal.
func (*EqualOperator) Evaluate ¶
func (o *EqualOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if two values are equal using deep equality comparison. Returns false if the values have different types or if either value is nil.
type ErrorType ¶
type ErrorType string
ErrorType identifies the category of error that occurred.
const ( // ErrEngine indicates a general engine execution error. ErrEngine ErrorType = "ENGINE_ERROR" // ErrAlmanac indicates an error related to the almanac or fact management. ErrAlmanac ErrorType = "ALMANAC_ERROR" // ErrFact indicates an error computing or accessing a fact value. ErrFact ErrorType = "FACT_ERROR" // ErrRule indicates an error in rule definition or structure. ErrRule ErrorType = "RULE_ERROR" // ErrCondition indicates an error evaluating a condition. ErrCondition ErrorType = "CONDITION_ERROR" // ErrOperator indicates an error with an operator (not found, invalid, etc.). ErrOperator ErrorType = "OPERATOR_ERROR" // ErrEvent indicates an error related to event handling. ErrEvent ErrorType = "EVENT_ERROR" // ErrJSON indicates an error parsing or unmarshaling JSON. ErrJSON ErrorType = "JSON_ERROR" )
type Event ¶
type Event struct {
Type string `json:"type"` // The event type identifier
Params map[string]interface{} `json:"params,omitempty"` // Optional parameters passed with the event
}
Event represents an event triggered by a rule when its conditions are met. Events can carry additional parameters in the Params map.
type EventHandler ¶
type EventHandler func(event Event, almanac *Almanac, ruleResult RuleResult) error
EventHandler is a callback function invoked when an event occurs. It receives the event, the current almanac state, and the rule result. Handlers can return an error to stop further processing.
type EventHandlers ¶
type EventHandlers struct {
// contains filtered or unexported fields
}
EventHandlers manages a registry of event handlers organized by event type. It allows multiple handlers to be registered for the same event type.
func (*EventHandlers) GetHandlers ¶
func (e *EventHandlers) GetHandlers(eventType string) []EventHandler
GetHandlers retrieves all handlers registered for a specific event type. Returns nil if no handlers are registered for the given event type.
func (*EventHandlers) RegisterHandler ¶
func (e *EventHandlers) RegisterHandler(eventType string, handler EventHandler)
RegisterHandler registers an event handler for a specific event type. Multiple handlers can be registered for the same event type and will be invoked in order.
type Fact ¶
type Fact struct {
// contains filtered or unexported fields
}
Fact represents a piece of data (fact) that can be used in rule conditions. Facts can be static values or dynamic functions that compute values on demand.
Example:
// Static fact
fact := gorulesengine.NewFact("age", 25)
// Dynamic fact
fact := gorulesengine.NewFact("temperature", func(params map[string]interface{}) interface{} {
return fetchTemperatureFromAPI()
})
func NewFact ¶
func NewFact(id FactID, valueOrMethod interface{}, opts ...FactOption) *Fact
NewFact creates a new fact with the given ID and value or computation function. If valueOrMethod is a function, the fact is dynamic and will compute its value on demand. Otherwise, the fact is static with a constant value.
Options can be provided to customize caching and priority behavior.
Example:
// Static fact
fact := gorulesengine.NewFact("age", 25)
// Dynamic fact with custom options
fact := gorulesengine.NewFact("temperature",
func(params map[string]interface{}) interface{} {
return fetchTemperature()
},
gorulesengine.WithCache(),
gorulesengine.WithPriority(10),
)
func (*Fact) Calculate ¶
Calculate executes the dynamic fact method or returns the constant fact value
func (*Fact) GetCacheKey ¶
GetCacheKey generates a unique cache key for the fact if it's cached
func (*Fact) IsDynamic ¶
IsDynamic returns true if the fact computes its value dynamically using a function.
func (*Fact) ValueOrMethod ¶
func (f *Fact) ValueOrMethod() interface{}
ValueOrMethod returns the fact's value (for static facts) or computation function (for dynamic facts).
type FactError ¶
FactError represents an error that occurred while computing or accessing a fact value.
type FactOption ¶
type FactOption func(*Fact)
FactOption defines a functional option for configuring facts.
func WithCache ¶
func WithCache() FactOption
WithCache enables caching for dynamic facts. When enabled, the fact's value will be computed once and reused.
func WithPriority ¶
func WithPriority(priority int) FactOption
WithPriority sets the evaluation priority of the fact. Higher priority facts may be evaluated before lower priority facts.
func WithoutCache ¶
func WithoutCache() FactOption
WithoutCache disables caching for facts. When disabled, dynamic facts will be re-evaluated on each access.
type GreaterThanInclusiveOperator ¶
type GreaterThanInclusiveOperator struct{}
GreaterThanInclusiveOperator checks if factValue >= compareValue.
func (*GreaterThanInclusiveOperator) Evaluate ¶
func (o *GreaterThanInclusiveOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if factValue is greater than or equal to compareValue. Both values must be numeric types.
type GreaterThanOperator ¶
type GreaterThanOperator struct{}
GreaterThanOperator checks if factValue > compareValue.
func (*GreaterThanOperator) Evaluate ¶
func (o *GreaterThanOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if factValue is greater than compareValue. Both values must be numeric types.
type InOperator ¶
type InOperator struct{}
InOperator checks if factValue is contained in compareValue (array).
func (*InOperator) Evaluate ¶
func (o *InOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if factValue is contained in the compareValue array. compareValue must be a slice or array.
type LessThanInclusiveOperator ¶
type LessThanInclusiveOperator struct{}
LessThanInclusiveOperator checks if factValue <= compareValue.
func (*LessThanInclusiveOperator) Evaluate ¶
func (o *LessThanInclusiveOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if factValue is less than or equal to compareValue. Both values must be numeric types.
type LessThanOperator ¶
type LessThanOperator struct{}
LessThanOperator checks if factValue < compareValue.
func (*LessThanOperator) Evaluate ¶
func (o *LessThanOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if factValue is less than compareValue. Both values must be numeric types.
type NotContainsOperator ¶
type NotContainsOperator struct{}
NotContainsOperator checks if factValue does not contain compareValue.
func (*NotContainsOperator) Evaluate ¶
func (o *NotContainsOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if factValue does not contain compareValue. Returns the inverse of the ContainsOperator result.
type NotEqualOperator ¶
type NotEqualOperator struct{}
NotEqualOperator checks if two values are not equal.
func (*NotEqualOperator) Evaluate ¶
func (o *NotEqualOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if two values are not equal. Returns the inverse of the EqualOperator result.
type NotInOperator ¶
type NotInOperator struct{}
NotInOperator checks if factValue is not contained in compareValue (array).
func (*NotInOperator) Evaluate ¶
func (o *NotInOperator) Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
Evaluate checks if factValue is not contained in the compareValue array. Returns the inverse of the InOperator result.
type Operator ¶
type Operator interface {
// Evaluate compares a fact value against a condition value and returns true if the comparison succeeds.
Evaluate(factValue interface{}, compareValue interface{}) (bool, error)
}
Operator defines the interface for all comparison operators. Custom operators can be registered by implementing this interface.
func GetOperator ¶
func GetOperator(opType OperatorType) (Operator, error)
GetOperator retrieves an operator from the registry by its type. Returns an error if the operator is not registered.
type OperatorError ¶
type OperatorError struct {
Operator OperatorType // The operator that failed
Value interface{} // The fact value being compared
CompareValue interface{} // The expected value
Err error // Underlying error
}
OperatorError represents an error related to a specific operator evaluation.
func (*OperatorError) Error ¶
func (e *OperatorError) Error() string
Error methods to convert to RuleEngineError
func (*OperatorError) Unwrap ¶
func (e *OperatorError) Unwrap() error
Unwrap returns the wrapped error
type OperatorType ¶
type OperatorType string
OperatorType represents the type of comparison operator used in conditions.
const Contains OperatorType = "contains"
Contains checks if the fact value contains the condition value (for strings and arrays).
const Equal OperatorType = "equal"
Equal checks if the fact value equals the condition value.
const GreaterThan OperatorType = "greater_than"
GreaterThan checks if the fact value is greater than the condition value.
const GreaterThanInclusive OperatorType = "greater_than_inclusive"
GreaterThanInclusive checks if the fact value is greater than or equal to the condition value.
const In OperatorType = "in"
In checks if the fact value is contained in the condition value (array).
const LessThan OperatorType = "less_than"
LessThan checks if the fact value is less than the condition value.
const LessThanInclusive OperatorType = "less_than_inclusive"
LessThanInclusive checks if the fact value is less than or equal to the condition value.
const NotContains OperatorType = "not_contains"
NotContains checks if the fact value does not contain the condition value.
const NotEqual OperatorType = "not_equal"
NotEqual checks if the fact value is not equal to the condition value.
const NotIn OperatorType = "not_in"
NotIn checks if the fact value is not contained in the condition value (array).
type PathResolver ¶
PathResolver resolves nested values within facts using a path expression (e.g., JSONPath).
type Rule ¶
type Rule struct {
Name string `json:"name,omitempty"`
Priority int `json:"priority,omitempty"` // Higher priority rules are evaluated first
Conditions ConditionSet `json:"conditions"`
Event Event `json:"event"`
OnSuccess *string `json:"on_success,omitempty"` // Name of callback to invoke on success
OnFailure *string `json:"on_failure,omitempty"` // Name of callback to invoke on failure
}
Rule represents a business rule with conditions and an associated event. Rules are evaluated against facts in an Almanac. When all conditions are met, the rule's event is triggered and any registered callbacks are invoked.
Example:
rule := &gorulesengine.Rule{
Name: "adult-user",
Priority: 10,
Conditions: gorulesengine.ConditionSet{
All: []gorulesengine.ConditionNode{
{Condition: &gorulesengine.Condition{
Fact: "age",
Operator: "greater_than",
Value: 18,
}},
},
},
Event: gorulesengine.Event{Type: "user-is-adult"},
}
type RuleEngineError ¶
type RuleEngineError struct {
Type ErrorType // The category of error
Msg string // Human-readable error message
Err error // Wrapped underlying error (optional)
}
RuleEngineError is the base error type for all errors in the rule engine. It categorizes errors by type and optionally wraps underlying errors.
func (*RuleEngineError) Error ¶
func (e *RuleEngineError) Error() string
Error implements the error interface
func (*RuleEngineError) Unwrap ¶
func (e *RuleEngineError) Unwrap() error
Unwrap returns the wrapped error
type RuleError ¶
RuleError represents an error related to a specific rule evaluation or definition.
type RuleResult ¶
type RuleResult struct {
Event Event
Rule *Rule
Result bool // true if the rule matched, false otherwise
}
RuleResult represents the result of evaluating a rule. It contains the rule that was evaluated, the event that was triggered, and whether the rule matched (Result = true) or not (Result = false).