mirror of
				https://github.com/MontFerret/ferret.git
				synced 2025-10-30 23:37:40 +02:00 
			
		
		
		
	Move value specific Iterable and Iterator interfaces into core module (#233)
* Move value specific Iterable and Iterator interfaces into core module * Update Makefile Reverted test command * Update collection.go * Fixed wrong iterator usage * Updated use of switch statements
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -64,4 +64,4 @@ else | ||||
| 	git tag -a v$(RELEASE_VERSION) -m "New $(RELEASE_VERSION) version" && \ | ||||
| 	git push origin v$(RELEASE_VERSION) && \ | ||||
| 	goreleaser | ||||
| endif | ||||
| endif | ||||
|   | ||||
| @@ -78,11 +78,11 @@ func (flags Flags) List() []string { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		switch val.(type) { | ||||
| 		switch v := val.(type) { | ||||
| 		case int: | ||||
| 			arg = fmt.Sprintf("--%s=%d", arg, val.(int)) | ||||
| 			arg = fmt.Sprintf("--%s=%d", arg, v) | ||||
| 		case string: | ||||
| 			arg = fmt.Sprintf("--%s=%s", arg, val.(string)) | ||||
| 			arg = fmt.Sprintf("--%s=%s", arg, v) | ||||
| 		default: | ||||
| 			arg = fmt.Sprintf("--%s", arg) | ||||
| 		} | ||||
|   | ||||
| @@ -1430,37 +1430,37 @@ func (v *visitor) visit(node antlr.Tree, scope *scope) (core.Expression, error) | ||||
| 	var out core.Expression | ||||
| 	var err error | ||||
|  | ||||
| 	switch node.(type) { | ||||
| 	switch ctx := node.(type) { | ||||
| 	case *fql.BodyContext: | ||||
| 		out, err = v.doVisitBody(node.(*fql.BodyContext), scope) | ||||
| 		out, err = v.doVisitBody(ctx, scope) | ||||
| 	case *fql.ExpressionContext: | ||||
| 		out, err = v.doVisitExpression(node.(*fql.ExpressionContext), scope) | ||||
| 		out, err = v.doVisitExpression(ctx, scope) | ||||
| 	case *fql.ForExpressionContext: | ||||
| 		out, err = v.doVisitForExpression(node.(*fql.ForExpressionContext), scope) | ||||
| 		out, err = v.doVisitForExpression(ctx, scope) | ||||
| 	case *fql.ReturnExpressionContext: | ||||
| 		out, err = v.doVisitReturnExpression(node.(*fql.ReturnExpressionContext), scope) | ||||
| 		out, err = v.doVisitReturnExpression(ctx, scope) | ||||
| 	case *fql.ArrayLiteralContext: | ||||
| 		out, err = v.doVisitArrayLiteral(node.(*fql.ArrayLiteralContext), scope) | ||||
| 		out, err = v.doVisitArrayLiteral(ctx, scope) | ||||
| 	case *fql.ObjectLiteralContext: | ||||
| 		out, err = v.doVisitObjectLiteral(node.(*fql.ObjectLiteralContext), scope) | ||||
| 		out, err = v.doVisitObjectLiteral(ctx, scope) | ||||
| 	case *fql.StringLiteralContext: | ||||
| 		out, err = v.doVisitStringLiteral(node.(*fql.StringLiteralContext)) | ||||
| 		out, err = v.doVisitStringLiteral(ctx) | ||||
| 	case *fql.IntegerLiteralContext: | ||||
| 		out, err = v.doVisitIntegerLiteral(node.(*fql.IntegerLiteralContext)) | ||||
| 		out, err = v.doVisitIntegerLiteral(ctx) | ||||
| 	case *fql.FloatLiteralContext: | ||||
| 		out, err = v.doVisitFloatLiteral(node.(*fql.FloatLiteralContext)) | ||||
| 		out, err = v.doVisitFloatLiteral(ctx) | ||||
| 	case *fql.BooleanLiteralContext: | ||||
| 		out, err = v.doVisitBooleanLiteral(node.(*fql.BooleanLiteralContext)) | ||||
| 		out, err = v.doVisitBooleanLiteral(ctx) | ||||
| 	case *fql.NoneLiteralContext: | ||||
| 		out, err = v.doVisitNoneLiteral(node.(*fql.NoneLiteralContext)) | ||||
| 		out, err = v.doVisitNoneLiteral(ctx) | ||||
| 	case *fql.VariableContext: | ||||
| 		out, err = v.doVisitVariable(node.(*fql.VariableContext), scope) | ||||
| 		out, err = v.doVisitVariable(ctx, scope) | ||||
| 	case *fql.VariableDeclarationContext: | ||||
| 		out, err = v.doVisitVariableDeclaration(node.(*fql.VariableDeclarationContext), scope) | ||||
| 		out, err = v.doVisitVariableDeclaration(ctx, scope) | ||||
| 	case *fql.FunctionCallExpressionContext: | ||||
| 		out, err = v.doVisitFunctionCallExpression(node.(*fql.FunctionCallExpressionContext), scope) | ||||
| 		out, err = v.doVisitFunctionCallExpression(ctx, scope) | ||||
| 	case *fql.ParamContext: | ||||
| 		out, err = v.doVisitParamContext(node.(*fql.ParamContext), scope) | ||||
| 		out, err = v.doVisitParamContext(ctx, scope) | ||||
| 	default: | ||||
| 		err = v.unexpectedToken(node) | ||||
| 	} | ||||
|   | ||||
| @@ -7,49 +7,42 @@ import ( | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	Collection interface { | ||||
| 		core.Value | ||||
| 	// Measurable represents an interface of a value that can has length. | ||||
| 	Measurable interface { | ||||
| 		Length() values.Int | ||||
| 	} | ||||
|  | ||||
| 	IndexedCollection interface { | ||||
| 		Collection | ||||
| 		core.Value | ||||
| 		Measurable | ||||
| 		Get(idx values.Int) core.Value | ||||
| 		Set(idx values.Int, value core.Value) error | ||||
| 	} | ||||
|  | ||||
| 	KeyedCollection interface { | ||||
| 		Collection | ||||
| 		core.Value | ||||
| 		Measurable | ||||
| 		Keys() []string | ||||
| 		Get(key values.String) (core.Value, values.Boolean) | ||||
| 		Set(key values.String, value core.Value) | ||||
| 	} | ||||
|  | ||||
| 	IterableCollection interface { | ||||
| 		core.Value | ||||
| 		Iterate(ctx context.Context) (CollectionIterator, error) | ||||
| 	} | ||||
|  | ||||
| 	CollectionIterator interface { | ||||
| 		Next(ctx context.Context) (value core.Value, key core.Value, err error) | ||||
| 	} | ||||
|  | ||||
| 	collectionIteratorWrapper struct { | ||||
| 	coreIterator struct { | ||||
| 		valVar string | ||||
| 		keyVar string | ||||
| 		values CollectionIterator | ||||
| 		values core.Iterator | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func NewCollectionIterator( | ||||
| func NewCoreIterator( | ||||
| 	valVar, | ||||
| 	keyVar string, | ||||
| 	values CollectionIterator, | ||||
| 	values core.Iterator, | ||||
| ) (Iterator, error) { | ||||
| 	return &collectionIteratorWrapper{valVar, keyVar, values}, nil | ||||
| 	return &coreIterator{valVar, keyVar, values}, nil | ||||
| } | ||||
|  | ||||
| func (iterator *collectionIteratorWrapper) Next(ctx context.Context, scope *core.Scope) (*core.Scope, error) { | ||||
| func (iterator *coreIterator) Next(ctx context.Context, scope *core.Scope) (*core.Scope, error) { | ||||
| 	val, key, err := iterator.values.Next(ctx) | ||||
|  | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -18,6 +18,17 @@ type ( | ||||
| 		Copy() Value | ||||
| 	} | ||||
|  | ||||
| 	// Iterable represents an interface of a value that can be iterated by using an iterator. | ||||
| 	Iterable interface { | ||||
| 		Iterate(ctx context.Context) (Iterator, error) | ||||
| 	} | ||||
|  | ||||
| 	// Iterator represents an interface of a value iterator. | ||||
| 	// When iterator is exhausted it must return None as a value. | ||||
| 	Iterator interface { | ||||
| 		Next(ctx context.Context) (value Value, key Value, err error) | ||||
| 	} | ||||
|  | ||||
| 	// Getter represents an interface of | ||||
| 	// complex types that needs to be used to read values by path. | ||||
| 	// The interface is created to let user-defined types be used in dot notation data access. | ||||
|   | ||||
| @@ -54,20 +54,19 @@ func (ds *DataSource) Iterate(ctx context.Context, scope *core.Scope) (collectio | ||||
| 			return collections.NewHTMLNodeIterator(ds.valVariable, ds.keyVariable, data.(values.HTMLNode)) | ||||
| 		default: | ||||
| 			// fallback to user defined types | ||||
| 			switch data.(type) { | ||||
| 			case collections.IterableCollection: | ||||
| 				collection := data.(collections.IterableCollection) | ||||
| 			switch collection := data.(type) { | ||||
| 			case core.Iterable: | ||||
| 				iterator, err := collection.Iterate(ctx) | ||||
|  | ||||
| 				if err != nil { | ||||
| 					return nil, err | ||||
| 				} | ||||
|  | ||||
| 				return collections.NewCollectionIterator(ds.valVariable, ds.keyVariable, iterator) | ||||
| 				return collections.NewCoreIterator(ds.valVariable, ds.keyVariable, iterator) | ||||
| 			case collections.KeyedCollection: | ||||
| 				return collections.NewIndexedIterator(ds.valVariable, ds.keyVariable, data.(collections.IndexedCollection)) | ||||
| 				return collections.NewKeyedIterator(ds.valVariable, ds.keyVariable, collection) | ||||
| 			case collections.IndexedCollection: | ||||
| 				return collections.NewKeyedIterator(ds.valVariable, ds.keyVariable, data.(collections.KeyedCollection)) | ||||
| 				return collections.NewIndexedIterator(ds.valVariable, ds.keyVariable, collection) | ||||
| 			default: | ||||
| 				return nil, core.TypeError( | ||||
| 					data.Type(), | ||||
| @@ -75,6 +74,7 @@ func (ds *DataSource) Iterate(ctx context.Context, scope *core.Scope) (collectio | ||||
| 					types.Object, | ||||
| 					types.HTMLDocument, | ||||
| 					types.HTMLElement, | ||||
| 					core.NewType("Iterable"), | ||||
| 				) | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -2,12 +2,12 @@ package expressions_test | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/expressions" | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/values" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/collections" | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/core" | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/expressions" | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/values" | ||||
| 	. "github.com/smartystreets/goconvey/convey" | ||||
| ) | ||||
|  | ||||
| @@ -51,7 +51,7 @@ func (c *testIterableCollection) Hash() uint64 { | ||||
| func (c *testIterableCollection) Copy() core.Value { | ||||
| 	return c | ||||
| } | ||||
| func (c *testIterableCollection) Iterate(ctx context.Context) (collections.CollectionIterator, error) { | ||||
| func (c *testIterableCollection) Iterate(ctx context.Context) (core.Iterator, error) { | ||||
| 	return &testCollectionIterator{c.values, -1}, nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -31,9 +31,9 @@ func ParseDateTime(input interface{}) (DateTime, error) { | ||||
| } | ||||
|  | ||||
| func ParseDateTimeWith(input interface{}, layout string) (DateTime, error) { | ||||
| 	switch input.(type) { | ||||
| 	switch value := input.(type) { | ||||
| 	case string: | ||||
| 		t, err := time.Parse(layout, input.(string)) | ||||
| 		t, err := time.Parse(layout, value) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return DateTime{time.Now()}, err | ||||
|   | ||||
| @@ -206,44 +206,42 @@ func SetIn(ctx context.Context, to core.Value, byPath []core.Value, value core.V | ||||
| } | ||||
|  | ||||
| func Parse(input interface{}) core.Value { | ||||
| 	switch input.(type) { | ||||
| 	switch value := input.(type) { | ||||
| 	case bool: | ||||
| 		return NewBoolean(input.(bool)) | ||||
| 		return NewBoolean(value) | ||||
| 	case string: | ||||
| 		return NewString(input.(string)) | ||||
| 		return NewString(value) | ||||
| 	case int: | ||||
| 		return NewInt(input.(int)) | ||||
| 		return NewInt(value) | ||||
| 	case float64: | ||||
| 		return NewFloat(input.(float64)) | ||||
| 		return NewFloat(value) | ||||
| 	case float32: | ||||
| 		return NewFloat(float64(input.(float32))) | ||||
| 		return NewFloat(float64(value)) | ||||
| 	case time.Time: | ||||
| 		return NewDateTime(input.(time.Time)) | ||||
| 		return NewDateTime(value) | ||||
| 	case []interface{}: | ||||
| 		input := input.([]interface{}) | ||||
| 		arr := NewArray(len(input)) | ||||
| 		arr := NewArray(len(value)) | ||||
|  | ||||
| 		for _, el := range input { | ||||
| 		for _, el := range value { | ||||
| 			arr.Push(Parse(el)) | ||||
| 		} | ||||
|  | ||||
| 		return arr | ||||
| 	case map[string]interface{}: | ||||
| 		input := input.(map[string]interface{}) | ||||
| 		obj := NewObject() | ||||
|  | ||||
| 		for key, el := range input { | ||||
| 		for key, el := range value { | ||||
| 			obj.Set(NewString(key), Parse(el)) | ||||
| 		} | ||||
|  | ||||
| 		return obj | ||||
| 	case []byte: | ||||
| 		return NewBinary(input.([]byte)) | ||||
| 		return NewBinary(value) | ||||
| 	case nil: | ||||
| 		return None | ||||
| 	default: | ||||
| 		v := reflect.ValueOf(input) | ||||
| 		t := reflect.TypeOf(input) | ||||
| 		v := reflect.ValueOf(value) | ||||
| 		t := reflect.TypeOf(value) | ||||
| 		kind := t.Kind() | ||||
|  | ||||
| 		if kind == reflect.Slice || kind == reflect.Array { | ||||
| @@ -251,8 +249,8 @@ func Parse(input interface{}) core.Value { | ||||
| 			arr := NewArray(size) | ||||
|  | ||||
| 			for i := 0; i < size; i++ { | ||||
| 				value := v.Index(i) | ||||
| 				arr.Push(Parse(value.Interface())) | ||||
| 				curVal := v.Index(i) | ||||
| 				arr.Push(Parse(curVal.Interface())) | ||||
| 			} | ||||
|  | ||||
| 			return arr | ||||
| @@ -264,9 +262,9 @@ func Parse(input interface{}) core.Value { | ||||
|  | ||||
| 			for _, k := range keys { | ||||
| 				key := Parse(k.Interface()) | ||||
| 				value := v.MapIndex(k) | ||||
| 				curVal := v.MapIndex(k) | ||||
|  | ||||
| 				obj.Set(NewString(key.String()), Parse(value.Interface())) | ||||
| 				obj.Set(NewString(key.String()), Parse(curVal.Interface())) | ||||
| 			} | ||||
|  | ||||
| 			return obj | ||||
| @@ -278,9 +276,9 @@ func Parse(input interface{}) core.Value { | ||||
|  | ||||
| 			for i := 0; i < size; i++ { | ||||
| 				field := t.Field(i) | ||||
| 				value := v.Field(i) | ||||
| 				fieldValue := v.Field(i) | ||||
|  | ||||
| 				obj.Set(NewString(field.Name), Parse(value.Interface())) | ||||
| 				obj.Set(NewString(field.Name), Parse(fieldValue.Interface())) | ||||
| 			} | ||||
|  | ||||
| 			return obj | ||||
|   | ||||
| @@ -23,7 +23,7 @@ func TestAppend(t *testing.T) { | ||||
|  | ||||
| 		So(err, ShouldBeNil) | ||||
| 		So(out, ShouldNotEqual, arr) | ||||
| 		So(out.(collections.Collection).Length(), ShouldBeGreaterThan, arr.Length()) | ||||
| 		So(out.(collections.Measurable).Length(), ShouldBeGreaterThan, arr.Length()) | ||||
| 	}) | ||||
|  | ||||
| 	Convey("Should ignore non-unique items", t, func() { | ||||
| @@ -39,12 +39,12 @@ func TestAppend(t *testing.T) { | ||||
|  | ||||
| 		So(err, ShouldBeNil) | ||||
| 		So(out, ShouldNotEqual, arr) | ||||
| 		So(out.(collections.Collection).Length(), ShouldEqual, arr.Length()) | ||||
| 		So(out.(collections.Measurable).Length(), ShouldEqual, arr.Length()) | ||||
|  | ||||
| 		out2, err := arrays.Append(context.Background(), arr, values.NewInt(6), values.True) | ||||
|  | ||||
| 		So(err, ShouldBeNil) | ||||
| 		So(out2, ShouldNotEqual, arr) | ||||
| 		So(out2.(collections.Collection).Length(), ShouldBeGreaterThan, arr.Length()) | ||||
| 		So(out2.(collections.Measurable).Length(), ShouldBeGreaterThan, arr.Length()) | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -18,7 +18,7 @@ func Length(_ context.Context, inputs ...core.Value) (core.Value, error) { | ||||
|  | ||||
| 	value := inputs[0] | ||||
|  | ||||
| 	c, ok := value.(collections.Collection) | ||||
| 	c, ok := value.(collections.Measurable) | ||||
|  | ||||
| 	if !ok { | ||||
| 		return values.None, core.TypeError(value.Type(), | ||||
| @@ -28,7 +28,7 @@ func Length(_ context.Context, inputs ...core.Value) (core.Value, error) { | ||||
| 			types.HTMLElement, | ||||
| 			types.HTMLDocument, | ||||
| 			types.Binary, | ||||
| 			core.NewType("Collection"), | ||||
| 			core.NewType("Measurable"), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -3,7 +3,6 @@ package html | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/collections" | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/core" | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/values" | ||||
| 	"github.com/MontFerret/ferret/pkg/runtime/values/types" | ||||
| @@ -81,7 +80,7 @@ func (p *Paging) Copy() core.Value { | ||||
| 	return values.None | ||||
| } | ||||
|  | ||||
| func (p *Paging) Iterate(_ context.Context) (collections.CollectionIterator, error) { | ||||
| func (p *Paging) Iterate(_ context.Context) (core.Iterator, error) { | ||||
| 	return &PagingIterator{p.document, p.selector, -1}, nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -41,7 +41,7 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) { | ||||
| 	timeout := values.NewInt(defaultTimeout) | ||||
|  | ||||
| 	// lets figure out what is passed as 1st argument | ||||
| 	switch args[0].(type) { | ||||
| 	switch obj := args[0].(type) { | ||||
| 	case values.DHTMLDocument: | ||||
| 		// revalidate args with more accurate amount | ||||
| 		err := core.ValidateArgs(args, 3, 4) | ||||
| @@ -57,12 +57,6 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) { | ||||
| 			return values.None, err | ||||
| 		} | ||||
|  | ||||
| 		doc, ok := args[0].(values.DHTMLDocument) | ||||
|  | ||||
| 		if !ok { | ||||
| 			return values.None, core.Errors(core.ErrInvalidType, ErrNotDynamic) | ||||
| 		} | ||||
|  | ||||
| 		selector := args[1].(values.String) | ||||
| 		class := args[2].(values.String) | ||||
|  | ||||
| @@ -76,14 +70,8 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) { | ||||
| 			timeout = args[3].(values.Int) | ||||
| 		} | ||||
|  | ||||
| 		return values.None, doc.WaitForClass(selector, class, timeout) | ||||
| 		return values.None, obj.WaitForClass(selector, class, timeout) | ||||
| 	case values.DHTMLNode: | ||||
| 		el, ok := args[0].(values.DHTMLNode) | ||||
|  | ||||
| 		if !ok { | ||||
| 			return values.None, core.Errors(core.ErrInvalidType, ErrNotDynamic) | ||||
| 		} | ||||
|  | ||||
| 		class := args[1].(values.String) | ||||
|  | ||||
| 		if len(args) == 3 { | ||||
| @@ -96,7 +84,7 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) { | ||||
| 			timeout = args[2].(values.Int) | ||||
| 		} | ||||
|  | ||||
| 		return values.None, el.WaitForClass(class, timeout) | ||||
| 		return values.None, obj.WaitForClass(class, timeout) | ||||
| 	default: | ||||
| 		return values.None, core.Errors(core.ErrInvalidType, ErrNotDynamic) | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user