expression

package
v1.0.0-beta.72 Latest Latest
Warning

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

Go to latest
Published: May 14, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package expression - Expression evaluator implementation

Package expression — Dotted-path access into MessageFields payloads.

Both `$message.*` substitution (in processor/rule/message_substitution.go) and `$message.*` condition evaluation (Evaluator.EvaluateWithStateAndMessage) walk the same map shape via the same dotted-path rules. This helper is the single source of truth — shared via the expression package so the rule package can import it without introducing a cycle.

Package expression - Regex pattern caching using framework's cache package

Package expression - Simple DSL for rule condition evaluation

Index

Constants

View Source
const (
	// Numeric operators
	OpEqual            = "eq"
	OpNotEqual         = "ne"
	OpLessThan         = "lt"
	OpLessThanEqual    = "lte"
	OpGreaterThan      = "gt"
	OpGreaterThanEqual = "gte"
	OpBetween          = "between"

	// String operators
	OpContains   = "contains"
	OpStartsWith = "starts_with"
	OpEndsWith   = "ends_with"
	OpRegexMatch = "regex"

	// Array operators
	OpIn            = "in"
	OpNotIn         = "not_in"
	OpLengthEq      = "length_eq"
	OpLengthGt      = "length_gt"
	OpLengthLt      = "length_lt"
	OpArrayContains = "array_contains"

	// State transition operator
	OpTransition = "transition"
)

Supported operators by field type

View Source
const (
	LogicAnd = "and"
	LogicOr  = "or"
)

Logic operators

Variables

This section is empty.

Functions

func ExtractMessageValue

func ExtractMessageValue(data MessageFields, path string) (any, bool)

ExtractMessageValue walks data along a dotted path. Returns the terminal value and true on success; the second return is false (and the caller must treat the result as "field not present") when:

  • any non-terminal segment is missing from the current map, OR
  • any non-terminal segment resolves to a non-map value (can't descend further), OR
  • the terminal segment is missing.

A nil terminal value is considered a successful resolution (the JSON `null` distinct from "missing"). Callers can render `<nil>` via fmt; the surfacing safety net is the regex-leftover warning at the substitution layer, or the `Required` flag at the condition layer.

The path is the bare dotted form (no `$message.` prefix). Callers trim the prefix before calling.

Types

type ConditionExpression

type ConditionExpression struct {
	Field    string      `json:"field"`          // Predicate field (e.g., "robotics.battery.level")
	Operator string      `json:"operator"`       // Comparison operator (e.g., "lte", "eq", "contains")
	Value    interface{} `json:"value"`          // Comparison value (20.0, "active", true)
	Required bool        `json:"required"`       // If false, missing field doesn't fail evaluation
	From     interface{} `json:"from,omitempty"` // For transition operator: allowed previous value(s)
}

ConditionExpression represents a single field/operator/value condition

type EvaluationError

type EvaluationError struct {
	Field    string
	Operator string
	Message  string
	Err      error
}

EvaluationError represents an error during expression evaluation

func (*EvaluationError) Error

func (e *EvaluationError) Error() string

func (*EvaluationError) Unwrap

func (e *EvaluationError) Unwrap() error

type Evaluator

type Evaluator struct {
	// contains filtered or unexported fields
}

Evaluator processes expressions against entity state

func NewExpressionEvaluator

func NewExpressionEvaluator() *Evaluator

NewExpressionEvaluator creates a new expression evaluator with all supported operators

func (*Evaluator) Distance

func (e *Evaluator) Distance(entity1, entity2 *gtypes.EntityState) (float64, error)

Distance calculates the great-circle distance between two entities in meters using the Haversine formula. Returns error if either entity lacks position data. Position is now extracted from geo.location.* triples (single source of truth).

func (*Evaluator) Evaluate

func (e *Evaluator) Evaluate(entityState *gtypes.EntityState, expr LogicalExpression) (bool, error)

Evaluate evaluates a logical expression against an entity state. Equivalent to EvaluateWithStateAndMessage with nil stateFields and nil messageFields — entity triples are the only resolution source.

func (*Evaluator) EvaluateWithStateAndMessage

func (e *Evaluator) EvaluateWithStateAndMessage(entityState *gtypes.EntityState, stateFields StateFields, messageFields MessageFields, expr LogicalExpression) (bool, error)

EvaluateWithStateAndMessage evaluates a logical expression against the full field-resolution surface: entity triples, rule match-state (`$state.*`/`$prev.*`), and the inbound message payload (`$message.*`).

Field resolution precedence per condition:

  1. `$state.*` / `$prev.*` → stateFields map
  2. `$message.<dotted.path>` → messageFields via deep-walk
  3. Bare field name → entity triples first (when entity is non-nil); falls through to messageFields if not present on the entity. When entity is nil (message-path rules), bare names resolve from messageFields directly.

This is the single condition evaluator for both rule-level conditions (where the rule fires on message-path or entity-state events) and action-level `when` guards. See ADR-041 for the unification rationale.

`$message.command` is the recommended form when both rule-level and when-clause use the same field, because it makes the resolution source explicit. Bare `command` works equivalently on message-path rules but implicitly prefers entity triples on entity-path rules.

func (*Evaluator) GetOutgoing

func (e *Evaluator) GetOutgoing(entity *gtypes.EntityState, predicate string) []string

GetOutgoing returns a list of entity IDs for outgoing relationships with the given predicate. Only returns valid entity IDs (4-part dotted notation), not literal values. Returns empty slice if entity is nil or no relationships found.

func (*Evaluator) HasTriple

func (e *Evaluator) HasTriple(entity *gtypes.EntityState, predicate string) bool

HasTriple checks if an entity has a triple with the given predicate. Returns false if entity is nil or predicate not found.

type FieldType

type FieldType int

FieldType represents the detected type of a field

const (
	// FieldTypeUnknown represents an unknown or unsupported field type
	FieldTypeUnknown FieldType = iota
	// FieldTypeFloat64 represents a floating point number field
	FieldTypeFloat64
	// FieldTypeString represents a string field
	FieldTypeString
	// FieldTypeBool represents a boolean field
	FieldTypeBool
	// FieldTypeArray represents an array field
	FieldTypeArray
)

func (FieldType) String

func (f FieldType) String() string

type LogicalExpression

type LogicalExpression struct {
	Conditions []ConditionExpression `json:"conditions"`
	Logic      string                `json:"logic"` // "and", "or"
}

LogicalExpression combines multiple conditions with logic operators

type MessageFields

type MessageFields map[string]any

MessageFields carries the inbound NATS message payload for `$message.*` pseudo-field resolution in condition expressions. The map shape is `map[string]any` (matching `GenericJSONPayload.Data` and what `ExecutionContext.MessageData` carries through the rule pipeline).

Deep-path access is supported: `$message.tool_args.command` walks the map along the dotted path. Bare field names (e.g. `command`) also resolve here when entity state is nil or when the field isn't present as an entity triple — see Evaluator.EvaluateWithStateAndMessage for the full precedence rule.

Empty or nil maps cause `$message.*` and bare-name message lookups to return "not found" — same surfacing behaviour as missing entity triples (condition fails when Required=false, errors when Required=true).

type OperatorFunc

type OperatorFunc func(fieldValue, compareValue interface{}) (bool, error)

OperatorFunc defines the signature for operator implementations

type StateFields

type StateFields map[string]interface{}

StateFields provides rule match state values for $state.* pseudo-field resolution in condition expressions. Keys are the full field names (e.g., "$state.iteration"). This avoids circular dependencies between the expression and rule packages.

type TypeDetector

type TypeDetector interface {
	GetFieldValue(entityState *gtypes.EntityState, field string) (value interface{}, exists bool, err error)
	DetectFieldType(value interface{}) FieldType
}

TypeDetector determines field type and extracts values from entity state

Jump to

Keyboard shortcuts

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