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
|
package values
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
@ -258,3 +259,29 @@ func (t *Array) SortWith(sorter ArraySorter) *Array {
|
|||||||
|
|
||||||
return res
|
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
|
package values_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -561,4 +563,90 @@ func TestArray(t *testing.T) {
|
|||||||
So(nestedInArr.Compare(nestedInClone), ShouldNotEqual, 0)
|
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"
|
"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) {
|
func GetIn(ctx context.Context, from core.Value, byPath []core.Value) (core.Value, error) {
|
||||||
if len(byPath) == 0 {
|
getter, ok := from.(core.Getter)
|
||||||
return None, nil
|
if !ok {
|
||||||
|
return None, core.TypeError(
|
||||||
|
from.Type(),
|
||||||
|
core.NewType("Getter"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = from
|
return getter.GetIn(ctx, byPath)
|
||||||
|
|
||||||
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:
|
|
||||||
return None, core.TypeError(
|
|
||||||
from.Type(),
|
|
||||||
types.Array,
|
|
||||||
types.Object,
|
|
||||||
core.NewType("Getter"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetIn(ctx context.Context, to core.Value, byPath []core.Value, value core.Value) error {
|
func SetIn(ctx context.Context, to core.Value, byPath []core.Value, value core.Value) error {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package values
|
package values
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
@ -267,3 +268,29 @@ func (t *Object) Clone() core.Cloneable {
|
|||||||
|
|
||||||
return cloned
|
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
|
package values_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -408,4 +410,86 @@ func TestObject(t *testing.T) {
|
|||||||
So(nestedInObjArr.Compare(nestedInCloneArr), ShouldNotEqual, 0)
|
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