Fix bugs with binding to Opaque objects and pointers
All checks were successful
Build / build (push) Successful in 2m17s

This commit is contained in:
Leon Mika 2025-06-17 13:37:52 +02:00
parent e2f471c608
commit 58195738ba
3 changed files with 107 additions and 6 deletions

View file

@ -263,6 +263,8 @@ func toGoValue(obj Object) (interface{}, bool) {
return v, true
case proxyObject:
return v.p, true
case OpaqueObject:
return v.v, true
case listableProxyObject:
return v.orig.Interface(), true
case structProxyObject:

View file

@ -163,10 +163,12 @@ func (ca CallArgs) bindArg(v interface{}, arg Object) error {
switch t := arg.(type) {
case proxyObject:
return bindProxyObject(v, reflect.ValueOf(t.p))
case OpaqueObject:
return bindProxyObject(v, reflect.ValueOf(t.v))
case listableProxyObject:
return bindProxyObject(v, t.v)
return bindProxyObject(v, t.orig)
case structProxyObject:
return bindProxyObject(v, t.v)
return bindProxyObject(v, t.orig)
}
return bindProxyObject(v, reflect.ValueOf(arg))
@ -185,9 +187,9 @@ func canBindArg(v interface{}, arg Object) bool {
case proxyObject:
return canBindProxyObject(v, reflect.ValueOf(t.p))
case listableProxyObject:
return canBindProxyObject(v, t.v)
return canBindProxyObject(v, t.orig)
case structProxyObject:
return canBindProxyObject(v, t.v)
return canBindProxyObject(v, t.orig)
}
return true

View file

@ -6,6 +6,7 @@ import (
"fmt"
"strings"
"testing"
"ucl.lmika.dev/ucl"
"github.com/stretchr/testify/assert"
@ -29,6 +30,59 @@ func TestInst_SetBuiltin(t *testing.T) {
assert.Equal(t, "Hello, World", res)
})
t.Run("simple builtin accepting and returning pointers", func(t *testing.T) {
type point struct {
x, y int
}
tests := []struct {
descr string
expr string
want string
}{
{descr: "pass via args", expr: `vec 1 2 | vadd`, want: "3"},
{descr: "pass via vars", expr: `x = (vec 2 3) ; vadd $x`, want: "5"},
{descr: "pass twice", expr: `vadd (vadd2 (vec 1 2) (vec 3 4))`, want: "10"},
}
for _, tt := range tests {
t.Run(tt.descr, func(t *testing.T) {
inst := ucl.New()
inst.SetBuiltin("vec", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x, y int
if err := args.Bind(&x, &y); err != nil {
return nil, err
}
return &point{x, y}, nil
})
inst.SetBuiltin("vadd", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var v *point
if err := args.Bind(&v); err != nil {
return nil, err
}
return v.x + v.y, nil
})
inst.SetBuiltin("vadd2", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var v, u *point
if err := args.Bind(&v, &u); err != nil {
return nil, err
}
return &point{v.x + u.x, v.y + u.y}, nil
})
res, err := inst.EvalString(context.Background(), tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, fmt.Sprint(res))
})
}
})
t.Run("bind shift arguments", func(t *testing.T) {
inst := ucl.New()
inst.SetBuiltin("add2", func(ctx context.Context, args ucl.CallArgs) (any, error) {
@ -105,7 +159,7 @@ func TestInst_SetBuiltin(t *testing.T) {
return nil, err
}
return pair{x, y}, nil
return ucl.Opaque(pair{x, y}), nil
})
res, err := inst.EvalString(context.Background(), `add2 "Hello" "World"`)
@ -137,7 +191,7 @@ func TestInst_SetBuiltin(t *testing.T) {
return nil, err
}
return pair{x, y}, nil
return ucl.Opaque(pair{x, y}), nil
})
inst.SetBuiltin("join", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x pair
@ -156,6 +210,49 @@ func TestInst_SetBuiltin(t *testing.T) {
}
})
t.Run("builtin operating on and returning proxy object for pointers", func(t *testing.T) {
type pair struct {
x, y string
}
tests := []struct {
descr string
expr string
want string
}{
{descr: "pass via args", expr: `join (add2 "left" "right")`, want: "left:right"},
{descr: "pass via vars", expr: `x = (add2 "blue" "green") ; join $x`, want: "blue:green"},
}
for _, tt := range tests {
t.Run(tt.descr, func(t *testing.T) {
inst := ucl.New()
inst.SetBuiltin("add2", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x, y string
if err := args.Bind(&x, &y); err != nil {
return nil, err
}
return ucl.Opaque(&pair{x, y}), nil
})
inst.SetBuiltin("join", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x *pair
if err := args.Bind(&x); err != nil {
return nil, err
}
return x.x + ":" + x.y, nil
})
res, err := inst.EvalString(context.Background(), tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, res)
})
}
})
t.Run("slices returned by commands treated as lists", func(t *testing.T) {
tests := []struct {
descr string