From bc453ed01912e5278a8da030ae61b966eaa9d07b Mon Sep 17 00:00:00 2001 From: Ryan Shumate Date: Fri, 22 Aug 2025 17:33:14 -0400 Subject: [PATCH] feat: Add ability to set Numbers to Fact Pointers Add the ability to set a number to a Fact field pointer if the pointer points to a number value. --- examples/NumberToPointer_test.go | 60 ++++++++++++++++++++++++++++++++ model/GoDataAccessLayer.go | 3 ++ pkg/reflectools.go | 5 +++ 3 files changed, 68 insertions(+) create mode 100644 examples/NumberToPointer_test.go diff --git a/examples/NumberToPointer_test.go b/examples/NumberToPointer_test.go new file mode 100644 index 0000000..8357221 --- /dev/null +++ b/examples/NumberToPointer_test.go @@ -0,0 +1,60 @@ +package examples + +import ( + "testing" + + "github.com/hyperjumptech/grule-rule-engine/ast" + "github.com/hyperjumptech/grule-rule-engine/builder" + "github.com/hyperjumptech/grule-rule-engine/engine" + "github.com/hyperjumptech/grule-rule-engine/pkg" + "github.com/stretchr/testify/assert" +) + +const ( + trainRule = ` + rule TrainSpeedAdjust "Adjust Train speed based on passenger count" salience 10 { + when + Train.PassengerCount > 50 + then + Train.Speed = 75.45; + Train.Stops = Train.Stops - 1; + Retract("TrainSpeedAdjust"); + } + ` +) + +type Train struct { + Speed *float32 + PassengerCount *uint16 + Stops *int64 +} + +func TestSetNumberToPointer(t *testing.T) { + + speed := float32(204.31) + passengerCount := uint16(100) + stops := int64(3) + train := &Train{ + Speed: &speed, + PassengerCount: &passengerCount, + Stops: &stops, + } + + dataContext := ast.NewDataContext() + err := dataContext.Add("Train", train) + assert.NoError(t, err) + + lib := ast.NewKnowledgeLibrary() + rb := builder.NewRuleBuilder(lib) + err = rb.BuildRuleFromResource("TestSetNumberToPointer", "0.1.1", pkg.NewBytesResource([]byte(trainRule))) + assert.NoError(t, err) + eng := &engine.GruleEngine{MaxCycle: 5} + kb, err := lib.NewKnowledgeBaseInstance("TestSetNumberToPointer", "0.1.1") + assert.NoError(t, err) + err = eng.Execute(dataContext, kb) + assert.NoError(t, err) + + assert.Equal(t, int64(2), *train.Stops) + assert.Equal(t, uint16(100), *train.PassengerCount) + assert.Equal(t, float32(75.45), *train.Speed) +} diff --git a/model/GoDataAccessLayer.go b/model/GoDataAccessLayer.go index b583253..5f0627e 100755 --- a/model/GoDataAccessLayer.go +++ b/model/GoDataAccessLayer.go @@ -406,7 +406,10 @@ func (node *GoValueNode) SetObjectValueByField(field string, newValue reflect.Va if pkg.IsNumber(fieldVal) && pkg.IsNumber(newValue) { return SetNumberValue(fieldVal, newValue) + } else if pkg.IsPointerToNumber(fieldVal) && pkg.IsNumber(newValue) { + return SetNumberValue(fieldVal.Elem(), newValue) } + fieldVal.Set(newValue) return nil diff --git a/pkg/reflectools.go b/pkg/reflectools.go index eb97d64..08a273c 100755 --- a/pkg/reflectools.go +++ b/pkg/reflectools.go @@ -659,6 +659,11 @@ func IsNumber(val reflect.Value) bool { } } +// IsPointerToNumber will check a value if it is a pointer to a number eg, int, uint, or float +func IsPointerToNumber(val reflect.Value) bool { + return IsNumber(GetValueElem(val)) +} + // GetValueElem will return the value val contains if val is of Kind Interface or Pointer func GetValueElem(val reflect.Value) reflect.Value { if val.Kind() == reflect.Pointer || val.Kind() == reflect.Interface {