mirror of
https://github.com/MontFerret/ferret.git
synced 2025-07-17 01:32:22 +02:00
refactoring: .GetIn (#353)
* sync with MontFerret/ferret * fix --param handling When params is converted to map it uses strings.Split, which slices a string into all substrings separated by :. * implement GetIn for values.Object * implement GetIn for values.Array * rewrite GetIn because values.Object and values.Array implement core.Getter now * fix bug when GetIn return nil instead of None * add tests for Object and Array .GetIn * add GetIn comment and remove 'len(byPath)' check * fix GetIn comment
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"hash/fnv"
|
||||
@ -258,3 +259,29 @@ func (t *Array) SortWith(sorter ArraySorter) *Array {
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (t *Array) GetIn(ctx context.Context, path []core.Value) (core.Value, error) {
|
||||
if len(path) == 0 {
|
||||
return None, nil
|
||||
}
|
||||
|
||||
if typ := path[0].Type(); typ != types.Int {
|
||||
return None, core.TypeError(typ, types.Int)
|
||||
}
|
||||
|
||||
first := t.Get(path[0].(Int))
|
||||
|
||||
if len(path) == 1 {
|
||||
return first, nil
|
||||
}
|
||||
|
||||
getter, ok := first.(core.Getter)
|
||||
if !ok {
|
||||
return None, core.TypeError(
|
||||
first.Type(),
|
||||
core.NewType("Getter"),
|
||||
)
|
||||
}
|
||||
|
||||
return getter.GetIn(ctx, path[1:])
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
package values_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
@ -561,4 +563,90 @@ func TestArray(t *testing.T) {
|
||||
So(nestedInArr.Compare(nestedInClone), ShouldNotEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".GetIn", t, func() {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
Convey("Should return the same as .Get when input is correct", func() {
|
||||
|
||||
Convey("Should return item by key", func() {
|
||||
key := values.NewInt(0)
|
||||
arr := values.NewArrayWith(
|
||||
values.NewInt(0),
|
||||
)
|
||||
|
||||
el, err := arr.GetIn(ctx, []core.Value{key})
|
||||
elGet := arr.Get(key)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(el.Compare(elGet), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should return None when no items", func() {
|
||||
key := values.NewInt(0)
|
||||
arr := values.NewArray(0)
|
||||
|
||||
el, err := arr.GetIn(ctx, []core.Value{key})
|
||||
elGet := arr.Get(key)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(el.Compare(elGet), ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Should error when input is not correct", func() {
|
||||
|
||||
Convey("Should error when path[0] is not an int", func() {
|
||||
arr := values.NewArray(0)
|
||||
path := []core.Value{values.NewString("")}
|
||||
|
||||
el, err := arr.GetIn(ctx, path)
|
||||
|
||||
So(err, ShouldBeError)
|
||||
So(el.Compare(values.None), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should error when first received item is not a Getter and len(path) > 1", func() {
|
||||
key := values.NewInt(0)
|
||||
arr := values.NewArrayWith(
|
||||
values.NewInt(1),
|
||||
)
|
||||
path := []core.Value{key, key}
|
||||
|
||||
el, err := arr.GetIn(ctx, path)
|
||||
|
||||
So(err, ShouldBeError)
|
||||
So(el.Compare(values.None), ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Should return None when len(path) == 0", func() {
|
||||
arr := values.NewArrayWith(
|
||||
values.NewInt(1),
|
||||
)
|
||||
|
||||
el, err := arr.GetIn(ctx, nil)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(el.Compare(values.None), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should call the nested Getter", func() {
|
||||
key := values.NewInt(0)
|
||||
arr := values.NewArrayWith(
|
||||
values.NewObjectWith(
|
||||
values.NewObjectProperty("foo", key),
|
||||
),
|
||||
)
|
||||
|
||||
el, err := arr.GetIn(ctx, []core.Value{
|
||||
key, // obj[0]
|
||||
values.NewString("foo"), // obj[0].foo
|
||||
})
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(el.Compare(key), ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -14,46 +14,18 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// GetIn checks that from implements core.Getter interface. If it implements,
|
||||
// GetIn call from.GetIn method, otherwise return error.
|
||||
func GetIn(ctx context.Context, from core.Value, byPath []core.Value) (core.Value, error) {
|
||||
if len(byPath) == 0 {
|
||||
return None, nil
|
||||
}
|
||||
|
||||
var result = from
|
||||
|
||||
for i, segment := range byPath {
|
||||
if result == None || result == nil {
|
||||
break
|
||||
}
|
||||
|
||||
segType := segment.Type()
|
||||
|
||||
switch segVal := result.(type) {
|
||||
case *Object:
|
||||
if segType != types.String {
|
||||
return nil, core.TypeError(segType, types.String)
|
||||
}
|
||||
|
||||
result, _ = segVal.Get(segment.(String))
|
||||
case *Array:
|
||||
if segType != types.Int {
|
||||
return nil, core.TypeError(segType, types.Int)
|
||||
}
|
||||
|
||||
result = segVal.Get(segment.(Int))
|
||||
case core.Getter:
|
||||
return segVal.GetIn(ctx, byPath[i:])
|
||||
default:
|
||||
getter, ok := from.(core.Getter)
|
||||
if !ok {
|
||||
return None, core.TypeError(
|
||||
from.Type(),
|
||||
types.Array,
|
||||
types.Object,
|
||||
core.NewType("Getter"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return getter.GetIn(ctx, byPath)
|
||||
}
|
||||
|
||||
func SetIn(ctx context.Context, to core.Value, byPath []core.Value, value core.Value) error {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"hash/fnv"
|
||||
@ -267,3 +268,29 @@ func (t *Object) Clone() core.Cloneable {
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
func (t *Object) GetIn(ctx context.Context, path []core.Value) (core.Value, error) {
|
||||
if len(path) == 0 {
|
||||
return None, nil
|
||||
}
|
||||
|
||||
if typ := path[0].Type(); typ != types.String {
|
||||
return None, core.TypeError(typ, types.String)
|
||||
}
|
||||
|
||||
first, _ := t.Get(path[0].(String))
|
||||
|
||||
if len(path) == 1 {
|
||||
return first, nil
|
||||
}
|
||||
|
||||
getter, ok := first.(core.Getter)
|
||||
if !ok {
|
||||
return None, core.TypeError(
|
||||
first.Type(),
|
||||
core.NewType("Getter"),
|
||||
)
|
||||
}
|
||||
|
||||
return getter.GetIn(ctx, path[1:])
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
package values_test
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
@ -408,4 +410,86 @@ func TestObject(t *testing.T) {
|
||||
So(nestedInObjArr.Compare(nestedInCloneArr), ShouldNotEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".GetIn", t, func() {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
Convey("Should return the same as .Get when input is correct", func() {
|
||||
|
||||
Convey("Should return item by key", func() {
|
||||
key := values.NewString("foo")
|
||||
obj := values.NewObjectWith(
|
||||
values.NewObjectProperty(key.String(), values.NewInt(1)),
|
||||
)
|
||||
|
||||
el, err := obj.GetIn(ctx, []core.Value{key})
|
||||
elGet, _ := obj.Get(key)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(el.Compare(elGet), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should return None when no items", func() {
|
||||
key := values.NewString("foo")
|
||||
obj := values.NewObject()
|
||||
|
||||
el, err := obj.GetIn(ctx, []core.Value{key})
|
||||
elGet, _ := obj.Get(key)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(el.Compare(elGet), ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Should error when input is not correct", func() {
|
||||
|
||||
Convey("Should error when path[0] is not a string", func() {
|
||||
obj := values.NewObject()
|
||||
path := []core.Value{values.NewInt(0)}
|
||||
|
||||
el, err := obj.GetIn(ctx, path)
|
||||
|
||||
So(err, ShouldBeError)
|
||||
So(el.Compare(values.None), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should error when first received item is not a Getter and len(path) > 1", func() {
|
||||
key := values.NewString("foo")
|
||||
obj := values.NewObjectWith(
|
||||
values.NewObjectProperty(key.String(), values.NewInt(1)),
|
||||
)
|
||||
path := []core.Value{key, key}
|
||||
|
||||
el, err := obj.GetIn(ctx, path)
|
||||
|
||||
So(err, ShouldBeError)
|
||||
So(el.Compare(values.None), ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Should return None when len(path) == 0", func() {
|
||||
obj := values.NewObject()
|
||||
|
||||
el, err := obj.GetIn(ctx, nil)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(el.Compare(values.None), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should call the nested Getter", func() {
|
||||
key := values.NewString("foo")
|
||||
obj := values.NewObjectWith(
|
||||
values.NewObjectProperty(key.String(), values.NewArrayWith(key)),
|
||||
)
|
||||
|
||||
el, err := obj.GetIn(ctx, []core.Value{
|
||||
key, // obj.foo
|
||||
values.NewInt(0), // obj.foo[0]
|
||||
})
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(el.Compare(key), ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user