From 5ee0f12837ce17fa7a4b8a86571c52dac2fd73d2 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Oct 2018 14:08:05 +0300 Subject: [PATCH 001/115] add pkg/stdlib/objects Length function --- pkg/stdlib/objects/lenght.go | 31 +++++++++++++++++++++++++++++++ pkg/stdlib/objects/length_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 pkg/stdlib/objects/lenght.go create mode 100644 pkg/stdlib/objects/length_test.go diff --git a/pkg/stdlib/objects/lenght.go b/pkg/stdlib/objects/lenght.go new file mode 100644 index 00000000..be59e67b --- /dev/null +++ b/pkg/stdlib/objects/lenght.go @@ -0,0 +1,31 @@ +package objects + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +/* + * Returns the Int equal to the number of values inside the object. + * @params (Object) - The source object. + * @returns (Int) - New Int equal to the number of values inside the source object. + */ +func Length(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.ObjectType) + + if err != nil { + return values.None, err + } + + obj := args[0].(*values.Object) + + return values.NewInt(int(obj.Length())), nil +} diff --git a/pkg/stdlib/objects/length_test.go b/pkg/stdlib/objects/length_test.go new file mode 100644 index 00000000..d6e2da85 --- /dev/null +++ b/pkg/stdlib/objects/length_test.go @@ -0,0 +1,31 @@ +package objects + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestLength(t *testing.T) { + Convey("Should return number 2", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("Int", values.NewInt(1)), + values.NewObjectProperty("String", values.NewString("one")), + ) + objLen := obj.Length() + + So(objLen, ShouldEqual, values.NewInt(2)) + So(int(objLen), ShouldEqual, 2) + }) + + Convey("Should return number 0", t, func() { + obj := values.NewObject() + objLen := obj.Length() + + So(objLen, ShouldEqual, values.NewInt(0)) + So(int(objLen), ShouldEqual, 0) + }) + +} From 62a03015986b85737474a1dfd2b15a1ff4cfefb8 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Oct 2018 14:09:47 +0300 Subject: [PATCH 002/115] rename lenght.go -> length.go --- pkg/stdlib/objects/{lenght.go => length.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pkg/stdlib/objects/{lenght.go => length.go} (100%) diff --git a/pkg/stdlib/objects/lenght.go b/pkg/stdlib/objects/length.go similarity index 100% rename from pkg/stdlib/objects/lenght.go rename to pkg/stdlib/objects/length.go From 42233b4a9dc17451e80663ab707f49ced60d8837 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Oct 2018 14:37:56 +0300 Subject: [PATCH 003/115] fix tests according to other tests --- pkg/stdlib/objects/length_test.go | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/pkg/stdlib/objects/length_test.go b/pkg/stdlib/objects/length_test.go index d6e2da85..272b4d28 100644 --- a/pkg/stdlib/objects/length_test.go +++ b/pkg/stdlib/objects/length_test.go @@ -1,9 +1,11 @@ -package objects +package objects_test import ( + "context" "testing" "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/objects" . "github.com/smartystreets/goconvey/convey" ) @@ -14,18 +16,28 @@ func TestLength(t *testing.T) { values.NewObjectProperty("Int", values.NewInt(1)), values.NewObjectProperty("String", values.NewString("one")), ) - objLen := obj.Length() - So(objLen, ShouldEqual, values.NewInt(2)) - So(int(objLen), ShouldEqual, 2) + objLen, err := objects.Length(context.Background(), obj) + + So(err, ShouldEqual, nil) + + len := objLen.(values.Int) + + So(len, ShouldEqual, values.NewInt(2)) + So(int(len), ShouldEqual, int(2)) }) Convey("Should return number 0", t, func() { obj := values.NewObject() - objLen := obj.Length() - So(objLen, ShouldEqual, values.NewInt(0)) - So(int(objLen), ShouldEqual, 0) + objLen, err := objects.Length(context.Background(), obj) + + So(err, ShouldEqual, nil) + + len := objLen.(values.Int) + + So(len, ShouldEqual, values.NewInt(0)) + So(int(len), ShouldEqual, int(0)) }) } From aa96c67774b69bf9ad9542dad5734d6760e56c89 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Oct 2018 15:37:32 +0300 Subject: [PATCH 004/115] add new tests to length tests --- pkg/stdlib/objects/length_test.go | 40 +++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/pkg/stdlib/objects/length_test.go b/pkg/stdlib/objects/length_test.go index 272b4d28..9ff7660e 100644 --- a/pkg/stdlib/objects/length_test.go +++ b/pkg/stdlib/objects/length_test.go @@ -11,6 +11,19 @@ import ( ) func TestLength(t *testing.T) { + Convey("Should return number 0", t, func() { + obj := values.NewObject() + + objLen, err := objects.Length(context.Background(), obj) + + So(err, ShouldEqual, nil) + + len := objLen.(values.Int) + + So(len, ShouldEqual, values.NewInt(0)) + So(int(len), ShouldEqual, int(0)) + }) + Convey("Should return number 2", t, func() { obj := values.NewObjectWith( values.NewObjectProperty("Int", values.NewInt(1)), @@ -27,17 +40,30 @@ func TestLength(t *testing.T) { So(int(len), ShouldEqual, int(2)) }) - Convey("Should return number 0", t, func() { - obj := values.NewObject() + Convey("When argument isn't object", t, func() { + notObj := values.NewInt(1) - objLen, err := objects.Length(context.Background(), obj) + len, err := objects.Length(context.Background(), notObj) - So(err, ShouldEqual, nil) + So(err, ShouldBeError) + So(len, ShouldEqual, values.None) + }) - len := objLen.(values.Int) + Convey("Not enought arguments", t, func() { + len, err := objects.Length(context.Background()) - So(len, ShouldEqual, values.NewInt(0)) - So(int(len), ShouldEqual, int(0)) + So(err, ShouldBeError) + So(len, ShouldEqual, values.None) }) + Convey("Too many arguments", t, func() { + obj := values.NewObject() + arg1 := values.NewInt(1) + arg2 := values.NewString("str") + + len, err := objects.Length(context.Background(), obj, arg1, arg2) + + So(err, ShouldBeError) + So(len, ShouldEqual, values.None) + }) } From e769e195c14513f23962f751aa59f077fb551f24 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 7 Oct 2018 23:09:14 +0300 Subject: [PATCH 005/115] delete objects method Length --- pkg/stdlib/objects/length.go | 31 -------------- pkg/stdlib/objects/length_test.go | 69 ------------------------------- 2 files changed, 100 deletions(-) delete mode 100644 pkg/stdlib/objects/length.go delete mode 100644 pkg/stdlib/objects/length_test.go diff --git a/pkg/stdlib/objects/length.go b/pkg/stdlib/objects/length.go deleted file mode 100644 index be59e67b..00000000 --- a/pkg/stdlib/objects/length.go +++ /dev/null @@ -1,31 +0,0 @@ -package objects - -import ( - "context" - - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" -) - -/* - * Returns the Int equal to the number of values inside the object. - * @params (Object) - The source object. - * @returns (Int) - New Int equal to the number of values inside the source object. - */ -func Length(_ context.Context, args ...core.Value) (core.Value, error) { - err := core.ValidateArgs(args, 1, 1) - - if err != nil { - return values.None, err - } - - err = core.ValidateType(args[0], core.ObjectType) - - if err != nil { - return values.None, err - } - - obj := args[0].(*values.Object) - - return values.NewInt(int(obj.Length())), nil -} diff --git a/pkg/stdlib/objects/length_test.go b/pkg/stdlib/objects/length_test.go deleted file mode 100644 index 9ff7660e..00000000 --- a/pkg/stdlib/objects/length_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package objects_test - -import ( - "context" - "testing" - - "github.com/MontFerret/ferret/pkg/runtime/values" - "github.com/MontFerret/ferret/pkg/stdlib/objects" - - . "github.com/smartystreets/goconvey/convey" -) - -func TestLength(t *testing.T) { - Convey("Should return number 0", t, func() { - obj := values.NewObject() - - objLen, err := objects.Length(context.Background(), obj) - - So(err, ShouldEqual, nil) - - len := objLen.(values.Int) - - So(len, ShouldEqual, values.NewInt(0)) - So(int(len), ShouldEqual, int(0)) - }) - - Convey("Should return number 2", t, func() { - obj := values.NewObjectWith( - values.NewObjectProperty("Int", values.NewInt(1)), - values.NewObjectProperty("String", values.NewString("one")), - ) - - objLen, err := objects.Length(context.Background(), obj) - - So(err, ShouldEqual, nil) - - len := objLen.(values.Int) - - So(len, ShouldEqual, values.NewInt(2)) - So(int(len), ShouldEqual, int(2)) - }) - - Convey("When argument isn't object", t, func() { - notObj := values.NewInt(1) - - len, err := objects.Length(context.Background(), notObj) - - So(err, ShouldBeError) - So(len, ShouldEqual, values.None) - }) - - Convey("Not enought arguments", t, func() { - len, err := objects.Length(context.Background()) - - So(err, ShouldBeError) - So(len, ShouldEqual, values.None) - }) - - Convey("Too many arguments", t, func() { - obj := values.NewObject() - arg1 := values.NewInt(1) - arg2 := values.NewString("str") - - len, err := objects.Length(context.Background(), obj, arg1, arg2) - - So(err, ShouldBeError) - So(len, ShouldEqual, values.None) - }) -} From 1bfe52c5b65ed0658fb2d03df8e984c8a6fe6de8 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 7 Oct 2018 23:11:04 +0300 Subject: [PATCH 006/115] add objects method Has --- pkg/stdlib/objects/has.go | 40 +++++++++++++++++ pkg/stdlib/objects/has_test.go | 81 ++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 pkg/stdlib/objects/has.go create mode 100644 pkg/stdlib/objects/has_test.go diff --git a/pkg/stdlib/objects/has.go b/pkg/stdlib/objects/has.go new file mode 100644 index 00000000..36988edb --- /dev/null +++ b/pkg/stdlib/objects/has.go @@ -0,0 +1,40 @@ +package objects + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +/* + * Returns the value stored by the given key. + * @params (String) - The key name string. + * @returns (Boolean) - True if the key exists else false. + */ +func Has(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, 2) + + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.ObjectType) + + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[1], core.StringType) + + if err != nil { + return values.None, err + } + + obj := args[0].(*values.Object) + keyName := args[1].(values.String) + + _, has := obj.Get(keyName) + + return values.NewBoolean(has), nil +} diff --git a/pkg/stdlib/objects/has_test.go b/pkg/stdlib/objects/has_test.go new file mode 100644 index 00000000..cc1c64d2 --- /dev/null +++ b/pkg/stdlib/objects/has_test.go @@ -0,0 +1,81 @@ +package objects_test + +import ( + "context" + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/objects" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestHas(t *testing.T) { + Convey("When key exists", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("key", values.NewString("val")), + ) + + val, err := objects.Has(context.Background(), obj, values.NewString("key")) + valBool := val.(values.Boolean) + + So(err, ShouldEqual, nil) + So(valBool, ShouldEqual, values.NewBoolean(true)) + So(bool(valBool), ShouldEqual, true) + }) + + Convey("When key doesn't exists", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("anyOtherKey", values.NewString("val")), + ) + + val, err := objects.Has(context.Background(), obj, values.NewString("key")) + valBool := val.(values.Boolean) + + So(err, ShouldEqual, nil) + So(valBool, ShouldEqual, values.NewBoolean(false)) + So(bool(valBool), ShouldEqual, false) + }) + + Convey("When there are no keys", t, func() { + obj := values.NewObject() + + val, err := objects.Has(context.Background(), obj, values.NewString("key")) + valBool := val.(values.Boolean) + + So(err, ShouldEqual, nil) + So(valBool, ShouldEqual, values.NewBoolean(false)) + So(bool(valBool), ShouldEqual, false) + }) + + Convey("Not enought arguments", t, func() { + val, err := objects.Has(context.Background()) + + So(err, ShouldBeError) + So(val, ShouldEqual, values.None) + + val, err = objects.Has(context.Background(), values.NewObject()) + + So(err, ShouldBeError) + So(val, ShouldEqual, values.None) + }) + + Convey("When keyName isn't string", t, func() { + obj := values.NewObject() + key := values.NewInt(1) + + val, err := objects.Has(context.Background(), obj, key) + + So(err, ShouldBeError) + So(val, ShouldEqual, values.None) + }) + + Convey("When first argument isn't object", t, func() { + notObj := values.NewInt(1) + + val, err := objects.Has(context.Background(), notObj, values.NewString("key")) + + So(err, ShouldBeError) + So(val, ShouldEqual, values.None) + }) +} From e9f197652a96557c671e285313b9ee459f687e84 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 7 Oct 2018 23:22:38 +0300 Subject: [PATCH 007/115] add objects function Keys --- pkg/stdlib/objects/keys.go | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 pkg/stdlib/objects/keys.go diff --git a/pkg/stdlib/objects/keys.go b/pkg/stdlib/objects/keys.go new file mode 100644 index 00000000..dd5c9ad5 --- /dev/null +++ b/pkg/stdlib/objects/keys.go @@ -0,0 +1,47 @@ +package objects + +import ( + "context" + "sort" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +func Keys(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 2) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.ObjectType) + if err != nil { + return values.None, err + } + + obj := args[0].(*values.Object) + needSort := false + + if len(args) == 2 { + err = core.ValidateType(args[1], core.BooleanType) + if err != nil { + return values.None, err + } + + needSort = bool(args[1].(values.Boolean)) + } + + keys := sort.StringSlice(obj.Keys()) + keysArray := values.NewArray(len(keys)) + + if needSort { + keys.Sort() + } + + for _, key := range keys { + keysArray.Push(values.NewString(key)) + } + + return keysArray, nil +} From 984d308619ab205a9c7f656efd708d4e2cf4fdc6 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 8 Oct 2018 00:14:23 +0300 Subject: [PATCH 008/115] small fixes in Keys and Has functions --- pkg/stdlib/objects/has.go | 2 +- pkg/stdlib/objects/keys.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/stdlib/objects/has.go b/pkg/stdlib/objects/has.go index 36988edb..b198c8f6 100644 --- a/pkg/stdlib/objects/has.go +++ b/pkg/stdlib/objects/has.go @@ -36,5 +36,5 @@ func Has(_ context.Context, args ...core.Value) (core.Value, error) { _, has := obj.Get(keyName) - return values.NewBoolean(has), nil + return values.NewBoolean(bool(has)), nil } diff --git a/pkg/stdlib/objects/keys.go b/pkg/stdlib/objects/keys.go index dd5c9ad5..63554363 100644 --- a/pkg/stdlib/objects/keys.go +++ b/pkg/stdlib/objects/keys.go @@ -4,9 +4,8 @@ import ( "context" "sort" - "github.com/MontFerret/ferret/pkg/runtime/values" - "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" ) func Keys(_ context.Context, args ...core.Value) (core.Value, error) { From 2a925a02ee47d524ae621c6c532c63efcde5b739 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 8 Oct 2018 00:32:58 +0300 Subject: [PATCH 009/115] change Has function --- pkg/stdlib/objects/has.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/stdlib/objects/has.go b/pkg/stdlib/objects/has.go index b198c8f6..98c97f26 100644 --- a/pkg/stdlib/objects/has.go +++ b/pkg/stdlib/objects/has.go @@ -36,5 +36,5 @@ func Has(_ context.Context, args ...core.Value) (core.Value, error) { _, has := obj.Get(keyName) - return values.NewBoolean(bool(has)), nil + return has, nil } From 28c4d00d3ddcfd4eef6c52d4865da498dc6a30e8 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 8 Oct 2018 00:33:31 +0300 Subject: [PATCH 010/115] unit tests for Keys function --- pkg/stdlib/objects/keys_test.go | 93 +++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 pkg/stdlib/objects/keys_test.go diff --git a/pkg/stdlib/objects/keys_test.go b/pkg/stdlib/objects/keys_test.go new file mode 100644 index 00000000..05453ec6 --- /dev/null +++ b/pkg/stdlib/objects/keys_test.go @@ -0,0 +1,93 @@ +package objects_test + +import ( + "context" + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/objects" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestKeys(t *testing.T) { + Convey("Keys(obj, false) should return 'a', 'c', 'b' in any order", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(0)), + values.NewObjectProperty("b", values.NewInt(1)), + values.NewObjectProperty("c", values.NewInt(2)), + ) + + keys, err := objects.Keys(context.Background(), obj) + keysArray := keys.(*values.Array) + + So(err, ShouldEqual, nil) + So(keysArray.Type(), ShouldEqual, core.ArrayType) + So(keysArray.Length(), ShouldEqual, 3) + + for _, k := range []string{"b", "a", "c"} { + iof := keysArray.IndexOf(values.NewString(k)) + So(iof, ShouldNotEqual, -1) + } + }) + + Convey("Keys(obj, false) should return ['a', 'b', 'c']", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("b", values.NewInt(0)), + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("c", values.NewInt(3)), + ) + + keys, err := objects.Keys(context.Background(), obj, values.NewBoolean(true)) + keysArray := keys.(*values.Array) + + So(err, ShouldEqual, nil) + + for idx, key := range []string{"a", "b", "c"} { + So(keysArray.Get(values.NewInt(idx)), ShouldEqual, values.NewString(key)) + } + }) + + Convey("When there are no keys", t, func() { + obj := values.NewObject() + + keys, err := objects.Keys(context.Background(), obj, values.NewBoolean(true)) + keysArray := keys.(*values.Array) + + So(err, ShouldEqual, nil) + So(keysArray.Length(), ShouldEqual, values.NewInt(0)) + So(int(keysArray.Length()), ShouldEqual, 0) + + keys, err = objects.Keys(context.Background(), obj, values.NewBoolean(false)) + keysArray = keys.(*values.Array) + + So(err, ShouldEqual, nil) + So(keysArray.Length(), ShouldEqual, values.NewInt(0)) + So(int(keysArray.Length()), ShouldEqual, 0) + }) + + Convey("When not enought arguments", t, func() { + _, err := objects.Keys(context.Background()) + + So(err, ShouldBeError) + }) + + Convey("When first argument isn't object", t, func() { + notObj := values.NewInt(0) + + _, err := objects.Keys(context.Background(), notObj) + + So(err, ShouldBeError) + }) + + Convey("When second argument isn't boolean", t, func() { + obj := values.NewObject() + + _, err := objects.Keys(context.Background(), obj, obj) + + So(err, ShouldBeError) + }) + +} From 8be47a5d58ddda65c7dbd538f9bdc75bf1d80434 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 8 Oct 2018 03:00:51 +0300 Subject: [PATCH 011/115] add unit tests for merge. also little change in lib.go --- pkg/stdlib/objects/lib.go | 5 +- pkg/stdlib/objects/merge_test.go | 126 +++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 pkg/stdlib/objects/merge_test.go diff --git a/pkg/stdlib/objects/lib.go b/pkg/stdlib/objects/lib.go index 643310a7..16af6fd2 100644 --- a/pkg/stdlib/objects/lib.go +++ b/pkg/stdlib/objects/lib.go @@ -4,7 +4,8 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "HAS": Has, - "KEYS": Keys, + "HAS": Has, + "KEYS": Keys, + "MERGE": Merge, } } diff --git a/pkg/stdlib/objects/merge_test.go b/pkg/stdlib/objects/merge_test.go new file mode 100644 index 00000000..86b44bcc --- /dev/null +++ b/pkg/stdlib/objects/merge_test.go @@ -0,0 +1,126 @@ +package objects_test + +import ( + "context" + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/objects" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestMergeObjects(t *testing.T) { + Convey("Merge 2 objects", t, func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("prop1", values.NewInt(1)), + values.NewObjectProperty("prop2", values.NewString("str")), + ) + obj2 := values.NewObjectWith( + values.NewObjectProperty("prop3", values.NewInt(3)), + ) + + resultObj := values.NewObjectWith( + values.NewObjectProperty("prop1", values.NewInt(1)), + values.NewObjectProperty("prop2", values.NewString("str")), + values.NewObjectProperty("prop3", values.NewInt(3)), + ) + + merged, err := objects.Merge(context.Background(), obj1, obj2) + mergedObj := merged.(*values.Object) + + So(err, ShouldEqual, nil) + So(mergedObj.Compare(resultObj), ShouldEqual, 0) + }) + + Convey("When not enought arguments", t, func() { + obj, err := objects.Merge(context.Background(), values.NewObject()) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + + obj, err = objects.Merge(context.Background()) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + }) + + Convey("When wrong argument", t, func() { + obj, err := objects.Merge(context.Background(), values.NewInt(0)) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + + obj, err = objects.Merge(context.Background(), values.NewObject(), values.NewInt(0)) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + }) +} + +func TestMergeArray(t *testing.T) { + Convey("Merge array", t, func() { + objArr := values.NewArrayWith( + values.NewObjectWith( + values.NewObjectProperty("prop1", values.NewInt(1)), + ), + values.NewObjectWith( + values.NewObjectProperty("prop2", values.NewInt(2)), + ), + ) + resultObj := values.NewObjectWith( + values.NewObjectProperty("prop1", values.NewInt(1)), + values.NewObjectProperty("prop2", values.NewInt(2)), + ) + + merged, err := objects.Merge(context.Background(), objArr) + mergedObj := merged.(*values.Object) + + So(err, ShouldEqual, nil) + So(mergedObj.Compare(resultObj), ShouldEqual, 0) + }) + + Convey("Merge empty array", t, func() { + objArr := values.NewArray(0) + resultObj := values.NewObject() + + merged, err := objects.Merge(context.Background(), objArr) + mergedObj := merged.(*values.Object) + + So(err, ShouldEqual, nil) + So(mergedObj.Compare(resultObj), ShouldEqual, 0) + }) + + Convey("When there is not object element inside the array", t, func() { + objArr := values.NewArrayWith( + values.NewObject(), + values.NewInt(0), + ) + + obj, err := objects.Merge(context.Background(), objArr) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + }) + + Convey("When not enought arguments", t, func() { + obj, err := objects.Merge(context.Background()) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + }) + + Convey("When too many arguments", t, func() { + obj, err := objects.Merge(context.Background(), values.NewArray(0), values.NewArray(0)) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + }) + + Convey("When argument isn't array", t, func() { + obj, err := objects.Merge(context.Background(), values.NewInt(0)) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + }) +} From 32dd9be400175e8c4e578fee5ab973fd2cb610a0 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 8 Oct 2018 03:49:01 +0300 Subject: [PATCH 012/115] add doc to Keys function --- pkg/stdlib/objects/keys.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/stdlib/objects/keys.go b/pkg/stdlib/objects/keys.go index 63554363..5e1b8656 100644 --- a/pkg/stdlib/objects/keys.go +++ b/pkg/stdlib/objects/keys.go @@ -8,6 +8,12 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) +/* + * Returns string array of object's keys + * @params obj (Object) - The object whose keys you want to extract + * @params sort (Boolean, optional) - If sort is true, then the returned keys will be sorted. + * @returns (Array of String) - Array that contains object keys. + */ func Keys(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) if err != nil { From df36cbf66b6df8c73bc3a17880035782284e34df Mon Sep 17 00:00:00 2001 From: = Date: Mon, 8 Oct 2018 03:57:05 +0300 Subject: [PATCH 013/115] Merge function prototype --- pkg/stdlib/objects/merge.go | 71 +++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 pkg/stdlib/objects/merge.go diff --git a/pkg/stdlib/objects/merge.go b/pkg/stdlib/objects/merge.go new file mode 100644 index 00000000..23d8c7aa --- /dev/null +++ b/pkg/stdlib/objects/merge.go @@ -0,0 +1,71 @@ +package objects + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +func Merge(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, core.MaxArgs) + if err != nil { + return values.None, err + } + + var mergedObj *values.Object + + var arr *values.Array + objs := make([]*values.Object, len(args)) + isArr := false + + if len(args) == 1 { + err = core.ValidateType(args[0], core.ArrayType) + if err == nil { + isArr = true + arr = args[0].(*values.Array) + err = validateArray(arr) + } + } else { + for i, arg := range args { + err = core.ValidateType(arg, core.ObjectType) + if err != nil { + break + } + objs[i] = arg.(*values.Object) + } + } + + if err != nil { + return values.None, err + } + + if isArr { + mergedObj = mergeArray(arr) + } else { + mergedObj = mergeObjects(objs...) + } + + return mergedObj, nil +} + +func mergeArray(arr *values.Array) *values.Object { + return values.NewObject() +} + +func mergeObjects(objs ...*values.Object) *values.Object { + return values.NewObject() +} + +func validateArray(arr *values.Array) (err error) { + var value core.Value + + for i := 0; i < int(arr.Length()); i++ { + value = arr.Get(values.NewInt(i)) + err = core.ValidateType(value, core.ObjectType) + if err != nil { + break + } + } + return +} From 0d55f59d394820c49b179b4c1b59176fa53637bb Mon Sep 17 00:00:00 2001 From: = Date: Tue, 9 Oct 2018 18:36:22 +0300 Subject: [PATCH 014/115] add unit tests for KEEP function --- pkg/stdlib/objects/keep_test.go | 163 ++++++++++++++++++++++++++++++++ pkg/stdlib/objects/lib.go | 1 + 2 files changed, 164 insertions(+) create mode 100644 pkg/stdlib/objects/keep_test.go diff --git a/pkg/stdlib/objects/keep_test.go b/pkg/stdlib/objects/keep_test.go new file mode 100644 index 00000000..bf0cc5be --- /dev/null +++ b/pkg/stdlib/objects/keep_test.go @@ -0,0 +1,163 @@ +package objects_test + +import ( + "context" + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/objects" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestKeep(t *testing.T) { + Convey("When not enought arguments)", t, func() { + // there is no object + obj, err := objects.Keep(context.Background()) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + + // there are no keys + obj, err = objects.Keep(context.Background(), values.NewObject()) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + }) + + Convey("When first argument isn't object", t, func() { + obj, err := objects.Keep(context.Background(), values.NewInt(0)) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + }) + + Convey("When wrong keys arguments", t, func() { + obj, err := objects.Keep(context.Background(), values.NewObject(), values.NewInt(0)) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + + // looks like a valid case + // but there is another argument besides an array + obj, err = objects.Keep(context.Background(), values.NewObject(), values.NewArray(0), values.NewInt(0)) + + So(err, ShouldBeError) + So(obj, ShouldEqual, values.None) + }) +} + +func TestKeepStrings(t *testing.T) { + Convey("Keep key 'a'", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("b", values.NewString("string")), + ) + resultObj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + ) + + afterKeep, err := objects.Keep(context.Background(), obj, values.NewString("a")) + + So(err, ShouldEqual, nil) + So(afterKeep.Compare(resultObj), ShouldEqual, 0) + }) + + Convey("Keep key doesn't exists", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("b", values.NewString("string")), + ) + resultObj := values.NewObject() + + afterKeep, err := objects.Keep(context.Background(), obj, values.NewString("c")) + + So(err, ShouldEqual, nil) + So(afterKeep.Compare(resultObj), ShouldEqual, 0) + }) + + Convey("Keep when there are more keys than object properties", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("b", values.NewString("string")), + ) + resultObj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("b", values.NewString("string")), + ) + + afterKeep, err := objects.Keep(context.Background(), obj, + values.NewString("a"), values.NewString("b"), values.NewString("c"), + ) + + So(err, ShouldEqual, nil) + So(afterKeep.Compare(resultObj), ShouldEqual, 0) + }) +} + +func TestKeepArray(t *testing.T) { + Convey("Keep array", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("b", values.NewString("string")), + ) + keys := values.NewArrayWith(values.NewString("a")) + resultObj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + ) + + afterKeep, err := objects.Keep(context.Background(), obj, keys) + + So(err, ShouldEqual, nil) + So(afterKeep.Compare(resultObj), ShouldEqual, 0) + }) + + Convey("Keep empty array", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("b", values.NewString("string")), + ) + keys := values.NewArray(0) + resultObj := values.NewObject() + + afterKeep, err := objects.Keep(context.Background(), obj, keys) + + So(err, ShouldEqual, nil) + So(afterKeep.Compare(resultObj), ShouldEqual, 0) + }) + + Convey("Keep when there are more keys than object properties", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("b", values.NewString("string")), + ) + keys := values.NewArrayWith( + values.NewString("a"), values.NewString("b"), values.NewString("c"), + ) + resultObj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("b", values.NewString("string")), + ) + + afterKeep, err := objects.Keep(context.Background(), obj, keys) + + So(err, ShouldEqual, nil) + So(afterKeep.Compare(resultObj), ShouldEqual, 0) + }) + + Convey("When there is not string key", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("b", values.NewString("string")), + ) + keys := values.NewArrayWith( + values.NewString("a"), + values.NewInt(0), + ) + + afterKeep, err := objects.Keep(context.Background(), obj, keys) + + So(err, ShouldBeError) + So(afterKeep, ShouldEqual, values.None) + }) +} diff --git a/pkg/stdlib/objects/lib.go b/pkg/stdlib/objects/lib.go index 643310a7..b4d61690 100644 --- a/pkg/stdlib/objects/lib.go +++ b/pkg/stdlib/objects/lib.go @@ -6,5 +6,6 @@ func NewLib() map[string]core.Function { return map[string]core.Function{ "HAS": Has, "KEYS": Keys, + "KEEP": Keep, } } From 9a01e2c3d6e3d959894b04ba581191ea10ec26e9 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 9 Oct 2018 19:20:52 +0300 Subject: [PATCH 015/115] added KEEP function --- pkg/stdlib/objects/keep.go | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 pkg/stdlib/objects/keep.go diff --git a/pkg/stdlib/objects/keep.go b/pkg/stdlib/objects/keep.go new file mode 100644 index 00000000..f5ec181b --- /dev/null +++ b/pkg/stdlib/objects/keep.go @@ -0,0 +1,66 @@ +package objects + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +/* + * Returns a new object with only given keys. + * @params src (Object) - source object. + * @params keys (Array Of String OR Strings) - keys that need to be keeped. + * @returns (Object) - New Object with only given keys. + */ +func Keep(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, core.MaxArgs) + + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.ObjectType) + + if err != nil { + return values.None, err + } + + keys := values.NewArrayWith(args[1:]...) + + if len(args) == 2 && args[1].Type() == core.ArrayType { + keys = args[1].(*values.Array) + } + + err = validateArrayOfStrings(keys) + + if err != nil { + return values.None, err + } + + obj := args[0].(*values.Object) + resultObj := values.NewObject() + + var key values.String + var val core.Value + var exists values.Boolean + + for idx := values.NewInt(0); idx < keys.Length(); idx++ { + key = keys.Get(idx).(values.String) + if val, exists = obj.Get(key); exists { + resultObj.Set(key, val) + } + } + + return resultObj, nil +} + +func validateArrayOfStrings(arr *values.Array) (err error) { + for idx := values.NewInt(0); idx < arr.Length(); idx++ { + err = core.ValidateType(arr.Get(idx), core.StringType) + if err != nil { + break + } + } + return +} From 5950e9be3f3a1eb8239585970e550be5954a2e2e Mon Sep 17 00:00:00 2001 From: = Date: Tue, 9 Oct 2018 19:22:27 +0300 Subject: [PATCH 016/115] added doc for KEYS function --- pkg/stdlib/objects/keys.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/stdlib/objects/keys.go b/pkg/stdlib/objects/keys.go index 63554363..5e1b8656 100644 --- a/pkg/stdlib/objects/keys.go +++ b/pkg/stdlib/objects/keys.go @@ -8,6 +8,12 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) +/* + * Returns string array of object's keys + * @params obj (Object) - The object whose keys you want to extract + * @params sort (Boolean, optional) - If sort is true, then the returned keys will be sorted. + * @returns (Array of String) - Array that contains object keys. + */ func Keys(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) if err != nil { From 2244fe42b55416038ce0c718b6ec53770c5b1848 Mon Sep 17 00:00:00 2001 From: 3timeslazy Date: Tue, 9 Oct 2018 19:29:08 +0300 Subject: [PATCH 017/115] update lib.go --- pkg/stdlib/objects/lib.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/stdlib/objects/lib.go b/pkg/stdlib/objects/lib.go index 16af6fd2..28f9cdd1 100644 --- a/pkg/stdlib/objects/lib.go +++ b/pkg/stdlib/objects/lib.go @@ -6,6 +6,7 @@ func NewLib() map[string]core.Function { return map[string]core.Function{ "HAS": Has, "KEYS": Keys, + "KEEP": Keep, "MERGE": Merge, } } From 018c67aa4879a6f9fcd3f4b99566be4b6756dc78 Mon Sep 17 00:00:00 2001 From: 3timeslazy Date: Tue, 9 Oct 2018 19:30:25 +0300 Subject: [PATCH 018/115] update lib.go --- pkg/stdlib/objects/lib.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/stdlib/objects/lib.go b/pkg/stdlib/objects/lib.go index 28f9cdd1..2f644522 100644 --- a/pkg/stdlib/objects/lib.go +++ b/pkg/stdlib/objects/lib.go @@ -4,8 +4,8 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "HAS": Has, - "KEYS": Keys, + "HAS": Has, + "KEYS": Keys, "KEEP": Keep, "MERGE": Merge, } From 74539ace9ca51e34b91c25cb2bed37e53c81d8a1 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 9 Oct 2018 20:10:41 +0300 Subject: [PATCH 019/115] upd merge prototype --- pkg/stdlib/objects/merge.go | 58 ++++++++++--------------------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/pkg/stdlib/objects/merge.go b/pkg/stdlib/objects/merge.go index 23d8c7aa..98e163e1 100644 --- a/pkg/stdlib/objects/merge.go +++ b/pkg/stdlib/objects/merge.go @@ -7,65 +7,37 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) +/* + * Merge the given objects into a single object. + * @params objs (Array Of Object OR Objects) - objects to merge. + * @returns (Object) - Object created by merging. + */ func Merge(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, core.MaxArgs) + if err != nil { return values.None, err } - var mergedObj *values.Object - - var arr *values.Array - objs := make([]*values.Object, len(args)) - isArr := false + objs := values.NewArrayWith(args...) - if len(args) == 1 { - err = core.ValidateType(args[0], core.ArrayType) - if err == nil { - isArr = true - arr = args[0].(*values.Array) - err = validateArray(arr) - } - } else { - for i, arg := range args { - err = core.ValidateType(arg, core.ObjectType) - if err != nil { - break - } - objs[i] = arg.(*values.Object) - } + if len(args) == 1 && args[0].Type() == core.ArrayType { + objs = args[0].(*values.Array) } + err = validateTopLevelObjects(objs) + if err != nil { return values.None, err } - if isArr { - mergedObj = mergeArray(arr) - } else { - mergedObj = mergeObjects(objs...) - } - - return mergedObj, nil + return mergeArray(objs), nil } -func mergeArray(arr *values.Array) *values.Object { - return values.NewObject() +func validateTopLevelObjects(arr *values.Array) error { + return nil } -func mergeObjects(objs ...*values.Object) *values.Object { +func mergeArray(arr *values.Array) *values.Object { return values.NewObject() } - -func validateArray(arr *values.Array) (err error) { - var value core.Value - - for i := 0; i < int(arr.Length()); i++ { - value = arr.Get(values.NewInt(i)) - err = core.ValidateType(value, core.ObjectType) - if err != nil { - break - } - } - return -} From 1bb745529f93e663d4f76f69631deca45c822d92 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 9 Oct 2018 21:15:28 +0300 Subject: [PATCH 020/115] addded isEqualObjects function to objects tests --- pkg/stdlib/objects/keep_test.go | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/pkg/stdlib/objects/keep_test.go b/pkg/stdlib/objects/keep_test.go index bf0cc5be..d3718643 100644 --- a/pkg/stdlib/objects/keep_test.go +++ b/pkg/stdlib/objects/keep_test.go @@ -4,9 +4,9 @@ import ( "context" "testing" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/stdlib/objects" - . "github.com/smartystreets/goconvey/convey" ) @@ -73,7 +73,7 @@ func TestKeepStrings(t *testing.T) { afterKeep, err := objects.Keep(context.Background(), obj, values.NewString("c")) So(err, ShouldEqual, nil) - So(afterKeep.Compare(resultObj), ShouldEqual, 0) + So(isEqualObjects(afterKeep.(*values.Object), resultObj), ShouldEqual, true) }) Convey("Keep when there are more keys than object properties", t, func() { @@ -91,7 +91,7 @@ func TestKeepStrings(t *testing.T) { ) So(err, ShouldEqual, nil) - So(afterKeep.Compare(resultObj), ShouldEqual, 0) + So(isEqualObjects(afterKeep.(*values.Object), resultObj), ShouldEqual, true) }) } @@ -109,7 +109,7 @@ func TestKeepArray(t *testing.T) { afterKeep, err := objects.Keep(context.Background(), obj, keys) So(err, ShouldEqual, nil) - So(afterKeep.Compare(resultObj), ShouldEqual, 0) + So(isEqualObjects(afterKeep.(*values.Object), resultObj), ShouldEqual, true) }) Convey("Keep empty array", t, func() { @@ -123,7 +123,7 @@ func TestKeepArray(t *testing.T) { afterKeep, err := objects.Keep(context.Background(), obj, keys) So(err, ShouldEqual, nil) - So(afterKeep.Compare(resultObj), ShouldEqual, 0) + So(isEqualObjects(afterKeep.(*values.Object), resultObj), ShouldEqual, true) }) Convey("Keep when there are more keys than object properties", t, func() { @@ -142,7 +142,7 @@ func TestKeepArray(t *testing.T) { afterKeep, err := objects.Keep(context.Background(), obj, keys) So(err, ShouldEqual, nil) - So(afterKeep.Compare(resultObj), ShouldEqual, 0) + So(isEqualObjects(afterKeep.(*values.Object), resultObj), ShouldEqual, true) }) Convey("When there is not string key", t, func() { @@ -161,3 +161,24 @@ func TestKeepArray(t *testing.T) { So(afterKeep, ShouldEqual, values.None) }) } + +func isEqualObjects(obj1 *values.Object, obj2 *values.Object) bool { + var val1 core.Value + var val2 core.Value + + for _, key := range obj1.Keys() { + val1, _ = obj1.Get(values.NewString(key)) + val2, _ = obj2.Get(values.NewString(key)) + if val1.Compare(val2) != 0 { + return false + } + } + for _, key := range obj2.Keys() { + val1, _ = obj1.Get(values.NewString(key)) + val2, _ = obj2.Get(values.NewString(key)) + if val2.Compare(val1) != 0 { + return false + } + } + return true +} From 9d76a5caadc0d305c8485ba282f20ef092db6dee Mon Sep 17 00:00:00 2001 From: = Date: Wed, 10 Oct 2018 03:13:29 +0300 Subject: [PATCH 021/115] change object method Compare --- pkg/runtime/values/object.go | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 1a79a17d..0cc57edf 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -59,21 +59,40 @@ func (t *Object) String() string { func (t *Object) Compare(other core.Value) int { switch other.Type() { case core.ObjectType: - arr := other.(*Object) + other := other.(*Object) - if t.Length() == 0 && arr.Length() == 0 { + if t.Length() == 0 && other.Length() == 0 { return 0 } var res = 1 - for _, val := range t.value { - arr.ForEach(func(otherVal core.Value, key string) bool { - res = val.Compare(otherVal) + var val core.Value + var exists bool - return res != -1 - }) - } + // check other contains t + other.ForEach(func(otherVal core.Value, key string) bool { + res = -1 + + if val, exists = t.value[key]; exists { + res = otherVal.Compare(val) + } + + return res != -1 + }) + + // check t contains other + // if t contains other and other contains t, + // then t equal to other + t.ForEach(func(tVal core.Value, key string) bool { + res = 1 + + if val, exists = other.value[key]; exists { + res = tVal.Compare(val) + } + + return res != 1 + }) return res default: From c4ef910e1ffe5e4be8674acdb9311155e6842ced Mon Sep 17 00:00:00 2001 From: = Date: Wed, 10 Oct 2018 03:14:00 +0300 Subject: [PATCH 022/115] added unit tests for Compare method --- pkg/runtime/values/object_test.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/pkg/runtime/values/object_test.go b/pkg/runtime/values/object_test.go index 67d600cb..88913e96 100644 --- a/pkg/runtime/values/object_test.go +++ b/pkg/runtime/values/object_test.go @@ -1,10 +1,11 @@ package values_test import ( + "testing" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" . "github.com/smartystreets/goconvey/convey" - "testing" ) func TestObject(t *testing.T) { @@ -120,6 +121,22 @@ func TestObject(t *testing.T) { So(obj1.Compare(obj2), ShouldEqual, 0) }) + Convey("It should return 0 when both objects are equal (independent of key order)", func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("foo", values.NewString("foo")), + values.NewObjectProperty("bar", values.NewString("bar")), + ) + obj2 := values.NewObjectWith( + values.NewObjectProperty("foo", values.NewString("foo")), + values.NewObjectProperty("bar", values.NewString("bar")), + ) + + So(obj1.Compare(obj1), ShouldEqual, 0) + So(obj2.Compare(obj2), ShouldEqual, 0) + So(obj1.Compare(obj2), ShouldEqual, 0) + So(obj2.Compare(obj1), ShouldEqual, 0) + }) + Convey("It should return 1 when other array is empty", func() { obj1 := values.NewObjectWith(values.NewObjectProperty("foo", values.NewString("bar"))) obj2 := values.NewObject() From 15a7ecd17653994a7dc409747f89effe55d989dd Mon Sep 17 00:00:00 2001 From: = Date: Wed, 10 Oct 2018 14:39:22 +0300 Subject: [PATCH 023/115] changed Compare method --- pkg/runtime/values/object.go | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 0cc57edf..0f94bea8 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -64,34 +64,26 @@ func (t *Object) Compare(other core.Value) int { if t.Length() == 0 && other.Length() == 0 { return 0 } + if t.Length() < other.Length() { + return -1 + } + if t.Length() > other.Length() { + return 1 + } - var res = 1 + var res = 0 var val core.Value var exists bool - // check other contains t other.ForEach(func(otherVal core.Value, key string) bool { - res = -1 + res = 0 if val, exists = t.value[key]; exists { - res = otherVal.Compare(val) - } - - return res != -1 - }) - - // check t contains other - // if t contains other and other contains t, - // then t equal to other - t.ForEach(func(tVal core.Value, key string) bool { - res = 1 - - if val, exists = other.value[key]; exists { - res = tVal.Compare(val) + res = val.Compare(otherVal) } - return res != 1 + return res != 0 }) return res From 070a33797c9c682383fb75fe81b08aea77a29eda Mon Sep 17 00:00:00 2001 From: = Date: Wed, 10 Oct 2018 15:32:13 +0300 Subject: [PATCH 024/115] fix Compare method --- pkg/runtime/values/object.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 0f94bea8..834a6bac 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -77,13 +77,13 @@ func (t *Object) Compare(other core.Value) int { var exists bool other.ForEach(func(otherVal core.Value, key string) bool { - res = 0 + res = -1 if val, exists = t.value[key]; exists { res = val.Compare(otherVal) } - return res != 0 + return res == 0 }) return res From 1c32d2a6e15c1ef3356bed971ff96499420d320d Mon Sep 17 00:00:00 2001 From: = Date: Wed, 10 Oct 2018 23:16:34 +0300 Subject: [PATCH 025/115] rename method Clone to Copy --- pkg/html/dynamic/document.go | 2 +- pkg/html/dynamic/element.go | 4 ++-- pkg/html/static/driver.go | 5 +++-- pkg/html/static/element.go | 5 +++-- pkg/runtime/core/value.go | 2 +- pkg/runtime/values/array.go | 5 +++-- pkg/runtime/values/binary.go | 5 +++-- pkg/runtime/values/boolean.go | 7 ++++--- pkg/runtime/values/date_time.go | 5 +++-- pkg/runtime/values/float.go | 7 ++++--- pkg/runtime/values/int.go | 7 ++++--- pkg/runtime/values/none.go | 2 +- pkg/runtime/values/object.go | 2 +- pkg/runtime/values/string.go | 7 ++++--- pkg/stdlib/arrays/unshift.go | 2 +- 15 files changed, 38 insertions(+), 29 deletions(-) diff --git a/pkg/html/dynamic/document.go b/pkg/html/dynamic/document.go index 9ee9783e..28e828b5 100644 --- a/pkg/html/dynamic/document.go +++ b/pkg/html/dynamic/document.go @@ -166,7 +166,7 @@ func (doc *HTMLDocument) Hash() uint64 { return h.Sum64() } -func (doc *HTMLDocument) Clone() core.Value { +func (doc *HTMLDocument) Copy() core.Value { return values.None } diff --git a/pkg/html/dynamic/element.go b/pkg/html/dynamic/element.go index efa85de1..b0ace313 100644 --- a/pkg/html/dynamic/element.go +++ b/pkg/html/dynamic/element.go @@ -227,7 +227,7 @@ func (el *HTMLElement) Value() core.Value { return val } -func (el *HTMLElement) Clone() core.Value { +func (el *HTMLElement) Copy() core.Value { return values.None } @@ -251,7 +251,7 @@ func (el *HTMLElement) GetAttributes() core.Value { } // returning shallow copy - return val.Clone() + return val.Copy() } func (el *HTMLElement) GetAttribute(name values.String) core.Value { diff --git a/pkg/html/static/driver.go b/pkg/html/static/driver.go index 1c04cf79..672a7b8d 100644 --- a/pkg/html/static/driver.go +++ b/pkg/html/static/driver.go @@ -3,14 +3,15 @@ package static import ( "bytes" "context" + "net/http" + "net/url" + "github.com/MontFerret/ferret/pkg/html/common" "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/PuerkitoBio/goquery" "github.com/corpix/uarand" "github.com/pkg/errors" "github.com/sethgrid/pester" - "net/http" - "net/url" ) type Driver struct { diff --git a/pkg/html/static/element.go b/pkg/html/static/element.go index 2d220ae6..a5feb414 100644 --- a/pkg/html/static/element.go +++ b/pkg/html/static/element.go @@ -2,11 +2,12 @@ package static import ( "encoding/json" + "hash/fnv" + "github.com/MontFerret/ferret/pkg/html/common" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/PuerkitoBio/goquery" - "hash/fnv" ) type HTMLElement struct { @@ -69,7 +70,7 @@ func (el *HTMLElement) Hash() uint64 { return h.Sum64() } -func (el *HTMLElement) Clone() core.Value { +func (el *HTMLElement) Copy() core.Value { c, _ := NewHTMLElement(el.selection.Clone()) return c diff --git a/pkg/runtime/core/value.go b/pkg/runtime/core/value.go index a5b0ad0e..79afba77 100644 --- a/pkg/runtime/core/value.go +++ b/pkg/runtime/core/value.go @@ -46,7 +46,7 @@ type Value interface { Compare(other Value) int Unwrap() interface{} Hash() uint64 - Clone() Value + Copy() Value } func IsTypeOf(value Value, check Type) bool { diff --git a/pkg/runtime/values/array.go b/pkg/runtime/values/array.go index de319d7c..23b73e3f 100644 --- a/pkg/runtime/values/array.go +++ b/pkg/runtime/values/array.go @@ -3,9 +3,10 @@ package values import ( "encoding/binary" "encoding/json" + "hash/fnv" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/pkg/errors" - "hash/fnv" ) type ( @@ -103,7 +104,7 @@ func (t *Array) Hash() uint64 { return h.Sum64() } -func (t *Array) Clone() core.Value { +func (t *Array) Copy() core.Value { c := NewArray(len(t.value)) for _, el := range t.value { diff --git a/pkg/runtime/values/binary.go b/pkg/runtime/values/binary.go index 41baa822..bed4cf52 100644 --- a/pkg/runtime/values/binary.go +++ b/pkg/runtime/values/binary.go @@ -2,10 +2,11 @@ package values import ( "encoding/json" - "github.com/MontFerret/ferret/pkg/runtime/core" "hash/fnv" "io" "io/ioutil" + + "github.com/MontFerret/ferret/pkg/runtime/core" ) type Binary struct { @@ -72,7 +73,7 @@ func (b *Binary) Hash() uint64 { return h.Sum64() } -func (b *Binary) Clone() core.Value { +func (b *Binary) Copy() core.Value { c := make([]byte, len(b.values)) copy(c, b.values) diff --git a/pkg/runtime/values/boolean.go b/pkg/runtime/values/boolean.go index 62fd2fc3..f09ab25d 100644 --- a/pkg/runtime/values/boolean.go +++ b/pkg/runtime/values/boolean.go @@ -2,10 +2,11 @@ package values import ( "encoding/json" - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/pkg/errors" "hash/fnv" "strings" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/pkg/errors" ) type Boolean bool @@ -108,6 +109,6 @@ func (t Boolean) Hash() uint64 { return h.Sum64() } -func (t Boolean) Clone() core.Value { +func (t Boolean) Copy() core.Value { return t } diff --git a/pkg/runtime/values/date_time.go b/pkg/runtime/values/date_time.go index 542681b1..69981ce5 100644 --- a/pkg/runtime/values/date_time.go +++ b/pkg/runtime/values/date_time.go @@ -1,9 +1,10 @@ package values import ( - "github.com/MontFerret/ferret/pkg/runtime/core" "hash/fnv" "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" ) const defaultTimeLayout = time.RFC3339 @@ -109,6 +110,6 @@ func (t DateTime) Hash() uint64 { return h.Sum64() } -func (t DateTime) Clone() core.Value { +func (t DateTime) Copy() core.Value { return NewDateTime(t.Time) } diff --git a/pkg/runtime/values/float.go b/pkg/runtime/values/float.go index 99cfa010..e8c0b5e4 100644 --- a/pkg/runtime/values/float.go +++ b/pkg/runtime/values/float.go @@ -4,11 +4,12 @@ import ( "encoding/binary" "encoding/json" "fmt" - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/pkg/errors" "hash/fnv" "math" "strconv" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/pkg/errors" ) type Float float64 @@ -127,6 +128,6 @@ func (t Float) Hash() uint64 { return h.Sum64() } -func (t Float) Clone() core.Value { +func (t Float) Copy() core.Value { return t } diff --git a/pkg/runtime/values/int.go b/pkg/runtime/values/int.go index 0b4a4181..bd41f2d8 100644 --- a/pkg/runtime/values/int.go +++ b/pkg/runtime/values/int.go @@ -3,10 +3,11 @@ package values import ( "encoding/binary" "encoding/json" - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/pkg/errors" "hash/fnv" "strconv" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/pkg/errors" ) type Int int @@ -125,6 +126,6 @@ func (t Int) Hash() uint64 { return h.Sum64() } -func (t Int) Clone() core.Value { +func (t Int) Copy() core.Value { return t } diff --git a/pkg/runtime/values/none.go b/pkg/runtime/values/none.go index b82df8dc..746434a0 100644 --- a/pkg/runtime/values/none.go +++ b/pkg/runtime/values/none.go @@ -37,6 +37,6 @@ func (t *none) Hash() uint64 { return 0 } -func (t *none) Clone() core.Value { +func (t *none) Copy() core.Value { return None } diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 834a6bac..488b1d48 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -141,7 +141,7 @@ func (t *Object) Hash() uint64 { return h.Sum64() } -func (t *Object) Clone() core.Value { +func (t *Object) Copy() core.Value { c := NewObject() for k, v := range t.value { diff --git a/pkg/runtime/values/string.go b/pkg/runtime/values/string.go index e0749f02..6bdabd4c 100644 --- a/pkg/runtime/values/string.go +++ b/pkg/runtime/values/string.go @@ -3,10 +3,11 @@ package values import ( "encoding/json" "fmt" - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/pkg/errors" "hash/fnv" "strings" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/pkg/errors" ) type String string @@ -103,7 +104,7 @@ func (t String) Hash() uint64 { return h.Sum64() } -func (t String) Clone() core.Value { +func (t String) Copy() core.Value { return t } diff --git a/pkg/stdlib/arrays/unshift.go b/pkg/stdlib/arrays/unshift.go index f6ede184..002c48cf 100644 --- a/pkg/stdlib/arrays/unshift.go +++ b/pkg/stdlib/arrays/unshift.go @@ -72,7 +72,7 @@ func Unshift(_ context.Context, args ...core.Value) (core.Value, error) { if !ok { // value is not unique, just return a new copy with same elements - return arr.Clone(), nil + return arr.Copy(), nil } } From c93a54f6cc7b9afd6dd41c3c67b78e95f007b753 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 10 Oct 2018 23:20:07 +0300 Subject: [PATCH 026/115] added Cloneable interface --- pkg/runtime/core/cloneable.go | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 pkg/runtime/core/cloneable.go diff --git a/pkg/runtime/core/cloneable.go b/pkg/runtime/core/cloneable.go new file mode 100644 index 00000000..3de9055a --- /dev/null +++ b/pkg/runtime/core/cloneable.go @@ -0,0 +1,5 @@ +package core + +type Cloneable interface { + Clone() Cloneable +} From d0c77fccb83e09e392f38261d6d69cf19aa975b6 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 11 Oct 2018 15:39:18 +0300 Subject: [PATCH 027/115] added Value to Cloneable interface --- pkg/runtime/core/cloneable.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/runtime/core/cloneable.go b/pkg/runtime/core/cloneable.go index 3de9055a..682f50bd 100644 --- a/pkg/runtime/core/cloneable.go +++ b/pkg/runtime/core/cloneable.go @@ -1,5 +1,16 @@ package core +var cloneableTypes = map[Type]bool{ + ArrayType: true, + ObjectType: true, +} + type Cloneable interface { + Value Clone() Cloneable } + +func IsCloneable(value Value) bool { + _, ok := cloneableTypes[value.Type()] + return ok +} From 6410c5e22e73392223bab237867788b00010c2c4 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 11 Oct 2018 15:40:43 +0300 Subject: [PATCH 028/115] implemented Cloneable intefrace by array --- pkg/runtime/values/array.go | 15 ++++++++ pkg/runtime/values/array_test.go | 61 +++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/pkg/runtime/values/array.go b/pkg/runtime/values/array.go index 23b73e3f..6318c058 100644 --- a/pkg/runtime/values/array.go +++ b/pkg/runtime/values/array.go @@ -200,3 +200,18 @@ func (t *Array) RemoveAt(idx Int) { t.value = append(t.value[:i], t.value[i+1:]...) } + +func (t *Array) Clone() core.Cloneable { + cloned := NewArray(0) + + var value core.Value + for idx := NewInt(0); idx < t.Length(); idx++ { + value = t.Get(idx) + if core.IsCloneable(value) { + value = value.(core.Cloneable).Clone() + } + cloned.Push(value) + } + + return cloned +} diff --git a/pkg/runtime/values/array_test.go b/pkg/runtime/values/array_test.go index 6bb6911f..db085761 100644 --- a/pkg/runtime/values/array_test.go +++ b/pkg/runtime/values/array_test.go @@ -2,10 +2,11 @@ package values_test import ( "encoding/json" + "testing" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" . "github.com/smartystreets/goconvey/convey" - "testing" ) func TestArray(t *testing.T) { @@ -395,4 +396,62 @@ func TestArray(t *testing.T) { So(arr.Get(0), ShouldEqual, 1) }) }) + + Convey(".Clone", t, func() { + Convey("Cloned array should be equal to source array", func() { + arr := values.NewArrayWith( + values.NewInt(0), + values.NewObjectWith( + values.NewObjectProperty("one", values.NewInt(1)), + ), + values.NewArrayWith( + values.NewInt(2), + ), + ) + + clone := arr.Clone().(*values.Array) + + So(arr.Length(), ShouldEqual, clone.Length()) + So(arr.Compare(clone), ShouldEqual, 0) + }) + + Convey("Cloned array should be independent of the source array", func() { + arr := values.NewArrayWith( + values.NewInt(0), + values.NewInt(1), + values.NewInt(2), + values.NewInt(3), + values.NewInt(4), + values.NewInt(5), + ) + + clone := arr.Clone().(*values.Array) + + arr.Push(values.NewInt(6)) + + So(arr.Length(), ShouldNotEqual, clone.Length()) + So(arr.Compare(clone), ShouldNotEqual, 0) + }) + + Convey("Cloned object must contain copies of the nested objects", func() { + arr := values.NewArrayWith( + values.NewArrayWith( + values.NewInt(0), + values.NewInt(1), + values.NewInt(2), + values.NewInt(3), + values.NewInt(4), + ), + ) + + clone := arr.Clone().(*values.Array) + + nestedInArr := arr.Get(values.NewInt(0)).(*values.Array) + nestedInArr.Push(values.NewInt(5)) + + nestedInClone := clone.Get(values.NewInt(0)).(*values.Array) + + So(nestedInArr.Length(), ShouldNotEqual, nestedInClone) + }) + }) } From ebbfb87b179dfa9ac48b1406635d22e6755898f6 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 11 Oct 2018 16:27:01 +0300 Subject: [PATCH 029/115] added some more unit tests for values.Array --- pkg/runtime/values/array_test.go | 90 +++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/pkg/runtime/values/array_test.go b/pkg/runtime/values/array_test.go index 6bb6911f..22ed244a 100644 --- a/pkg/runtime/values/array_test.go +++ b/pkg/runtime/values/array_test.go @@ -2,10 +2,11 @@ package values_test import ( "encoding/json" + "testing" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" . "github.com/smartystreets/goconvey/convey" - "testing" ) func TestArray(t *testing.T) { @@ -117,6 +118,93 @@ func TestArray(t *testing.T) { So(arr1.Compare(arr2), ShouldEqual, 1) }) + + Convey("It should return 0 when arrays are equal", func() { + Convey("When only simple types are nested", func() { + arr1 := values.NewArrayWith( + values.NewInt(0), values.NewString("str"), + ) + arr2 := values.NewArrayWith( + values.NewInt(0), values.NewString("str"), + ) + + So(arr1.Compare(arr2), ShouldEqual, 0) + }) + + Convey("When object and array are nested at the same time", func() { + arr1 := values.NewArrayWith( + values.NewObjectWith( + values.NewObjectProperty("one", values.NewInt(1)), + ), + values.NewArrayWith( + values.NewInt(2), + ), + ) + arr2 := values.NewArrayWith( + values.NewObjectWith( + values.NewObjectProperty("one", values.NewInt(1)), + ), + values.NewArrayWith( + values.NewInt(2), + ), + ) + + So(arr1.Compare(arr2), ShouldEqual, 0) + }) + + Convey("When only objects are nested", func() { + arr1 := values.NewArrayWith( + values.NewObjectWith( + values.NewObjectProperty("one", values.NewInt(1)), + ), + ) + arr2 := values.NewArrayWith( + values.NewObjectWith( + values.NewObjectProperty("one", values.NewInt(1)), + ), + ) + + So(arr1.Compare(arr2), ShouldEqual, 0) + }) + + Convey("When only arrays are nested", func() { + arr1 := values.NewArrayWith( + values.NewArrayWith( + values.NewInt(2), + ), + ) + arr2 := values.NewArrayWith( + values.NewArrayWith( + values.NewInt(2), + ), + ) + + So(arr1.Compare(arr2), ShouldEqual, 0) + }) + + Convey("When simple and complex types at the same time", func() { + arr1 := values.NewArrayWith( + values.NewInt(0), + values.NewObjectWith( + values.NewObjectProperty("one", values.NewInt(1)), + ), + values.NewArrayWith( + values.NewInt(2), + ), + ) + arr2 := values.NewArrayWith( + values.NewInt(0), + values.NewObjectWith( + values.NewObjectProperty("one", values.NewInt(1)), + ), + values.NewArrayWith( + values.NewInt(2), + ), + ) + + So(arr1.Compare(arr2), ShouldEqual, 0) + }) + }) }) Convey(".Hash", t, func() { From 591b1535188198114242557a02ad0a29e4a77fc0 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 11 Oct 2018 16:36:15 +0300 Subject: [PATCH 030/115] fix values.Array.Compare method --- pkg/runtime/values/array.go | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/pkg/runtime/values/array.go b/pkg/runtime/values/array.go index de319d7c..b338563c 100644 --- a/pkg/runtime/values/array.go +++ b/pkg/runtime/values/array.go @@ -3,9 +3,10 @@ package values import ( "encoding/binary" "encoding/json" + "hash/fnv" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/pkg/errors" - "hash/fnv" ) type ( @@ -44,21 +45,27 @@ func (t *Array) String() string { func (t *Array) Compare(other core.Value) int { switch other.Type() { case core.ArrayType: - arr := other.(*Array) + other := other.(*Array) - if t.Length() == 0 && arr.Length() == 0 { + if t.Length() == 0 && other.Length() == 0 { return 0 } + if t.Length() < other.Length() { + return -1 + } + if t.Length() > other.Length() { + return 1 + } - var res = 1 + var res = 0 + var val core.Value - for _, val := range t.value { - arr.ForEach(func(otherVal core.Value, idx int) bool { - res = val.Compare(otherVal) + other.ForEach(func(otherVal core.Value, idx int) bool { + val = t.Get(NewInt(idx)) + res = val.Compare(otherVal) - return res != -1 - }) - } + return res == 0 + }) return res case core.ObjectType: From 7935b70ba31e1856bcff0bc62f831e6af18a1ed5 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 11 Oct 2018 16:36:36 +0300 Subject: [PATCH 031/115] added one more unit test --- pkg/runtime/values/array_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pkg/runtime/values/array_test.go b/pkg/runtime/values/array_test.go index 22ed244a..6c2e5010 100644 --- a/pkg/runtime/values/array_test.go +++ b/pkg/runtime/values/array_test.go @@ -204,6 +204,25 @@ func TestArray(t *testing.T) { So(arr1.Compare(arr2), ShouldEqual, 0) }) + + Convey("When custom complex type", func() { + arr1 := values.NewArrayWith( + values.NewObjectWith( + values.NewObjectProperty( + "arr", values.NewArrayWith(values.NewObject()), + ), + ), + ) + arr2 := values.NewArrayWith( + values.NewObjectWith( + values.NewObjectProperty( + "arr", values.NewArrayWith(values.NewObject()), + ), + ), + ) + + So(arr1.Compare(arr2), ShouldEqual, 0) + }) }) }) From ca39ad808df87a238ad4fd31ae08710647e76457 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 11 Oct 2018 17:27:29 +0300 Subject: [PATCH 032/115] implemented Cloneable interface by Object --- pkg/runtime/values/object.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 488b1d48..88bff29d 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -202,3 +202,20 @@ func (t *Object) Remove(key String) { func (t *Object) SetIn(path []core.Value, value core.Value) error { return SetIn(t, path, value) } + +func (t *Object) Clone() core.Cloneable { + cloned := NewObject() + + var value core.Value + var keyString String + for _, key := range t.Keys() { + keyString = NewString(key) + value, _ = t.Get(keyString) + if core.IsCloneable(value) { + value = value.(core.Cloneable).Clone() + } + cloned.Set(keyString, value) + } + + return cloned +} From a38f0f8898ec64e64e6ee8d3633b32fec94c0d0a Mon Sep 17 00:00:00 2001 From: = Date: Thu, 11 Oct 2018 17:28:07 +0300 Subject: [PATCH 033/115] unit tests for Object.Clone --- pkg/runtime/values/array_test.go | 4 +-- pkg/runtime/values/object_test.go | 45 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/pkg/runtime/values/array_test.go b/pkg/runtime/values/array_test.go index d3d2e35a..6d0cc9c5 100644 --- a/pkg/runtime/values/array_test.go +++ b/pkg/runtime/values/array_test.go @@ -539,7 +539,7 @@ func TestArray(t *testing.T) { So(arr.Compare(clone), ShouldNotEqual, 0) }) - Convey("Cloned object must contain copies of the nested objects", func() { + Convey("Cloned array must contain copies of the nested objects", func() { arr := values.NewArrayWith( values.NewArrayWith( values.NewInt(0), @@ -557,7 +557,7 @@ func TestArray(t *testing.T) { nestedInClone := clone.Get(values.NewInt(0)).(*values.Array) - So(nestedInArr.Length(), ShouldNotEqual, nestedInClone) + So(nestedInArr.Compare(nestedInClone), ShouldNotEqual, 0) }) }) } diff --git a/pkg/runtime/values/object_test.go b/pkg/runtime/values/object_test.go index 88913e96..ec7ac240 100644 --- a/pkg/runtime/values/object_test.go +++ b/pkg/runtime/values/object_test.go @@ -300,4 +300,49 @@ func TestObject(t *testing.T) { So(v.Compare(values.NewInt(1)), ShouldEqual, 0) }) }) + + Convey(".Clone", t, func() { + Convey("Cloned object should be equal to source object", func() { + obj := values.NewObjectWith( + values.NewObjectProperty("one", values.NewInt(1)), + values.NewObjectProperty("two", values.NewInt(2)), + ) + + clone := obj.Clone().(*values.Object) + + So(obj.Compare(clone), ShouldEqual, 0) + }) + + Convey("Cloned object should be independent of the source object", func() { + obj := values.NewObjectWith( + values.NewObjectProperty("one", values.NewInt(1)), + values.NewObjectProperty("two", values.NewInt(2)), + ) + + clone := obj.Clone().(*values.Object) + + obj.Remove(values.NewString("one")) + + So(obj.Compare(clone), ShouldNotEqual, 0) + }) + + Convey("Cloned object must contain copies of the nested objects", func() { + obj := values.NewObjectWith( + values.NewObjectProperty( + "arr", values.NewArrayWith(values.NewInt(1)), + ), + ) + + clone := obj.Clone().(*values.Object) + + nestedInObj, _ := obj.Get(values.NewString("arr")) + nestedInObjArr := nestedInObj.(*values.Array) + nestedInObjArr.Push(values.NewInt(2)) + + nestedInClone, _ := clone.Get(values.NewString("arr")) + nestedInCloneArr := nestedInClone.(*values.Array) + + So(nestedInObjArr.Compare(nestedInCloneArr), ShouldNotEqual, 0) + }) + }) } From 6a1f475ce8f76f490250937e5634794fd8f48609 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 12 Oct 2018 14:49:29 +0300 Subject: [PATCH 034/115] move core.IsCloneable to value.go --- pkg/runtime/core/cloneable.go | 5 ----- pkg/runtime/core/value.go | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/runtime/core/cloneable.go b/pkg/runtime/core/cloneable.go index 682f50bd..3119d704 100644 --- a/pkg/runtime/core/cloneable.go +++ b/pkg/runtime/core/cloneable.go @@ -9,8 +9,3 @@ type Cloneable interface { Value Clone() Cloneable } - -func IsCloneable(value Value) bool { - _, ok := cloneableTypes[value.Type()] - return ok -} diff --git a/pkg/runtime/core/value.go b/pkg/runtime/core/value.go index 79afba77..b38dfd3e 100644 --- a/pkg/runtime/core/value.go +++ b/pkg/runtime/core/value.go @@ -70,3 +70,8 @@ func ValidateType(value Value, required ...Type) error { return nil } + +func IsCloneable(value Value) bool { + _, ok := cloneableTypes[value.Type()] + return ok +} From 5ec2545844b9ea4736be59d1a26ff131cd003884 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 12 Oct 2018 14:50:02 +0300 Subject: [PATCH 035/115] change Clone function --- pkg/runtime/values/object.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 88bff29d..bfb025da 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -208,7 +208,7 @@ func (t *Object) Clone() core.Cloneable { var value core.Value var keyString String - for _, key := range t.Keys() { + for key := range t.value { keyString = NewString(key) value, _ = t.Get(keyString) if core.IsCloneable(value) { From 60575ac0b65484b262117e6c25f8b8da94d7dc66 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 12 Oct 2018 18:49:32 +0300 Subject: [PATCH 036/115] move IsClonable to package values --- pkg/runtime/core/cloneable.go | 5 ----- pkg/runtime/core/value.go | 5 ----- pkg/runtime/values/array.go | 2 +- pkg/runtime/values/helpers.go | 14 +++++++++++++- pkg/runtime/values/object.go | 2 +- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/pkg/runtime/core/cloneable.go b/pkg/runtime/core/cloneable.go index 3119d704..a382e9d7 100644 --- a/pkg/runtime/core/cloneable.go +++ b/pkg/runtime/core/cloneable.go @@ -1,10 +1,5 @@ package core -var cloneableTypes = map[Type]bool{ - ArrayType: true, - ObjectType: true, -} - type Cloneable interface { Value Clone() Cloneable diff --git a/pkg/runtime/core/value.go b/pkg/runtime/core/value.go index b38dfd3e..79afba77 100644 --- a/pkg/runtime/core/value.go +++ b/pkg/runtime/core/value.go @@ -70,8 +70,3 @@ func ValidateType(value Value, required ...Type) error { return nil } - -func IsCloneable(value Value) bool { - _, ok := cloneableTypes[value.Type()] - return ok -} diff --git a/pkg/runtime/values/array.go b/pkg/runtime/values/array.go index bacf06ce..8fd7d9b2 100644 --- a/pkg/runtime/values/array.go +++ b/pkg/runtime/values/array.go @@ -213,7 +213,7 @@ func (t *Array) Clone() core.Cloneable { var value core.Value for idx := NewInt(0); idx < t.Length(); idx++ { value = t.Get(idx) - if core.IsCloneable(value) { + if IsCloneable(value) { value = value.(core.Cloneable).Clone() } cloned.Push(value) diff --git a/pkg/runtime/values/helpers.go b/pkg/runtime/values/helpers.go index 87ff87dc..023f02da 100644 --- a/pkg/runtime/values/helpers.go +++ b/pkg/runtime/values/helpers.go @@ -2,9 +2,10 @@ package values import ( "encoding/json" - "github.com/MontFerret/ferret/pkg/runtime/core" "reflect" "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" ) func GetIn(from core.Value, byPath []core.Value) (core.Value, error) { @@ -294,3 +295,14 @@ func ToBoolean(input core.Value) core.Value { return True } } + +func IsCloneable(value core.Value) Boolean { + switch value.Type() { + case core.ArrayType: + return NewBoolean(true) + case core.ObjectType: + return NewBoolean(true) + default: + return NewBoolean(false) + } +} diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index bfb025da..c5b56438 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -211,7 +211,7 @@ func (t *Object) Clone() core.Cloneable { for key := range t.value { keyString = NewString(key) value, _ = t.Get(keyString) - if core.IsCloneable(value) { + if IsCloneable(value) { value = value.(core.Cloneable).Clone() } cloned.Set(keyString, value) From ca67a1192b900fdb31420b7ba08fc22e80b30f39 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 16:32:30 +0300 Subject: [PATCH 037/115] updated MERGE unit tests --- pkg/stdlib/objects/merge_test.go | 144 +++++++++++++------------------ 1 file changed, 58 insertions(+), 86 deletions(-) diff --git a/pkg/stdlib/objects/merge_test.go b/pkg/stdlib/objects/merge_test.go index 6a661953..89ca26c5 100644 --- a/pkg/stdlib/objects/merge_test.go +++ b/pkg/stdlib/objects/merge_test.go @@ -4,16 +4,58 @@ import ( "context" "testing" - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/stdlib/objects" . "github.com/smartystreets/goconvey/convey" ) +func TestMerge(t *testing.T) { + Convey("When not enought arguments", t, func() { + obj, err := objects.Merge(context.Background()) + + So(err, ShouldBeError) + So(obj.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When wrong type of arguments", t, func() { + obj, err := objects.Merge(context.Background(), values.NewInt(0)) + + So(err, ShouldBeError) + So(obj.Compare(values.None), ShouldEqual, 0) + + obj, err = objects.Merge(context.Background(), values.NewObject(), values.NewInt(0)) + + So(err, ShouldBeError) + So(obj.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When too many arrays", t, func() { + obj, err := objects.Merge(context.Background(), values.NewArray(0), values.NewArray(0)) + + So(err, ShouldBeError) + So(obj.Compare(values.None), ShouldEqual, 0) + }) +} + func TestMergeObjects(t *testing.T) { - Convey("Merge 2 objects", t, func() { + Convey("Merge single object", t, func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("prop1", values.NewInt(1)), + values.NewObjectProperty("prop2", values.NewString("str")), + ) + result := values.NewObjectWith( + values.NewObjectProperty("prop1", values.NewInt(1)), + values.NewObjectProperty("prop2", values.NewString("str")), + ) + + merged, err := objects.Merge(context.Background(), obj1) + + So(err, ShouldBeNil) + So(merged.Compare(result), ShouldEqual, 0) + }) + + Convey("Merge two objects", t, func() { obj1 := values.NewObjectWith( values.NewObjectProperty("prop1", values.NewInt(1)), values.NewObjectProperty("prop2", values.NewString("str")), @@ -22,17 +64,16 @@ func TestMergeObjects(t *testing.T) { values.NewObjectProperty("prop3", values.NewInt(3)), ) - resultObj := values.NewObjectWith( + result := values.NewObjectWith( values.NewObjectProperty("prop1", values.NewInt(1)), values.NewObjectProperty("prop2", values.NewString("str")), values.NewObjectProperty("prop3", values.NewInt(3)), ) merged, err := objects.Merge(context.Background(), obj1, obj2) - mergedObj := merged.(*values.Object) - So(err, ShouldEqual, nil) - So(isEqual(mergedObj, resultObj), ShouldEqual, true) + So(err, ShouldBeNil) + So(merged.Compare(result), ShouldEqual, 0) }) Convey("When keys are repeated", t, func() { @@ -43,40 +84,15 @@ func TestMergeObjects(t *testing.T) { obj2 := values.NewObjectWith( values.NewObjectProperty("prop1", values.NewInt(3)), ) - resultObj := values.NewObjectWith( + result := values.NewObjectWith( values.NewObjectProperty("prop1", values.NewInt(3)), values.NewObjectProperty("prop2", values.NewString("str")), ) merged, err := objects.Merge(context.Background(), obj1, obj2) - mergedObj := merged.(*values.Object) - - So(err, ShouldEqual, nil) - So(isEqual(mergedObj, resultObj), ShouldEqual, true) - }) - - Convey("When not enought arguments", t, func() { - obj, err := objects.Merge(context.Background(), values.NewObject()) - - So(err, ShouldBeError) - So(obj, ShouldEqual, values.None) - obj, err = objects.Merge(context.Background()) - - So(err, ShouldBeError) - So(obj, ShouldEqual, values.None) - }) - - Convey("When wrong argument", t, func() { - obj, err := objects.Merge(context.Background(), values.NewInt(0)) - - So(err, ShouldBeError) - So(obj, ShouldEqual, values.None) - - obj, err = objects.Merge(context.Background(), values.NewObject(), values.NewInt(0)) - - So(err, ShouldBeError) - So(obj, ShouldEqual, values.None) + So(err, ShouldBeNil) + So(merged.Compare(result), ShouldEqual, 0) }) } @@ -90,27 +106,25 @@ func TestMergeArray(t *testing.T) { values.NewObjectProperty("prop2", values.NewInt(2)), ), ) - resultObj := values.NewObjectWith( + result := values.NewObjectWith( values.NewObjectProperty("prop1", values.NewInt(1)), values.NewObjectProperty("prop2", values.NewInt(2)), ) merged, err := objects.Merge(context.Background(), objArr) - mergedObj := merged.(*values.Object) - So(err, ShouldEqual, nil) - So(isEqual(mergedObj, resultObj), ShouldEqual, true) + So(err, ShouldBeNil) + So(merged.Compare(result), ShouldEqual, 0) }) Convey("Merge empty array", t, func() { objArr := values.NewArray(0) - resultObj := values.NewObject() + result := values.NewObject() merged, err := objects.Merge(context.Background(), objArr) - mergedObj := merged.(*values.Object) - So(err, ShouldEqual, nil) - So(isEqual(mergedObj, resultObj), ShouldEqual, true) + So(err, ShouldBeNil) + So(merged.Compare(result), ShouldEqual, 0) }) Convey("When there is not object element inside the array", t, func() { @@ -122,48 +136,6 @@ func TestMergeArray(t *testing.T) { obj, err := objects.Merge(context.Background(), objArr) So(err, ShouldBeError) - So(obj, ShouldEqual, values.None) - }) - - Convey("When not enought arguments", t, func() { - obj, err := objects.Merge(context.Background()) - - So(err, ShouldBeError) - So(obj, ShouldEqual, values.None) + So(obj.Compare(values.None), ShouldEqual, 0) }) - - Convey("When too many arguments", t, func() { - obj, err := objects.Merge(context.Background(), values.NewArray(0), values.NewArray(0)) - - So(err, ShouldBeError) - So(obj, ShouldEqual, values.None) - }) - - Convey("When argument isn't array", t, func() { - obj, err := objects.Merge(context.Background(), values.NewInt(0)) - - So(err, ShouldBeError) - So(obj, ShouldEqual, values.None) - }) -} - -func isEqual(obj1 *values.Object, obj2 *values.Object) bool { - var val1 core.Value - var val2 core.Value - - for _, key := range obj1.Keys() { - val1, _ = obj1.Get(values.NewString(key)) - val2, _ = obj2.Get(values.NewString(key)) - if val1.Compare(val2) != 0 { - return false - } - } - for _, key := range obj2.Keys() { - val1, _ = obj1.Get(values.NewString(key)) - val2, _ = obj2.Get(values.NewString(key)) - if val2.Compare(val1) != 0 { - return false - } - } - return true } From f761dd802d5f0b825713c4f5fe01af723ff63c3b Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 16:33:37 +0300 Subject: [PATCH 038/115] added MERGE function --- pkg/stdlib/objects/merge.go | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/pkg/stdlib/objects/merge.go b/pkg/stdlib/objects/merge.go index 98e163e1..60780355 100644 --- a/pkg/stdlib/objects/merge.go +++ b/pkg/stdlib/objects/merge.go @@ -25,7 +25,7 @@ func Merge(_ context.Context, args ...core.Value) (core.Value, error) { objs = args[0].(*values.Array) } - err = validateTopLevelObjects(objs) + err = validateArrayOf(core.ObjectType, objs) if err != nil { return values.None, err @@ -34,10 +34,34 @@ func Merge(_ context.Context, args ...core.Value) (core.Value, error) { return mergeArray(objs), nil } -func validateTopLevelObjects(arr *values.Array) error { - return nil +func mergeArray(arr *values.Array) *values.Object { + merged := values.NewObject() + + var val core.Value + var k values.String + var obj *values.Object + + for idx := values.NewInt(0); idx < arr.Length(); idx++ { + obj = arr.Get(idx).(*values.Object) + for _, key := range obj.Keys() { + k = values.NewString(key) + val, _ = obj.Get(k) + if values.IsCloneable(val) { + val = val.(core.Cloneable).Clone() + } + merged.Set(k, val) + } + } + + return merged } -func mergeArray(arr *values.Array) *values.Object { - return values.NewObject() +func validateArrayOf(typ core.Type, arr *values.Array) (err error) { + for idx := values.NewInt(0); idx < arr.Length(); idx++ { + if err != nil { + break + } + err = core.ValidateType(arr.Get(idx), typ) + } + return } From 599768fa68fe882a8f55c4aff581cd7420fef479 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 16:34:16 +0300 Subject: [PATCH 039/115] added MERGE to lib --- pkg/stdlib/objects/lib.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/stdlib/objects/lib.go b/pkg/stdlib/objects/lib.go index b4d61690..850ad6bd 100644 --- a/pkg/stdlib/objects/lib.go +++ b/pkg/stdlib/objects/lib.go @@ -4,8 +4,9 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "HAS": Has, - "KEYS": Keys, - "KEEP": Keep, + "HAS": Has, + "KEYS": Keys, + "KEEP": Keep, + "MERGE": Merge, } } From 56a36800a7789c05dd47f91afc37c3f3ffde0515 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 19:38:38 +0300 Subject: [PATCH 040/115] added one more test --- pkg/stdlib/objects/merge_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pkg/stdlib/objects/merge_test.go b/pkg/stdlib/objects/merge_test.go index 89ca26c5..de054d35 100644 --- a/pkg/stdlib/objects/merge_test.go +++ b/pkg/stdlib/objects/merge_test.go @@ -36,6 +36,30 @@ func TestMerge(t *testing.T) { So(err, ShouldBeError) So(obj.Compare(values.None), ShouldEqual, 0) }) + + Convey("Merged object should be independent of source objects", t, func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("prop1", values.NewInt(1)), + values.NewObjectProperty("prop2", values.NewString("str")), + ) + obj2 := values.NewObjectWith( + values.NewObjectProperty("prop3", values.NewInt(3)), + ) + + result := values.NewObjectWith( + values.NewObjectProperty("prop1", values.NewInt(1)), + values.NewObjectProperty("prop2", values.NewString("str")), + values.NewObjectProperty("prop3", values.NewInt(3)), + ) + + merged, err := objects.Merge(context.Background(), obj1, obj2) + + So(err, ShouldBeNil) + + obj1.Remove(values.NewString("prop1")) + + So(merged.Compare(result), ShouldEqual, 0) + }) } func TestMergeObjects(t *testing.T) { From fcce07256e5c5a54c1224620245b129c959a0869 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 19:39:01 +0300 Subject: [PATCH 041/115] changed MERGE function --- pkg/stdlib/objects/merge.go | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/pkg/stdlib/objects/merge.go b/pkg/stdlib/objects/merge.go index 60780355..5884cbf3 100644 --- a/pkg/stdlib/objects/merge.go +++ b/pkg/stdlib/objects/merge.go @@ -35,23 +35,19 @@ func Merge(_ context.Context, args ...core.Value) (core.Value, error) { } func mergeArray(arr *values.Array) *values.Object { - merged := values.NewObject() + merged, obj := values.NewObject(), values.NewObject() - var val core.Value - var k values.String - var obj *values.Object - - for idx := values.NewInt(0); idx < arr.Length(); idx++ { - obj = arr.Get(idx).(*values.Object) - for _, key := range obj.Keys() { - k = values.NewString(key) - val, _ = obj.Get(k) - if values.IsCloneable(val) { - val = val.(core.Cloneable).Clone() + arr.ForEach(func(arrValue core.Value, arrIdx int) bool { + obj = arrValue.(*values.Object) + obj.ForEach(func(objValue core.Value, objKey string) bool { + if values.IsCloneable(objValue) { + objValue = objValue.(core.Cloneable).Clone() } - merged.Set(k, val) - } - } + merged.Set(values.NewString(objKey), objValue) + return true + }) + return true + }) return merged } From 95d50624116cb5a5855d7322080e3b652818bd07 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 20:30:29 +0300 Subject: [PATCH 042/115] rewrite a few comments according to Go Best Practices --- pkg/html/common/lazy.go | 31 ++++++++++++------------------ pkg/stdlib/arrays/append.go | 13 ++++++------- pkg/stdlib/arrays/first.go | 9 ++++----- pkg/stdlib/arrays/flatten.go | 19 +++++++++--------- pkg/stdlib/arrays/intersection.go | 13 ++++++------- pkg/stdlib/arrays/last.go | 9 ++++----- pkg/stdlib/arrays/minus.go | 11 +++++------ pkg/stdlib/arrays/nth.go | 15 +++++++-------- pkg/stdlib/arrays/outersection.go | 11 +++++------ pkg/stdlib/arrays/pop.go | 9 ++++----- pkg/stdlib/arrays/position.go | 11 +++++------ pkg/stdlib/arrays/push.go | 13 ++++++------- pkg/stdlib/arrays/remove_nth.go | 11 +++++------ pkg/stdlib/arrays/remove_value.go | 15 +++++++-------- pkg/stdlib/arrays/remove_values.go | 11 +++++------ pkg/stdlib/arrays/reverse.go | 9 ++++----- 16 files changed, 94 insertions(+), 116 deletions(-) diff --git a/pkg/html/common/lazy.go b/pkg/html/common/lazy.go index 4727dc90..74562174 100644 --- a/pkg/html/common/lazy.go +++ b/pkg/html/common/lazy.go @@ -1,9 +1,10 @@ package common import ( + "sync" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "sync" ) type ( @@ -27,10 +28,8 @@ func NewLazyValue(factory LazyFactory) *LazyValue { return lz } -/* - * Indicates whether the value is ready. - * @returns (Boolean) - Boolean value indicating whether the value is ready. - */ +// Ready indicates whether the value is ready. +// @returns (Boolean) - Boolean value indicating whether the value is ready. func (lv *LazyValue) Ready() bool { lv.Lock() defer lv.Unlock() @@ -38,11 +37,9 @@ func (lv *LazyValue) Ready() bool { return lv.ready } -/* - * Returns an underlying value. - * Not thread safe. Should not mutated. - * @returns (Value) - Underlying value if successfully loaded, otherwise error - */ +// Read returns an underlying value. +// Not thread safe. Should not mutated. +// @returns (Value) - Underlying value if successfully loaded, otherwise error func (lv *LazyValue) Read() (core.Value, error) { lv.Lock() defer lv.Unlock() @@ -54,11 +51,9 @@ func (lv *LazyValue) Read() (core.Value, error) { return lv.value, lv.err } -/* - * Safely mutates an underlying value. - * Loads a value if it's not ready. - * Thread safe. - */ +// Write safely mutates an underlying value. +// Loads a value if it's not ready. +// Thread safe. func (lv *LazyValue) Write(writer func(v core.Value, err error)) { lv.Lock() defer lv.Unlock() @@ -70,10 +65,8 @@ func (lv *LazyValue) Write(writer func(v core.Value, err error)) { writer(lv.value, lv.err) } -/* - * Resets the storage. - * Next call of Read will trigger the factory function again. - */ +// Reset resets the storage. +// Next call of Read will trigger the factory function again. func (lv *LazyValue) Reset() { lv.Lock() defer lv.Unlock() diff --git a/pkg/stdlib/arrays/append.go b/pkg/stdlib/arrays/append.go index 821dea22..a26b9086 100644 --- a/pkg/stdlib/arrays/append.go +++ b/pkg/stdlib/arrays/append.go @@ -2,17 +2,16 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Appends a new item to an array and returns a new array with a given element. - * If ``uniqueOnly`` is set to true, then will add the item only if it's unique. - * @param arr (Array) - Target array. - * @param item (Read) - Target value to add. - * @returns arr (Array) - New array. - */ +// Append appends a new item to an array and returns a new array with a given element. +// If ``uniqueOnly`` is set to true, then will add the item only if it's unique. +// @param arr (Array) - Target array. +// @param item (Read) - Target value to add. +// @returns arr (Array) - New array. func Append(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/arrays/first.go b/pkg/stdlib/arrays/first.go index 07f4a6d5..9028676f 100644 --- a/pkg/stdlib/arrays/first.go +++ b/pkg/stdlib/arrays/first.go @@ -2,15 +2,14 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a first element from a given array. - * @param arr (Array) - Target array. - * @returns element (Read) - First element in a given array. - */ +// First returns a first element from a given array. +// @param arr (Array) - Target array. +// @returns element (Read) - First element in a given array. func First(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/arrays/flatten.go b/pkg/stdlib/arrays/flatten.go index c134e71a..267a8ef0 100644 --- a/pkg/stdlib/arrays/flatten.go +++ b/pkg/stdlib/arrays/flatten.go @@ -2,20 +2,19 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Turn an array of arrays into a flat array. - * All array elements in array will be expanded in the result array. - * Non-array elements are added as they are. - * The function will recurse into sub-arrays up to the specified depth. - * Duplicates will not be removed. - * @param arr (Array) - Target array. - * @param depth (Int, optional) - Depth level. - * @returns (Array) - Flat array. - */ +// Flatten turn an array of arrays into a flat array. +// All array elements in array will be expanded in the result array. +// Non-array elements are added as they are. +// The function will recurse into sub-arrays up to the specified depth. +// Duplicates will not be removed. +// @param arr (Array) - Target array. +// @param depth (Int, optional) - Depth level. +// @returns (Array) - Flat array. func Flatten(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) diff --git a/pkg/stdlib/arrays/intersection.go b/pkg/stdlib/arrays/intersection.go index 77a419ae..afe06826 100644 --- a/pkg/stdlib/arrays/intersection.go +++ b/pkg/stdlib/arrays/intersection.go @@ -2,17 +2,16 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Return the intersection of all arrays specified. - * The result is an array of values that occur in all arguments. - * @param arrays (Array, repeated) - An arbitrary number of arrays as multiple arguments (at least 2). - * @returns (Array) - A single array with only the elements, which exist in all provided arrays. - * The element order is random. Duplicates are removed. - */ +// Intersection return the intersection of all arrays specified. +// The result is an array of values that occur in all arguments. +// @param arrays (Array, repeated) - An arbitrary number of arrays as multiple arguments (at least 2). +// @returns (Array) - A single array with only the elements, which exist in all provided arrays. +// The element order is random. Duplicates are removed. func Intersection(_ context.Context, args ...core.Value) (core.Value, error) { return sections(args, len(args)) } diff --git a/pkg/stdlib/arrays/last.go b/pkg/stdlib/arrays/last.go index b56c38c9..3b7dbc21 100644 --- a/pkg/stdlib/arrays/last.go +++ b/pkg/stdlib/arrays/last.go @@ -2,15 +2,14 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns the last element of an array. - * @param array (Array) - The target array. - * @returns (Read) - Last element of an array. - */ +// Last returns the last element of an array. +// @param array (Array) - The target array. +// @returns (Read) - Last element of an array. func Last(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/arrays/minus.go b/pkg/stdlib/arrays/minus.go index da58f200..5344825b 100644 --- a/pkg/stdlib/arrays/minus.go +++ b/pkg/stdlib/arrays/minus.go @@ -2,16 +2,15 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Return the difference of all arrays specified. - * @param arrays (Array, repeated) - An arbitrary number of arrays as multiple arguments (at least 2). - * @returns array (Array) - An array of values that occur in the first array, but not in any of the subsequent arrays. - * The order of the result array is undefined and should not be relied on. Duplicates will be removed. - */ +// Minus return the difference of all arrays specified. +// @param arrays (Array, repeated) - An arbitrary number of arrays as multiple arguments (at least 2). +// @returns array (Array) - An array of values that occur in the first array, but not in any of the subsequent arrays. +// The order of the result array is undefined and should not be relied on. Duplicates will be removed. func Minus(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, core.MaxArgs) diff --git a/pkg/stdlib/arrays/nth.go b/pkg/stdlib/arrays/nth.go index f8cf2bbb..381856e8 100644 --- a/pkg/stdlib/arrays/nth.go +++ b/pkg/stdlib/arrays/nth.go @@ -2,18 +2,17 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns the element of an array at a given position. - * It is the same as anyArray[position] for positive positions, but does not support negative positions. - * @param array (Array) - An array with elements of arbitrary type. - * @param index (Int) - Position of desired element in array, positions start at 0. - * @returns (Read) - The array element at the given position. - * If position is negative or beyond the upper bound of the array, then NONE will be returned. - */ +// Nth returns the element of an array at a given position. +// It is the same as anyArray[position] for positive positions, but does not support negative positions. +// @param array (Array) - An array with elements of arbitrary type. +// @param index (Int) - Position of desired element in array, positions start at 0. +// @returns (Read) - The array element at the given position. +// If position is negative or beyond the upper bound of the array, then NONE will be returned. func Nth(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/arrays/outersection.go b/pkg/stdlib/arrays/outersection.go index 6e25c398..d7b2491e 100644 --- a/pkg/stdlib/arrays/outersection.go +++ b/pkg/stdlib/arrays/outersection.go @@ -2,15 +2,14 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" ) -/* - * Return the values that occur only once across all arrays specified. - * @param arrays (Array, repeated) - An arbitrary number of arrays as multiple arguments (at least 2). - * @returns (Array) - A single array with only the elements that exist only once across all provided arrays. - * The element order is random. - */ +// Outersection return the values that occur only once across all arrays specified. +// @param arrays (Array, repeated) - An arbitrary number of arrays as multiple arguments (at least 2). +// @returns (Array) - A single array with only the elements that exist only once across all provided arrays. +// The element order is random. func Outersection(_ context.Context, args ...core.Value) (core.Value, error) { return sections(args, 1) } diff --git a/pkg/stdlib/arrays/pop.go b/pkg/stdlib/arrays/pop.go index 6329f3f2..19d598e5 100644 --- a/pkg/stdlib/arrays/pop.go +++ b/pkg/stdlib/arrays/pop.go @@ -2,15 +2,14 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a new array without last element. - * @param array (Array) - Target array. - * @returns (Array) - Copy of an array without last element. - */ +// Pop returns a new array without last element. +// @param array (Array) - Target array. +// @returns (Array) - Copy of an array without last element. func Pop(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/arrays/position.go b/pkg/stdlib/arrays/position.go index ef8c71e7..f63fa50f 100644 --- a/pkg/stdlib/arrays/position.go +++ b/pkg/stdlib/arrays/position.go @@ -2,16 +2,15 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a value indicating whether an element is contained in array. Optionally returns its position. - * @param array (Array) - The source array. - * @param value (Read) - The target value. - * @param returnIndex (Boolean, optional) - Read which indicates whether to return item's position. - */ +// Position returns a value indicating whether an element is contained in array. Optionally returns its position. +// @param array (Array) - The source array. +// @param value (Read) - The target value. +// @param returnIndex (Boolean, optional) - Read which indicates whether to return item's position. func Position(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/arrays/push.go b/pkg/stdlib/arrays/push.go index d7f1e92c..965b5d3c 100644 --- a/pkg/stdlib/arrays/push.go +++ b/pkg/stdlib/arrays/push.go @@ -2,17 +2,16 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Create a new array with appended value. - * @param array (Array) - Source array. - * @param value (Read) - Target value. - * @param unique (Boolean, optional) - Read indicating whether to do uniqueness check. - * @returns (Array) - A new array with appended value. - */ +// Push create a new array with appended value. +// @param array (Array) - Source array. +// @param value (Read) - Target value. +// @param unique (Boolean, optional) - Read indicating whether to do uniqueness check. +// @returns (Array) - A new array with appended value. func Push(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/arrays/remove_nth.go b/pkg/stdlib/arrays/remove_nth.go index d371eae5..8ffcda22 100644 --- a/pkg/stdlib/arrays/remove_nth.go +++ b/pkg/stdlib/arrays/remove_nth.go @@ -2,16 +2,15 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a new array without an element by a given position. - * @param array (Array) - Source array. - * @param position (Int) - Target element position. - * @return (Array) - A new array without an element by a given position. - */ +// RemoveNth returns a new array without an element by a given position. +// @param array (Array) - Source array. +// @param position (Int) - Target element position. +// @return (Array) - A new array without an element by a given position. func RemoveNth(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/arrays/remove_value.go b/pkg/stdlib/arrays/remove_value.go index 96b8f779..753593c8 100644 --- a/pkg/stdlib/arrays/remove_value.go +++ b/pkg/stdlib/arrays/remove_value.go @@ -2,18 +2,17 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a new array with removed all occurrences of value in a given array. - * Optionally with a limit to the number of removals. - * @param array (Array) - Source array. - * @param value (Read) - Target value. - * @param limit (Int, optional) - A limit to the number of removals. - * @returns (Array) - A new array with removed all occurrences of value in a given array. - */ +// RemoveValue returns a new array with removed all occurrences of value in a given array. +// Optionally with a limit to the number of removals. +// @param array (Array) - Source array. +// @param value (Read) - Target value. +// @param limit (Int, optional) - A limit to the number of removals. +// @returns (Array) - A new array with removed all occurrences of value in a given array. func RemoveValue(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/arrays/remove_values.go b/pkg/stdlib/arrays/remove_values.go index 0e928827..6e6b46b9 100644 --- a/pkg/stdlib/arrays/remove_values.go +++ b/pkg/stdlib/arrays/remove_values.go @@ -2,16 +2,15 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a new array with removed all occurrences of values in a given array. - * @param array (Array) - Source array. - * @param values (Array) - Target values. - * @returns (Array) - A new array with removed all occurrences of values in a given array. - */ +// RemoveValues returns a new array with removed all occurrences of values in a given array. +// @param array (Array) - Source array. +// @param values (Array) - Target values. +// @returns (Array) - A new array with removed all occurrences of values in a given array. func RemoveValues(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/arrays/reverse.go b/pkg/stdlib/arrays/reverse.go index b5fc10a2..22554094 100644 --- a/pkg/stdlib/arrays/reverse.go +++ b/pkg/stdlib/arrays/reverse.go @@ -2,15 +2,14 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Return a new array with its elements reversed. - * @param array (Array) - Target array. - * @returns (Array) - A new array with its elements reversed. - */ +// Reverse return a new array with its elements reversed. +// @param array (Array) - Target array. +// @returns (Array) - A new array with its elements reversed. func Reverse(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) From 9f2417216494b8353638f89fc70502a6ec009c0e Mon Sep 17 00:00:00 2001 From: = Date: Sun, 14 Oct 2018 17:53:08 +0300 Subject: [PATCH 043/115] rewrite comments --- pkg/stdlib/arrays/shift.go | 9 ++--- pkg/stdlib/arrays/slice.go | 13 +++---- pkg/stdlib/arrays/sorted.go | 11 +++--- pkg/stdlib/arrays/sorted_unique.go | 13 +++---- pkg/stdlib/arrays/union.go | 9 ++--- pkg/stdlib/arrays/union_test.go | 11 +++--- pkg/stdlib/arrays/unique.go | 9 ++--- pkg/stdlib/arrays/unshift.go | 15 ++++---- pkg/stdlib/html/blob.go | 22 +++++------ pkg/stdlib/html/click.go | 9 ++--- pkg/stdlib/html/click_all.go | 11 +++--- pkg/stdlib/html/document.go | 15 ++++---- pkg/stdlib/html/document_parse.go | 11 +++--- pkg/stdlib/html/element.go | 13 +++---- pkg/stdlib/html/elements.go | 13 +++---- pkg/stdlib/html/elements_count.go | 13 +++---- pkg/stdlib/html/inner_html.go | 11 +++--- pkg/stdlib/html/inner_html_all.go | 11 +++--- pkg/stdlib/html/inner_text.go | 11 +++--- pkg/stdlib/html/inner_text_all.go | 11 +++--- pkg/stdlib/html/input.go | 14 +++---- pkg/stdlib/html/navigate.go | 15 ++++---- pkg/stdlib/html/navigate_back.go | 17 ++++----- pkg/stdlib/html/navigate_forward.go | 17 ++++----- pkg/stdlib/html/wait_class.go | 21 +++++------ pkg/stdlib/html/wait_class_all.go | 15 ++++---- pkg/stdlib/html/wait_element.go | 13 +++---- pkg/stdlib/html/wait_navigation.go | 11 +++--- pkg/stdlib/objects/has.go | 8 ++-- pkg/stdlib/objects/keep.go | 10 ++--- pkg/stdlib/objects/keys.go | 10 ++--- pkg/stdlib/objects/merge.go | 8 ++-- pkg/stdlib/strings/case.go | 19 ++++------ pkg/stdlib/strings/concat.go | 19 ++++------ pkg/stdlib/strings/contains.go | 15 ++++---- pkg/stdlib/strings/encode.go | 43 +++++++++------------- pkg/stdlib/strings/find.go | 35 ++++++++---------- pkg/stdlib/strings/json.go | 17 ++++----- pkg/stdlib/strings/like.go | 12 +++--- pkg/stdlib/strings/random.go | 13 +++---- pkg/stdlib/strings/regex.go | 55 ++++++++++++---------------- pkg/stdlib/strings/reverse.go | 9 ++--- pkg/stdlib/strings/split.go | 15 ++++---- pkg/stdlib/strings/substitute.go | 17 ++++----- pkg/stdlib/strings/substr.go | 33 +++++++---------- pkg/stdlib/strings/trim.go | 33 +++++++---------- pkg/stdlib/types/is_array.go | 9 ++--- pkg/stdlib/types/is_binary.go | 9 ++--- pkg/stdlib/types/is_boolean.go | 9 ++--- pkg/stdlib/types/is_date_time.go | 9 ++--- pkg/stdlib/types/is_float.go | 9 ++--- pkg/stdlib/types/is_html_document.go | 9 ++--- pkg/stdlib/types/is_html_element.go | 9 ++--- pkg/stdlib/types/is_int.go | 9 ++--- pkg/stdlib/types/is_none.go | 9 ++--- pkg/stdlib/types/is_object.go | 9 ++--- pkg/stdlib/types/is_string.go | 9 ++--- pkg/stdlib/types/to_array.go | 17 ++++----- pkg/stdlib/types/to_boolean.go | 21 +++++------ pkg/stdlib/types/to_date_time.go | 9 ++--- pkg/stdlib/types/to_float.go | 27 +++++++------- pkg/stdlib/types/to_int.go | 27 +++++++------- pkg/stdlib/types/to_string.go | 9 ++--- pkg/stdlib/types/type_name.go | 9 ++--- pkg/stdlib/utils/log.go | 5 +-- pkg/stdlib/utils/wait.go | 9 ++--- 66 files changed, 432 insertions(+), 535 deletions(-) diff --git a/pkg/stdlib/arrays/shift.go b/pkg/stdlib/arrays/shift.go index 4638eef7..fb1fd262 100644 --- a/pkg/stdlib/arrays/shift.go +++ b/pkg/stdlib/arrays/shift.go @@ -2,15 +2,14 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a new array without the first element. - * @param array (Array) - Target array. - * @returns (Array) - Copy of an array without the first element. - */ +// Shift returns a new array without the first element. +// @param array (Array) - Target array. +// @returns (Array) - Copy of an array without the first element. func Shift(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/arrays/slice.go b/pkg/stdlib/arrays/slice.go index fdda740a..26e4ab6b 100644 --- a/pkg/stdlib/arrays/slice.go +++ b/pkg/stdlib/arrays/slice.go @@ -2,17 +2,16 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a new sliced array. - * @param array (Array) - Source array. - * @param start (Int) - Start position of extraction. - * @param length (Int, optional) - Read indicating how many elements to extract. - * @returns (Array) - Sliced array. - */ +// Slice returns a new sliced array. +// @param array (Array) - Source array. +// @param start (Int) - Start position of extraction. +// @param length (Int, optional) - Read indicating how many elements to extract. +// @returns (Array) - Sliced array. func Slice(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/arrays/sorted.go b/pkg/stdlib/arrays/sorted.go index b7cb1e93..f7ee67a6 100644 --- a/pkg/stdlib/arrays/sorted.go +++ b/pkg/stdlib/arrays/sorted.go @@ -2,17 +2,16 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/collections" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Sorts all elements in anyArray. - * The function will use the default comparison order for FQL value types. - * @param array (Array) - Target array. - * @returns (Array) - Sorted array. - */ +// Sorted sorts all elements in anyArray. +// The function will use the default comparison order for FQL value types. +// @param array (Array) - Target array. +// @returns (Array) - Sorted array. func Sorted(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/arrays/sorted_unique.go b/pkg/stdlib/arrays/sorted_unique.go index 748443d3..a90a96ab 100644 --- a/pkg/stdlib/arrays/sorted_unique.go +++ b/pkg/stdlib/arrays/sorted_unique.go @@ -2,18 +2,17 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/collections" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Sorts all elements in anyArray. - * The function will use the default comparison order for FQL value types. - * Additionally, the values in the result array will be made unique - * @param array (Array) - Target array. - * @returns (Array) - Sorted array. - */ +// SortedUnique sorts all elements in anyArray. +// The function will use the default comparison order for FQL value types. +// Additionally, the values in the result array will be made unique +// @param array (Array) - Target array. +// @returns (Array) - Sorted array. func SortedUnique(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/arrays/union.go b/pkg/stdlib/arrays/union.go index 9b2fa82c..25ecc7b8 100644 --- a/pkg/stdlib/arrays/union.go +++ b/pkg/stdlib/arrays/union.go @@ -2,15 +2,14 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns the union of all passed arrays. - * @param arrays (Array, repeated) - List of arrays to combine. - * @returns (Array) - All array elements combined in a single array, in any order. - */ +// Union returns the union of all passed arrays. +// @param arrays (Array, repeated) - List of arrays to combine. +// @returns (Array) - All array elements combined in a single array, in any order. func Union(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, core.MaxArgs) diff --git a/pkg/stdlib/arrays/union_test.go b/pkg/stdlib/arrays/union_test.go index 563e47ea..707c6f77 100644 --- a/pkg/stdlib/arrays/union_test.go +++ b/pkg/stdlib/arrays/union_test.go @@ -2,17 +2,16 @@ package arrays_test import ( "context" + "testing" + "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/stdlib/arrays" . "github.com/smartystreets/goconvey/convey" - "testing" ) -/* - * Returns the union of distinct values of all passed arrays. - * @param arrays (Array, repeated) - List of arrays to combine. - * @returns (Array) - All array elements combined in a single array, without duplicates, in any order. - */ +// TestUnion returns the union of distinct values of all passed arrays. +// @param arrays (Array, repeated) - List of arrays to combine. +// @returns (Array) - All array elements combined in a single array, without duplicates, in any order. func TestUnion(t *testing.T) { Convey("Should union all arrays", t, func() { arr1 := values.NewArrayWith( diff --git a/pkg/stdlib/arrays/unique.go b/pkg/stdlib/arrays/unique.go index cda933b5..8e9808c2 100644 --- a/pkg/stdlib/arrays/unique.go +++ b/pkg/stdlib/arrays/unique.go @@ -2,16 +2,15 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/collections" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns all unique elements from a given array. - * @param array (Array) - Target array. - * @returns (Array) - New array without duplicates. - */ +// Unique returns all unique elements from a given array. +// @param array (Array) - Target array. +// @returns (Array) - New array without duplicates. func Unique(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/arrays/unshift.go b/pkg/stdlib/arrays/unshift.go index 002c48cf..51c8ade8 100644 --- a/pkg/stdlib/arrays/unshift.go +++ b/pkg/stdlib/arrays/unshift.go @@ -2,18 +2,17 @@ package arrays import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Prepends value to a given array. - * @param array (Array) - Target array. - * @param value (Read) - Target value to prepend. - * @param unique (Boolean, optional) - Optional value indicating whether a value must be unique to be prepended. - * Default is false. - * @returns (Array) - New array with prepended value. - */ +// Unshift prepends value to a given array. +// @param array (Array) - Target array. +// @param value (Read) - Target value to prepend. +// @param unique (Boolean, optional) - Optional value indicating whether a value must be unique to be prepended. +// Default is false. +// @returns (Array) - New array with prepended value. func Unshift(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/html/blob.go b/pkg/stdlib/html/blob.go index dd72b0e5..b9e6b2c2 100644 --- a/pkg/stdlib/html/blob.go +++ b/pkg/stdlib/html/blob.go @@ -10,18 +10,16 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/core" ) -/* - * Take a screenshot of the current page. - * @param source (Document) - Document. - * @param params (Object) - Optional, An object containing the following properties : - * x (Float|Int) - Optional, X position of the viewport. - * x (Float|Int) - Optional,Y position of the viewport. - * width (Float|Int) - Optional, Width of the viewport. - * height (Float|Int) - Optional, Height of the viewport. - * format (String) - Optional, Either "jpeg" or "png". - * quality (Int) - Optional, Quality, in [0, 100], only for jpeg format. - * @returns data (Binary) - Returns a base64 encoded string in binary format. - */ +// Screenshot take a screenshot of the current page. +// @param source (Document) - Document. +// @param params (Object) - Optional, An object containing the following properties : +// x (Float|Int) - Optional, X position of the viewport. +// x (Float|Int) - Optional,Y position of the viewport. +// width (Float|Int) - Optional, Width of the viewport. +// height (Float|Int) - Optional, Height of the viewport. +// format (String) - Optional, Either "jpeg" or "png". +// quality (Int) - Optional, Quality, in [0, 100], only for jpeg format. +// @returns data (Binary) - Returns a base64 encoded string in binary format. func Screenshot(ctx context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) if err != nil { diff --git a/pkg/stdlib/html/click.go b/pkg/stdlib/html/click.go index c97af7fe..d5af7a10 100644 --- a/pkg/stdlib/html/click.go +++ b/pkg/stdlib/html/click.go @@ -2,16 +2,15 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Dispatches click event on a given element - * @param source (Document | Element) - Event source. - * @param selector (String, optional) - Optional selector. Only used when a document instance is passed. - */ +// Click dispatches click event on a given element +// @param source (Document | Element) - Event source. +// @param selector (String, optional) - Optional selector. Only used when a document instance is passed. func Click(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) diff --git a/pkg/stdlib/html/click_all.go b/pkg/stdlib/html/click_all.go index 7499f52c..04ab04a5 100644 --- a/pkg/stdlib/html/click_all.go +++ b/pkg/stdlib/html/click_all.go @@ -2,17 +2,16 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Dispatches click event on all matched element - * @param source (Document) - Document. - * @param selector (String) - Selector. - * @returns (Boolean) - Returns true if matched at least one element. - */ +// ClickAll dispatches click event on all matched element +// @param source (Document) - Document. +// @param selector (String) - Selector. +// @returns (Boolean) - Returns true if matched at least one element. func ClickAll(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/html/document.go b/pkg/stdlib/html/document.go index 5dfd4002..5c95a0bc 100644 --- a/pkg/stdlib/html/document.go +++ b/pkg/stdlib/html/document.go @@ -2,19 +2,18 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Loads a document by a given url. - * By default, loads a document by http call - resulted document does not support any interactions. - * If passed "true" as a second argument, headless browser is used for loading the document which support interactions. - * @param url (String) - Target url string. If passed "about:blank" for dynamic document - it will open an empty page. - * @param dynamic (Boolean) - Optional boolean value indicating whether to use dynamic document. - * @returns (HTMLDocument) - Returns loaded HTML document. - */ +// Document loads a document by a given url. +// By default, loads a document by http call - resulted document does not support any interactions. +// If passed "true" as a second argument, headless browser is used for loading the document which support interactions. +// @param url (String) - Target url string. If passed "about:blank" for dynamic document - it will open an empty page. +// @param dynamic (Boolean) - Optional boolean value indicating whether to use dynamic document. +// @returns (HTMLDocument) - Returns loaded HTML document. func Document(ctx context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) diff --git a/pkg/stdlib/html/document_parse.go b/pkg/stdlib/html/document_parse.go index e87130a8..90aaee8c 100644 --- a/pkg/stdlib/html/document_parse.go +++ b/pkg/stdlib/html/document_parse.go @@ -2,18 +2,17 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html" "github.com/MontFerret/ferret/pkg/html/static" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Parses a given HTML string and returns a HTML document. - * Returned HTML document is always static. - * @param html (String) - Target HTML string. - * @returns (HTMLDocument) - Parsed HTML static document. - */ +// DocumentParse parses a given HTML string and returns a HTML document. +// Returned HTML document is always static. +// @param html (String) - Target HTML string. +// @returns (HTMLDocument) - Parsed HTML static document. func DocumentParse(ctx context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/html/element.go b/pkg/stdlib/html/element.go index a29c99a3..e548609f 100644 --- a/pkg/stdlib/html/element.go +++ b/pkg/stdlib/html/element.go @@ -2,17 +2,16 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Finds an element by a given CSS selector. - * Returns NONE if element not found. - * @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element. - * @param selector (String) - CSS selector. - * @returns (HTMLElement | None) - Returns an HTMLElement if found, otherwise NONE. - */ +// Element finds an element by a given CSS selector. +// Returns NONE if element not found. +// @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element. +// @param selector (String) - CSS selector. +// @returns (HTMLElement | None) - Returns an HTMLElement if found, otherwise NONE. func Element(_ context.Context, args ...core.Value) (core.Value, error) { el, selector, err := queryArgs(args) diff --git a/pkg/stdlib/html/elements.go b/pkg/stdlib/html/elements.go index 433871e7..c387f6cc 100644 --- a/pkg/stdlib/html/elements.go +++ b/pkg/stdlib/html/elements.go @@ -2,17 +2,16 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Finds HTML elements by a given CSS selector. - * Returns an empty array if element not found. - * @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element. - * @param selector (String) - CSS selector. - * @returns (Array) - Returns an array of found HTML element. - */ +// Elements finds HTML elements by a given CSS selector. +// Returns an empty array if element not found. +// @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element. +// @param selector (String) - CSS selector. +// @returns (Array) - Returns an array of found HTML element. func Elements(_ context.Context, args ...core.Value) (core.Value, error) { el, selector, err := queryArgs(args) diff --git a/pkg/stdlib/html/elements_count.go b/pkg/stdlib/html/elements_count.go index 84cb73cd..92fe9367 100644 --- a/pkg/stdlib/html/elements_count.go +++ b/pkg/stdlib/html/elements_count.go @@ -2,17 +2,16 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a number of found HTML elements by a given CSS selector. - * Returns an empty array if element not found. - * @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element. - * @param selector (String) - CSS selector. - * @returns (Int) - A number of found HTML elements by a given CSS selector. - */ +// ElementsCount returns a number of found HTML elements by a given CSS selector. +// Returns an empty array if element not found. +// @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element. +// @param selector (String) - CSS selector. +// @returns (Int) - A number of found HTML elements by a given CSS selector. func ElementsCount(_ context.Context, args ...core.Value) (core.Value, error) { el, selector, err := queryArgs(args) diff --git a/pkg/stdlib/html/inner_html.go b/pkg/stdlib/html/inner_html.go index f093e88c..63cbf8dc 100644 --- a/pkg/stdlib/html/inner_html.go +++ b/pkg/stdlib/html/inner_html.go @@ -2,16 +2,15 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns inner HTML string of a matched element - * @param doc (Document|Element) - Parent document or element. - * @param selector (String) - String of CSS selector. - * @returns (String) - Inner HTML string if an element found, otherwise empty string. - */ +// InnerHTML Returns inner HTML string of a matched element +// @param doc (Document|Element) - Parent document or element. +// @param selector (String) - String of CSS selector. +// @returns (String) - Inner HTML string if an element found, otherwise empty string. func InnerHTML(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/html/inner_html_all.go b/pkg/stdlib/html/inner_html_all.go index c518930e..11ccaf28 100644 --- a/pkg/stdlib/html/inner_html_all.go +++ b/pkg/stdlib/html/inner_html_all.go @@ -2,16 +2,15 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns an array of inner HTML strings of matched elements. - * @param doc (HTMLDocument|HTMLElement) - Parent document or element. - * @param selector (String) - String of CSS selector. - * @returns (String) - An array of inner HTML strings if any element found, otherwise empty array. - */ +// InnerHTMLAll returns an array of inner HTML strings of matched elements. +// @param doc (HTMLDocument|HTMLElement) - Parent document or element. +// @param selector (String) - String of CSS selector. +// @returns (String) - An array of inner HTML strings if any element found, otherwise empty array. func InnerHTMLAll(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/html/inner_text.go b/pkg/stdlib/html/inner_text.go index 9eea1831..ea6aaa83 100644 --- a/pkg/stdlib/html/inner_text.go +++ b/pkg/stdlib/html/inner_text.go @@ -2,16 +2,15 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns inner text of a matched element - * @param doc (HTMLDocument|HTMLElement) - Parent document or element. - * @param selector (String) - String of CSS selector. - * @returns (String) - Inner text if an element found, otherwise empty string. - */ +// InnerText returns inner text of a matched element +// @param doc (HTMLDocument|HTMLElement) - Parent document or element. +// @param selector (String) - String of CSS selector. +// @returns (String) - Inner text if an element found, otherwise empty string. func InnerText(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/html/inner_text_all.go b/pkg/stdlib/html/inner_text_all.go index 040a362e..2d8bea91 100644 --- a/pkg/stdlib/html/inner_text_all.go +++ b/pkg/stdlib/html/inner_text_all.go @@ -2,16 +2,15 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns an array of inner text of matched elements. - * @param doc (HTMLDocument|HTMLElement) - Parent document or element. - * @param selector (String) - String of CSS selector. - * @returns (String) - An array of inner text if any element found, otherwise empty array. - */ +// InnerTextAll returns an array of inner text of matched elements. +// @param doc (HTMLDocument|HTMLElement) - Parent document or element. +// @param selector (String) - String of CSS selector. +// @returns (String) - An array of inner text if any element found, otherwise empty array. func InnerTextAll(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/html/input.go b/pkg/stdlib/html/input.go index 401361c6..7dd30caa 100644 --- a/pkg/stdlib/html/input.go +++ b/pkg/stdlib/html/input.go @@ -8,14 +8,12 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Types a value to an underlying input element. - * @param source (Document | Element) - Event target. - * @param valueOrSelector (String) - Selector or a value. - * @param value (String) - Target value. - * @param delay (Int, optional) - Waits delay milliseconds between keystrokes - * @returns (Boolean) - Returns true if an element was found. - */ +// Input types a value to an underlying input element. +// @param source (Document | Element) - Event target. +// @param valueOrSelector (String) - Selector or a value. +// @param value (String) - Target value. +// @param delay (Int, optional) - Waits delay milliseconds between keystrokes +// @returns (Boolean) - Returns true if an element was found. func Input(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 4) diff --git a/pkg/stdlib/html/navigate.go b/pkg/stdlib/html/navigate.go index a523a82b..0c8a5ca5 100644 --- a/pkg/stdlib/html/navigate.go +++ b/pkg/stdlib/html/navigate.go @@ -2,19 +2,18 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Navigates a document to a new resource. - * The operation blocks the execution until the page gets loaded. - * Which means there is no need in WAIT_NAVIGATION function. - * @param doc (Document) - Target document. - * @param url (String) - Target url to navigate. - * @param timeout (Int, optional) - Optional timeout. Default is 5000. - */ +// Navigate navigates a document to a new resource. +// The operation blocks the execution until the page gets loaded. +// Which means there is no need in WAIT_NAVIGATION function. +// @param doc (Document) - Target document. +// @param url (String) - Target url to navigate. +// @param timeout (Int, optional) - Optional timeout. Default is 5000. func Navigate(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/html/navigate_back.go b/pkg/stdlib/html/navigate_back.go index 919c6b70..736e6643 100644 --- a/pkg/stdlib/html/navigate_back.go +++ b/pkg/stdlib/html/navigate_back.go @@ -2,20 +2,19 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Navigates a document back within its navigation history. - * The operation blocks the execution until the page gets loaded. - * If the history is empty, the function returns FALSE. - * @param doc (Document) - Target document. - * @param entry (Int, optional) - Optional value indicating how many pages to skip. Default 1. - * @param timeout (Int, optional) - Optional timeout. Default is 5000. - * @returns (Boolean) - Returns TRUE if history exists and the operation succeeded, otherwise FALSE. - */ +// NavigateBack navigates a document back within its navigation history. +// The operation blocks the execution until the page gets loaded. +// If the history is empty, the function returns FALSE. +// @param doc (Document) - Target document. +// @param entry (Int, optional) - Optional value indicating how many pages to skip. Default 1. +// @param timeout (Int, optional) - Optional timeout. Default is 5000. +// @returns (Boolean) - Returns TRUE if history exists and the operation succeeded, otherwise FALSE. func NavigateBack(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 3) diff --git a/pkg/stdlib/html/navigate_forward.go b/pkg/stdlib/html/navigate_forward.go index 56a2e85f..51863909 100644 --- a/pkg/stdlib/html/navigate_forward.go +++ b/pkg/stdlib/html/navigate_forward.go @@ -2,20 +2,19 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Navigates a document forward within its navigation history. - * The operation blocks the execution until the page gets loaded. - * If the history is empty, the function returns FALSE. - * @param doc (Document) - Target document. - * @param entry (Int, optional) - Optional value indicating how many pages to skip. Default 1. - * @param timeout (Int, optional) - Optional timeout. Default is 5000. - * @returns (Boolean) - Returns TRUE if history exists and the operation succeeded, otherwise FALSE. - */ +// NavigateForward navigates a document forward within its navigation history. +// The operation blocks the execution until the page gets loaded. +// If the history is empty, the function returns FALSE. +// @param doc (Document) - Target document. +// @param entry (Int, optional) - Optional value indicating how many pages to skip. Default 1. +// @param timeout (Int, optional) - Optional timeout. Default is 5000. +// @returns (Boolean) - Returns TRUE if history exists and the operation succeeded, otherwise FALSE. func NavigateForward(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 3) diff --git a/pkg/stdlib/html/wait_class.go b/pkg/stdlib/html/wait_class.go index bd54d02f..699901a2 100644 --- a/pkg/stdlib/html/wait_class.go +++ b/pkg/stdlib/html/wait_class.go @@ -2,22 +2,21 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Waits for a class to appear on a given element. - * Stops the execution until the navigation ends or operation times out. - * @param docOrEl (HTMLDocument|HTMLElement) - Target document or element. - * @param selectorOrClass (String) - If document is passed, this param must represent an element selector. - * Otherwise target class. - * @param classOrTimeout (String|Int, optional) - If document is passed, this param must represent target class name. - * Otherwise timeout. - * @param timeout (Int, optional) - If document is passed, this param must represent timeout. - * Otherwise not passed. - */ +// WaitClass waits for a class to appear on a given element. +// Stops the execution until the navigation ends or operation times out. +// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element. +// @param selectorOrClass (String) - If document is passed, this param must represent an element selector. +// Otherwise target class. +// @param classOrTimeout (String|Int, optional) - If document is passed, this param must represent target class name. +// Otherwise timeout. +// @param timeout (Int, optional) - If document is passed, this param must represent timeout. +// Otherwise not passed. func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 4) diff --git a/pkg/stdlib/html/wait_class_all.go b/pkg/stdlib/html/wait_class_all.go index 30cf1b2a..be1f60ae 100644 --- a/pkg/stdlib/html/wait_class_all.go +++ b/pkg/stdlib/html/wait_class_all.go @@ -2,19 +2,18 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Waits for a class to appear on all matched elements. - * Stops the execution until the navigation ends or operation times out. - * @param doc (HTMLDocument) - Parent document. - * @param selector (String) - String of CSS selector. - * @param class (String) - String of target CSS class. - * @param timeout (Int, optional) - Optional timeout. - */ +// WaitClassAll waits for a class to appear on all matched elements. +// Stops the execution until the navigation ends or operation times out. +// @param doc (HTMLDocument) - Parent document. +// @param selector (String) - String of CSS selector. +// @param class (String) - String of target CSS class. +// @param timeout (Int, optional) - Optional timeout. func WaitClassAll(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 3, 4) diff --git a/pkg/stdlib/html/wait_element.go b/pkg/stdlib/html/wait_element.go index 9fb43f51..27270d71 100644 --- a/pkg/stdlib/html/wait_element.go +++ b/pkg/stdlib/html/wait_element.go @@ -2,18 +2,17 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Waits for element to appear in the DOM. - * Stops the execution until it finds an element or operation times out. - * @param doc (HTMLDocument) - Dynamic HTMLDocument. - * @param selector (String) - Target element's selector. - * @param timeout (Int, optional) - Optional timeout. Default 5000 ms. - */ +// WaitElement waits for element to appear in the DOM. +// Stops the execution until it finds an element or operation times out. +// @param doc (HTMLDocument) - Dynamic HTMLDocument. +// @param selector (String) - Target element's selector. +// @param timeout (Int, optional) - Optional timeout. Default 5000 ms. func WaitElement(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/html/wait_navigation.go b/pkg/stdlib/html/wait_navigation.go index 7c1756cc..30f6e99a 100644 --- a/pkg/stdlib/html/wait_navigation.go +++ b/pkg/stdlib/html/wait_navigation.go @@ -2,17 +2,16 @@ package html import ( "context" + "github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Waits for document to navigate to a new url. - * Stops the execution until the navigation ends or operation times out. - * @param doc (HTMLDocument) - Dynamic HTMLDocument. - * @param timeout (Int, optional) - Optional timeout. Default 5000 ms. - */ +// WaitNavigation waits for document to navigate to a new url. +// Stops the execution until the navigation ends or operation times out. +// @param doc (HTMLDocument) - Dynamic HTMLDocument. +// @param timeout (Int, optional) - Optional timeout. Default 5000 ms. func WaitNavigation(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) diff --git a/pkg/stdlib/objects/has.go b/pkg/stdlib/objects/has.go index 98c97f26..39c09f34 100644 --- a/pkg/stdlib/objects/has.go +++ b/pkg/stdlib/objects/has.go @@ -7,11 +7,9 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns the value stored by the given key. - * @params (String) - The key name string. - * @returns (Boolean) - True if the key exists else false. - */ +// Has returns the value stored by the given key. +// @params (String) - The key name string. +// @returns (Boolean) - True if the key exists else false. func Has(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/objects/keep.go b/pkg/stdlib/objects/keep.go index f5ec181b..45e3734c 100644 --- a/pkg/stdlib/objects/keep.go +++ b/pkg/stdlib/objects/keep.go @@ -7,12 +7,10 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a new object with only given keys. - * @params src (Object) - source object. - * @params keys (Array Of String OR Strings) - keys that need to be keeped. - * @returns (Object) - New Object with only given keys. - */ +// Keep returns a new object with only given keys. +// @params src (Object) - source object. +// @params keys (Array Of String OR Strings) - keys that need to be keeped. +// @returns (Object) - New Object with only given keys. func Keep(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, core.MaxArgs) diff --git a/pkg/stdlib/objects/keys.go b/pkg/stdlib/objects/keys.go index 5e1b8656..b304e6b9 100644 --- a/pkg/stdlib/objects/keys.go +++ b/pkg/stdlib/objects/keys.go @@ -8,12 +8,10 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns string array of object's keys - * @params obj (Object) - The object whose keys you want to extract - * @params sort (Boolean, optional) - If sort is true, then the returned keys will be sorted. - * @returns (Array of String) - Array that contains object keys. - */ +// Keys returns string array of object's keys +// @params obj (Object) - The object whose keys you want to extract +// @params sort (Boolean, optional) - If sort is true, then the returned keys will be sorted. +// @returns (Array of String) - Array that contains object keys. func Keys(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) if err != nil { diff --git a/pkg/stdlib/objects/merge.go b/pkg/stdlib/objects/merge.go index 5884cbf3..2dc8dca0 100644 --- a/pkg/stdlib/objects/merge.go +++ b/pkg/stdlib/objects/merge.go @@ -7,11 +7,9 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Merge the given objects into a single object. - * @params objs (Array Of Object OR Objects) - objects to merge. - * @returns (Object) - Object created by merging. - */ +// Merge merge the given objects into a single object. +// @params objs (Array Of Object OR Objects) - objects to merge. +// @returns (Object) - Object created by merging. func Merge(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, core.MaxArgs) diff --git a/pkg/stdlib/strings/case.go b/pkg/stdlib/strings/case.go index a5dc13cc..63a43d96 100644 --- a/pkg/stdlib/strings/case.go +++ b/pkg/stdlib/strings/case.go @@ -2,16 +2,15 @@ package strings import ( "context" + "strings" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "strings" ) -/* - * Converts strings to their lower-case counterparts. All other characters are returned unchanged. - * @param src (String) - The source string. - * @returns (String) - THis string in lower case. - */ +// Lower converts strings to their lower-case counterparts. All other characters are returned unchanged. +// @param src (String) - The source string. +// @returns (String) - THis string in lower case. func Lower(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) @@ -24,11 +23,9 @@ func Lower(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewString(text), nil } -/* - * Converts strings to their upper-case counterparts. All other characters are returned unchanged. - * @param src (String) - The source string. - * @returns (String) - THis string in upper case. - */ +// Upper converts strings to their upper-case counterparts. All other characters are returned unchanged. +// @param src (String) - The source string. +// @returns (String) - THis string in upper case. func Upper(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/strings/concat.go b/pkg/stdlib/strings/concat.go index 71782734..170708e2 100644 --- a/pkg/stdlib/strings/concat.go +++ b/pkg/stdlib/strings/concat.go @@ -2,15 +2,14 @@ package strings import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Concatenates one or more instances of Read, or an Array. - * @params src (String...|Array) - The source string / array. - * @returns String - */ +// Concat concatenates one or more instances of Read, or an Array. +// @params src (String...|Array) - The source string / array. +// @returns String func Concat(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, core.MaxArgs) @@ -41,12 +40,10 @@ func Concat(_ context.Context, args ...core.Value) (core.Value, error) { return res, nil } -/* - * Concatenates one or more instances of Read, or an Array with a given separator. - * @params separator (string) - The separator string. - * @params src (string...|array) - The source string / array. - * @returns string - */ +// ConcatWithSeparator concatenates one or more instances of Read, or an Array with a given separator. +// @params separator (string) - The separator string. +// @params src (string...|array) - The source string / array. +// @returns string func ConcatWithSeparator(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, core.MaxArgs) diff --git a/pkg/stdlib/strings/contains.go b/pkg/stdlib/strings/contains.go index 5e4b96ea..2e100fb0 100644 --- a/pkg/stdlib/strings/contains.go +++ b/pkg/stdlib/strings/contains.go @@ -2,18 +2,17 @@ package strings import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a value indicating whether a specified substring occurs within a string. - * @param src (String) - The source string. - * @param search (String) - The string to seek. - * @param returnIndex (Boolean) - Values which indicates whether to return the character position of the match is returned instead of a boolean. - * The default is false. - * @returns (Boolean|Int) - */ +// Contains returns a value indicating whether a specified substring occurs within a string. +// @param src (String) - The source string. +// @param search (String) - The string to seek. +// @param returnIndex (Boolean) - Values which indicates whether to return the character position of the match is returned instead of a boolean. +// The default is false. +// @returns (Boolean|Int) func Contains(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/strings/encode.go b/pkg/stdlib/strings/encode.go index b54e93c6..ddac8d3f 100644 --- a/pkg/stdlib/strings/encode.go +++ b/pkg/stdlib/strings/encode.go @@ -6,16 +6,15 @@ import ( "crypto/sha1" "crypto/sha512" "encoding/base64" + "net/url" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "net/url" ) -/* - * Returns the encoded String of uri. - * @param (String) - Uri to encode. - * @returns String - Encoded string. - */ +// EncodeURIComponent returns the encoded String of uri. +// @param (String) - Uri to encode. +// @returns String - Encoded string. func EncodeURIComponent(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) @@ -28,11 +27,9 @@ func EncodeURIComponent(_ context.Context, args ...core.Value) (core.Value, erro return values.NewString(str), nil } -/* - * Calculates the MD5 checksum for text and return it in a hexadecimal string representation. - * @param text (String) - The string to do calculations against to. - * @return (String) - MD5 checksum as hex string. - */ +// Md5 calculates the MD5 checksum for text and return it in a hexadecimal string representation. +// @param text (String) - The string to do calculations against to. +// @return (String) - MD5 checksum as hex string. func Md5(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) @@ -46,11 +43,9 @@ func Md5(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewString(string(res[:])), nil } -/* - * Calculates the SHA1 checksum for text and returns it in a hexadecimal string representation. - * @param text (String) - The string to do calculations against to. - * @return (String) - Sha1 checksum as hex string. - */ +// Sha1 calculates the SHA1 checksum for text and returns it in a hexadecimal string representation. +// @param text (String) - The string to do calculations against to. +// @return (String) - Sha1 checksum as hex string. func Sha1(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) @@ -64,11 +59,9 @@ func Sha1(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewString(string(res[:])), nil } -/* - * Calculates the SHA512 checksum for text and returns it in a hexadecimal string representation. - * @param text (String) - The string to do calculations against to. - * @return (String) - SHA512 checksum as hex string. - */ +// Sha512 calculates the SHA512 checksum for text and returns it in a hexadecimal string representation. +// @param text (String) - The string to do calculations against to. +// @return (String) - SHA512 checksum as hex string. func Sha512(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) @@ -82,11 +75,9 @@ func Sha512(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewString(string(res[:])), nil } -/* - * Returns the base64 representation of value. - * @param value (string) - The string to encode. - * @returns toBase64String (String) - A base64 representation of the string. - */ +// ToBase64 returns the base64 representation of value. +// @param value (string) - The string to encode. +// @returns toBase64String (String) - A base64 representation of the string. func ToBase64(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/strings/find.go b/pkg/stdlib/strings/find.go index 8b56fb92..aa495dd3 100644 --- a/pkg/stdlib/strings/find.go +++ b/pkg/stdlib/strings/find.go @@ -2,20 +2,19 @@ package strings import ( "context" + "strings" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "strings" ) -/* - * Returns the position of the first occurrence of the string search inside the string text. Positions start at 0. - * @param src (String) - The source string. - * @param search (String) - The string to seek. - * @param start (Int, optional) - Limit the search to a subset of the text, beginning at start. - * @param end (Int, optional) - Limit the search to a subset of the text, ending at end - * @returns (Int) - The character position of the match. - * If search is not contained in text, -1 is returned. If search is empty, start is returned. - */ +// FindFirst returns the position of the first occurrence of the string search inside the string text. Positions start at 0. +// @param src (String) - The source string. +// @param search (String) - The string to seek. +// @param start (Int, optional) - Limit the search to a subset of the text, beginning at start. +// @param end (Int, optional) - Limit the search to a subset of the text, ending at end +// @returns (Int) - The character position of the match. +// If search is not contained in text, -1 is returned. If search is empty, start is returned. func FindFirst(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 4) @@ -56,15 +55,13 @@ func FindFirst(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewInt(found), nil } -/* - * Returns the position of the last occurrence of the string search inside the string text. Positions start at 0. - * @param src (String) - The source string. - * @param search (String) - The string to seek. - * @param start (Int, optional) - Limit the search to a subset of the text, beginning at start. - * @param end (Int, optional) - Limit the search to a subset of the text, ending at end - * @returns (Int) - The character position of the match. - * If search is not contained in text, -1 is returned. If search is empty, start is returned. - */ +// FindLast returns the position of the last occurrence of the string search inside the string text. Positions start at 0. +// @param src (String) - The source string. +// @param search (String) - The string to seek. +// @param start (Int, optional) - Limit the search to a subset of the text, beginning at start. +// @param end (Int, optional) - Limit the search to a subset of the text, ending at end +// @returns (Int) - The character position of the match. +// If search is not contained in text, -1 is returned. If search is empty, start is returned. func FindLast(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 4) diff --git a/pkg/stdlib/strings/json.go b/pkg/stdlib/strings/json.go index 512f82a4..da60a04a 100644 --- a/pkg/stdlib/strings/json.go +++ b/pkg/stdlib/strings/json.go @@ -3,15 +3,14 @@ package strings import ( "context" "encoding/json" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a FQL value described by the JSON-encoded input string. - * @params text (String) - The string to parse as JSON. - * @returns FQL value (Read) - */ +// JSONParse returns a FQL value described by the JSON-encoded input string. +// @params text (String) - The string to parse as JSON. +// @returns FQL value (Read) func JSONParse(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) @@ -30,11 +29,9 @@ func JSONParse(_ context.Context, args ...core.Value) (core.Value, error) { return values.Parse(val), nil } -/* - * Returns a JSON string representation of the input value. - * @params value (Read) - The input value to serialize. - * @returns json (String) - */ +// JSONStringify returns a JSON string representation of the input value. +// @params value (Read) - The input value to serialize. +// @returns json (String) func JSONStringify(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/strings/like.go b/pkg/stdlib/strings/like.go index b0463196..53c16808 100644 --- a/pkg/stdlib/strings/like.go +++ b/pkg/stdlib/strings/like.go @@ -8,13 +8,11 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether the pattern search is contained in the string text, using wildcard matching. - * @param text (String) - The string to search in. - * @param search (String) - A search pattern that can contain the wildcard characters. - * @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. - * @return (Boolean) - Returns true if the pattern is contained in text, and false otherwise. - */ +// Like checks whether the pattern search is contained in the string text, using wildcard matching. +// @param text (String) - The string to search in. +// @param search (String) - A search pattern that can contain the wildcard characters. +// @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. +// @return (Boolean) - Returns true if the pattern is contained in text, and false otherwise. func Like(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/strings/random.go b/pkg/stdlib/strings/random.go index 783e1621..9c6bf9f3 100644 --- a/pkg/stdlib/strings/random.go +++ b/pkg/stdlib/strings/random.go @@ -2,10 +2,11 @@ package strings import ( "context" - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" "math/rand" "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" ) const ( @@ -15,11 +16,9 @@ const ( letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits ) -/* - * Generates a pseudo-random token string with the specified length. The algorithm for token generation should be treated as opaque. - * @param length (Int) - The desired string length for the token. It must be greater than 0 and at most 65536. - * @return (String) - A generated token consisting of lowercase letters, uppercase letters and numbers. - */ +// RandomToken generates a pseudo-random token string with the specified length. The algorithm for token generation should be treated as opaque. +// @param length (Int) - The desired string length for the token. It must be greater than 0 and at most 65536. +// @return (String) - A generated token consisting of lowercase letters, uppercase letters and numbers. func RandomToken(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/strings/regex.go b/pkg/stdlib/strings/regex.go index b785e314..e161b7b2 100644 --- a/pkg/stdlib/strings/regex.go +++ b/pkg/stdlib/strings/regex.go @@ -2,18 +2,17 @@ package strings import ( "context" + "regexp" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "regexp" ) -/* - * Returns the matches in the given string text, using the regex. - * @param text (String) - The string to search in. - * @param regex (String) - A regular expression to use for matching the text. - * @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. - * @return (Array) - An array of strings containing the matches. - */ +// RegexMatch returns the matches in the given string text, using the regex. +// @param text (String) - The string to search in. +// @param regex (String) - A regular expression to use for matching the text. +// @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. +// @return (Array) - An array of strings containing the matches. func RegexMatch(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) @@ -50,14 +49,12 @@ func RegexMatch(_ context.Context, args ...core.Value) (core.Value, error) { return res, nil } -/* - * Splits the given string text into a list of strings, using the separator. - * @param text (String) - The string to split. - * @param regex (String) - A regular expression to use for splitting the text. - * @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. - * @param limit (Int) - Limit the number of split values in the result. If no limit is given, the number of splits returned is not bounded. - * @return (Array) - An array of strings splited by teh expression. - */ +// RegexSplit splits the given string text into a list of strings, using the separator. +// @param text (String) - The string to split. +// @param regex (String) - A regular expression to use for splitting the text. +// @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. +// @param limit (Int) - Limit the number of split values in the result. If no limit is given, the number of splits returned is not bounded. +// @return (Array) - An array of strings splited by teh expression. func RegexSplit(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 4) @@ -95,13 +92,11 @@ func RegexSplit(_ context.Context, args ...core.Value) (core.Value, error) { return res, nil } -/* - * Test wether the regexp has at least one match in the given text. - * @param text (String) - The string to split. - * @param regex (String) - A regular expression to use for splitting the text. - * @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. - * @return (Boolean) - Returns true if the pattern is contained in text, and false otherwise. - */ +// RegexTest test wether the regexp has at least one match in the given text. +// @param text (String) - The string to split. +// @param regex (String) - A regular expression to use for splitting the text. +// @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. +// @return (Boolean) - Returns true if the pattern is contained in text, and false otherwise. func RegexTest(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) @@ -129,14 +124,12 @@ func RegexTest(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewBoolean(matches), nil } -/* - * Replace every substring matched with the regexp with a given string. - * @param text (String) - The string to split. - * @param regex (String) - A regular expression search pattern. - * @param replacement (String) - The string to replace the search pattern with - * @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. - * @return (String) - Returns the string text with the search regex pattern replaced with the replacement string wherever the pattern exists in text - */ +// RegexReplace replace every substring matched with the regexp with a given string. +// @param text (String) - The string to split. +// @param regex (String) - A regular expression search pattern. +// @param replacement (String) - The string to replace the search pattern with +// @param caseInsensitive (Boolean) - If set to true, the matching will be case-insensitive. The default is false. +// @return (String) - Returns the string text with the search regex pattern replaced with the replacement string wherever the pattern exists in text func RegexReplace(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 3, 4) diff --git a/pkg/stdlib/strings/reverse.go b/pkg/stdlib/strings/reverse.go index 646a66c4..78509bc7 100644 --- a/pkg/stdlib/strings/reverse.go +++ b/pkg/stdlib/strings/reverse.go @@ -2,15 +2,14 @@ package strings import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns the reverse of the string value. - * @param text (String) - The string to revers - * @returns (String) - Returns a reversed version of the string. - */ +// Reverse returns the reverse of the string value. +// @param text (String) - The string to revers +// @returns (String) - Returns a reversed version of the string. func Reverse(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/strings/split.go b/pkg/stdlib/strings/split.go index 4e85f862..bfedd270 100644 --- a/pkg/stdlib/strings/split.go +++ b/pkg/stdlib/strings/split.go @@ -2,18 +2,17 @@ package strings import ( "context" + "strings" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "strings" ) -/* - * Splits the given string value into a list of strings, using the separator. - * @params text (String) - The string to split. - * @params separator (String) - The sperator. - * @params limit (Int) - Limit the number of split values in the result. If no limit is given, the number of splits returned is not bounded. - * @returns strings (Array) - Array of strings. - */ +// Split splits the given string value into a list of strings, using the separator. +// @params text (String) - The string to split. +// @params separator (String) - The sperator. +// @params limit (Int) - Limit the number of split values in the result. If no limit is given, the number of splits returned is not bounded. +// @returns strings (Array) - Array of strings. func Split(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) diff --git a/pkg/stdlib/strings/substitute.go b/pkg/stdlib/strings/substitute.go index d5a3f1c3..aa2b0a93 100644 --- a/pkg/stdlib/strings/substitute.go +++ b/pkg/stdlib/strings/substitute.go @@ -2,19 +2,18 @@ package strings import ( "context" + "strings" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "strings" ) -/* - * Replaces search values in the string value. - * @params text (String) - The string to modify - * @params search (String) - The string representing a search pattern - * @params replace (String) - The string representing a replace value - * @param limit (Int) - The cap the number of replacements to this value. - * @return (String) - Returns a string with replace substring. - */ +// Substitute replaces search values in the string value. +// @params text (String) - The string to modify +// @params search (String) - The string representing a search pattern +// @params replace (String) - The string representing a replace value +// @param limit (Int) - The cap the number of replacements to this value. +// @return (String) - Returns a string with replace substring. func Substitute(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 4) diff --git a/pkg/stdlib/strings/substr.go b/pkg/stdlib/strings/substr.go index 6e4af170..fc7d252b 100644 --- a/pkg/stdlib/strings/substr.go +++ b/pkg/stdlib/strings/substr.go @@ -2,17 +2,16 @@ package strings import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns a substring of value. - * @params value (String) - The source string. - * @param offset (Int) - Start at offset, offsets start at position 0. - * @param length (Int, optional) - At most length characters, omit to get the substring from offset to the end of the string. Optional. - * @returns substring (String) - A substring of value. - */ +// Substring returns a substring of value. +// @params value (String) - The source string. +// @param offset (Int) - Start at offset, offsets start at position 0. +// @param length (Int, optional) - At most length characters, omit to get the substring from offset to the end of the string. Optional. +// @returns substring (String) - A substring of value. func Substring(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 3) @@ -55,12 +54,10 @@ func Substring(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewStringFromRunes(substr), nil } -/* - * Returns the leftmost characters of the string value by index. - * @param src (String) - The source string. - * @params length (Int) - The amount of characters to return. - * @returns substr (String) - */ +// Left returns the leftmost characters of the string value by index. +// @param src (String) - The source string. +// @params length (Int) - The amount of characters to return. +// @returns substr (String) func Left(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) @@ -84,12 +81,10 @@ func Left(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewStringFromRunes(runes[0:pos]), nil } -/* - * Returns the rightmost characters of the string value. - * @param src (String) - The source string. - * @params length (Int) - The amount of characters to return. - * @returns substr (String) - */ +// Right returns the rightmost characters of the string value. +// @param src (String) - The source string. +// @params length (Int) - The amount of characters to return. +// @returns substr (String) func Right(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 2, 2) diff --git a/pkg/stdlib/strings/trim.go b/pkg/stdlib/strings/trim.go index 81dc2821..c9a15be7 100644 --- a/pkg/stdlib/strings/trim.go +++ b/pkg/stdlib/strings/trim.go @@ -2,17 +2,16 @@ package strings import ( "context" + "strings" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "strings" ) -/* - * Returns the string value with whitespace stripped from the start and/or end. - * @param value (String) - The string. - * @param chars (String) - Overrides the characters that should be removed from the string. It defaults to \r\n \t. - * @returns (String) - The string without chars on both sides. - */ +// Trim returns the string value with whitespace stripped from the start and/or end. +// @param value (String) - The string. +// @param chars (String) - Overrides the characters that should be removed from the string. It defaults to \r\n \t. +// @returns (String) - The string without chars on both sides. func Trim(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) @@ -29,12 +28,10 @@ func Trim(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewString(strings.TrimSpace(text)), nil } -/* - * Returns the string value with whitespace stripped from the start only. - * @param value (String) - The string. - * @param chars (String) - Overrides the characters that should be removed from the string. It defaults to \r\n \t. - * @returns (String) - The string without chars at the left-hand side. - */ +// LTrim returns the string value with whitespace stripped from the start only. +// @param value (String) - The string. +// @param chars (String) - Overrides the characters that should be removed from the string. It defaults to \r\n \t. +// @returns (String) - The string without chars at the left-hand side. func LTrim(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) @@ -52,12 +49,10 @@ func LTrim(_ context.Context, args ...core.Value) (core.Value, error) { return values.NewString(strings.TrimLeft(text, chars)), nil } -/* - * Returns the string value with whitespace stripped from the end only. - * @param value (String) - The string. - * @param chars (String) - Overrides the characters that should be removed from the string. It defaults to \r\n \t. - * @returns (String) - The string without chars at the right-hand side. - */ +// RTrim returns the string value with whitespace stripped from the end only. +// @param value (String) - The string. +// @param chars (String) - Overrides the characters that should be removed from the string. It defaults to \r\n \t. +// @returns (String) - The string without chars at the right-hand side. func RTrim(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 2) diff --git a/pkg/stdlib/types/is_array.go b/pkg/stdlib/types/is_array.go index 7248efda..66ea3a28 100644 --- a/pkg/stdlib/types/is_array.go +++ b/pkg/stdlib/types/is_array.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is an array value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is array, otherwise false. - */ +// IsArray checks whether value is an array value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is array, otherwise false. func IsArray(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_binary.go b/pkg/stdlib/types/is_binary.go index 9a2c044d..fcc0b11d 100644 --- a/pkg/stdlib/types/is_binary.go +++ b/pkg/stdlib/types/is_binary.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is a binary value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is binary, otherwise false. - */ +// IsBinary checks whether value is a binary value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is binary, otherwise false. func IsBinary(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_boolean.go b/pkg/stdlib/types/is_boolean.go index 75c6c8b5..81f2e87e 100644 --- a/pkg/stdlib/types/is_boolean.go +++ b/pkg/stdlib/types/is_boolean.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is a boolean value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is boolean, otherwise false. - */ +// IsBool checks whether value is a boolean value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is boolean, otherwise false. func IsBool(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_date_time.go b/pkg/stdlib/types/is_date_time.go index efc87e6e..23e45330 100644 --- a/pkg/stdlib/types/is_date_time.go +++ b/pkg/stdlib/types/is_date_time.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is a date time value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is date time, otherwise false. - */ +// IsDateTime checks whether value is a date time value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is date time, otherwise false. func IsDateTime(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_float.go b/pkg/stdlib/types/is_float.go index 8d834907..08c5355c 100644 --- a/pkg/stdlib/types/is_float.go +++ b/pkg/stdlib/types/is_float.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is a float value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is float, otherwise false. - */ +// IsFloat checks whether value is a float value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is float, otherwise false. func IsFloat(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_html_document.go b/pkg/stdlib/types/is_html_document.go index b8a91ff1..6b6f764a 100644 --- a/pkg/stdlib/types/is_html_document.go +++ b/pkg/stdlib/types/is_html_document.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is a HTMLDocument value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is HTMLDocument, otherwise false. - */ +// IsHTMLDocument checks whether value is a HTMLDocument value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is HTMLDocument, otherwise false. func IsHTMLDocument(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_html_element.go b/pkg/stdlib/types/is_html_element.go index 7a182a15..d2e6e82c 100644 --- a/pkg/stdlib/types/is_html_element.go +++ b/pkg/stdlib/types/is_html_element.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is a HTMLElement value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is HTMLElement, otherwise false. - */ +// IsHTMLElement checks whether value is a HTMLElement value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is HTMLElement, otherwise false. func IsHTMLElement(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_int.go b/pkg/stdlib/types/is_int.go index f55da825..7b9420cb 100644 --- a/pkg/stdlib/types/is_int.go +++ b/pkg/stdlib/types/is_int.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is a int value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is int, otherwise false. - */ +// IsInt checks whether value is a int value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is int, otherwise false. func IsInt(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_none.go b/pkg/stdlib/types/is_none.go index 7a80fe61..fa04bf97 100644 --- a/pkg/stdlib/types/is_none.go +++ b/pkg/stdlib/types/is_none.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is a none value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is none, otherwise false. - */ +// IsNone checks whether value is a none value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is none, otherwise false. func IsNone(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_object.go b/pkg/stdlib/types/is_object.go index 8d8d84a9..de13cc8e 100644 --- a/pkg/stdlib/types/is_object.go +++ b/pkg/stdlib/types/is_object.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is an object value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is object, otherwise false. - */ +// IsObject checks whether value is an object value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is object, otherwise false. func IsObject(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/is_string.go b/pkg/stdlib/types/is_string.go index 11b767d4..b5be6091 100644 --- a/pkg/stdlib/types/is_string.go +++ b/pkg/stdlib/types/is_string.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Checks whether value is a string value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns true if value is string, otherwise false. - */ +// IsString checks whether value is a string value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns true if value is string, otherwise false. func IsString(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/to_array.go b/pkg/stdlib/types/to_array.go index ca91dc79..81c37ff4 100644 --- a/pkg/stdlib/types/to_array.go +++ b/pkg/stdlib/types/to_array.go @@ -2,20 +2,19 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/collections" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Takes an input value of any type and convert it into an array value. - * @param (Value) - Input value of arbitrary type. - * @returns (Array) - * None is converted to an empty array - * Boolean values, numbers and strings are converted to an array containing the original value as its single element - * Arrays keep their original value - * Objects / HTML nodes are converted to an array containing their attribute values as array elements. - */ +// ToArray takes an input value of any type and convert it into an array value. +// @param (Value) - Input value of arbitrary type. +// @returns (Array) +// None is converted to an empty array +// Boolean values, numbers and strings are converted to an array containing the original value as its single element +// Arrays keep their original value +// Objects / HTML nodes are converted to an array containing their attribute values as array elements. func ToArray(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/to_boolean.go b/pkg/stdlib/types/to_boolean.go index 6dbb6289..e706eecc 100644 --- a/pkg/stdlib/types/to_boolean.go +++ b/pkg/stdlib/types/to_boolean.go @@ -2,21 +2,20 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Takes an input value of any type and converts it into the appropriate boolean value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - - * None is converted to false - * Numbers are converted to true, except for 0, which is converted to false - * Strings are converted to true if they are non-empty, and to false otherwise - * Dates are converted to true if they are not zero, and to false otherwise - * Arrays are always converted to true (even if empty) - * Objects / HtmlNodes / Binary are always converted to true - */ +// ToBool takes an input value of any type and converts it into the appropriate boolean value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - +// None is converted to false +// Numbers are converted to true, except for 0, which is converted to false +// Strings are converted to true if they are non-empty, and to false otherwise +// Dates are converted to true if they are not zero, and to false otherwise +// Arrays are always converted to true (even if empty) +// Objects / HtmlNodes / Binary are always converted to true func ToBool(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/to_date_time.go b/pkg/stdlib/types/to_date_time.go index 9554c3b5..fd31489d 100644 --- a/pkg/stdlib/types/to_date_time.go +++ b/pkg/stdlib/types/to_date_time.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Takes an input value of any type and converts it into the appropriate date time value. - * @param value (Value) - Input value of arbitrary type. - * @returns (DateTime) - Parsed date time. - */ +// ToDateTime takes an input value of any type and converts it into the appropriate date time value. +// @param value (Value) - Input value of arbitrary type. +// @returns (DateTime) - Parsed date time. func ToDateTime(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/to_float.go b/pkg/stdlib/types/to_float.go index bdb275de..9f1b72b6 100644 --- a/pkg/stdlib/types/to_float.go +++ b/pkg/stdlib/types/to_float.go @@ -2,24 +2,23 @@ package types import ( "context" + "strconv" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "strconv" ) -/* - * Takes an input value of any type and convert it into a float value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Float) - - * None and false are converted to the value 0 - * true is converted to 1 - * Numbers keep their original value - * Strings are converted to their numeric equivalent if the string contains a valid representation of a number. - * String values that do not contain any valid representation of a number will be converted to the number 0. - * An empty array is converted to 0, an array with one member is converted into the result of TO_NUMBER() for its sole member. - * An array with two or more members is converted to the number 0. - * An object / HTML node is converted to the number 0. - */ +// ToFloat takes an input value of any type and convert it into a float value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Float) - +// None and false are converted to the value 0 +// true is converted to 1 +// Numbers keep their original value +// Strings are converted to their numeric equivalent if the string contains a valid representation of a number. +// String values that do not contain any valid representation of a number will be converted to the number 0. +// An empty array is converted to 0, an array with one member is converted into the result of TO_NUMBER() for its sole member. +// An array with two or more members is converted to the number 0. +// An object / HTML node is converted to the number 0. func ToFloat(ctx context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/to_int.go b/pkg/stdlib/types/to_int.go index 75667dfb..e0857fde 100644 --- a/pkg/stdlib/types/to_int.go +++ b/pkg/stdlib/types/to_int.go @@ -2,24 +2,23 @@ package types import ( "context" + "strconv" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "strconv" ) -/* - * Takes an input value of any type and convert it into an integer value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Int) - - * None and false are converted to the value 0 - * true is converted to 1 - * Numbers keep their original value - * Strings are converted to their numeric equivalent if the string contains a valid representation of a number. - * String values that do not contain any valid representation of a number will be converted to the number 0. - * An empty array is converted to 0, an array with one member is converted into the result of TO_NUMBER() for its sole member. - * An array with two or more members is converted to the number 0. - * An object / HTML node is converted to the number 0. - */ +// ToInt takes an input value of any type and convert it into an integer value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Int) - +// None and false are converted to the value 0 +// true is converted to 1 +// Numbers keep their original value +// Strings are converted to their numeric equivalent if the string contains a valid representation of a number. +// String values that do not contain any valid representation of a number will be converted to the number 0. +// An empty array is converted to 0, an array with one member is converted into the result of TO_NUMBER() for its sole member. +// An array with two or more members is converted to the number 0. +// An object / HTML node is converted to the number 0. func ToInt(ctx context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/to_string.go b/pkg/stdlib/types/to_string.go index eba82095..9d91acd8 100644 --- a/pkg/stdlib/types/to_string.go +++ b/pkg/stdlib/types/to_string.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Takes an input value of any type and convert it into a string value. - * @param value (Value) - Input value of arbitrary type. - * @return (String) - String representation of a given value. - */ +// ToString takes an input value of any type and convert it into a string value. +// @param value (Value) - Input value of arbitrary type. +// @return (String) - String representation of a given value. func ToString(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/types/type_name.go b/pkg/stdlib/types/type_name.go index 4b82a001..40aae45a 100644 --- a/pkg/stdlib/types/type_name.go +++ b/pkg/stdlib/types/type_name.go @@ -2,15 +2,14 @@ package types import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Returns the data type name of value. - * @param value (Value) - Input value of arbitrary type. - * @returns (Boolean) - Returns string representation of a type. - */ +// TypeName returns the data type name of value. +// @param value (Value) - Input value of arbitrary type. +// @returns (Boolean) - Returns string representation of a type. func TypeName(_ context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, 1) diff --git a/pkg/stdlib/utils/log.go b/pkg/stdlib/utils/log.go index e34cb131..8b152bc6 100644 --- a/pkg/stdlib/utils/log.go +++ b/pkg/stdlib/utils/log.go @@ -2,14 +2,13 @@ package utils import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/logging" "github.com/MontFerret/ferret/pkg/runtime/values" ) -/* - * Writes messages into the system log. - */ +// Log writes messages into the system log. func Log(ctx context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, core.MaxArgs) diff --git a/pkg/stdlib/utils/wait.go b/pkg/stdlib/utils/wait.go index e38a81a6..f007516e 100644 --- a/pkg/stdlib/utils/wait.go +++ b/pkg/stdlib/utils/wait.go @@ -2,15 +2,14 @@ package utils import ( "context" + "time" + "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "time" ) -/* - * Pauses the execution for a given period. - * @param timeout (Int) - Integer value indication for how long to pause. - */ +// Wait pauses the execution for a given period. +// @param timeout (Int) - Integer value indication for how long to pause. func Wait(_ context.Context, inputs ...core.Value) (core.Value, error) { err := core.ValidateArgs(inputs, 1, 1) From 80ac30e5e210ae8ec9478c475150a2e350f82889 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 14 Oct 2018 21:19:11 +0300 Subject: [PATCH 044/115] fix bug when result of the KEEP function was dependent on source object --- pkg/stdlib/objects/keep.go | 3 +++ pkg/stdlib/objects/keep_test.go | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/pkg/stdlib/objects/keep.go b/pkg/stdlib/objects/keep.go index 45e3734c..b15a05cd 100644 --- a/pkg/stdlib/objects/keep.go +++ b/pkg/stdlib/objects/keep.go @@ -46,6 +46,9 @@ func Keep(_ context.Context, args ...core.Value) (core.Value, error) { for idx := values.NewInt(0); idx < keys.Length(); idx++ { key = keys.Get(idx).(values.String) if val, exists = obj.Get(key); exists { + if values.IsCloneable(val) { + val = val.(core.Cloneable).Clone() + } resultObj.Set(key, val) } } diff --git a/pkg/stdlib/objects/keep_test.go b/pkg/stdlib/objects/keep_test.go index d3718643..03ea8e88 100644 --- a/pkg/stdlib/objects/keep_test.go +++ b/pkg/stdlib/objects/keep_test.go @@ -45,6 +45,24 @@ func TestKeep(t *testing.T) { So(err, ShouldBeError) So(obj, ShouldEqual, values.None) }) + + Convey("Result object is independent of the source object", t, func() { + arr := values.NewArrayWith(values.Int(0)) + obj := values.NewObjectWith( + values.NewObjectProperty("a", arr), + ) + resultObj := values.NewObjectWith( + values.NewObjectProperty("a", values.NewArrayWith(values.Int(0))), + ) + + afterKeep, err := objects.Keep(context.Background(), obj, values.NewString("a")) + + So(err, ShouldBeNil) + + arr.Push(values.NewInt(1)) + + So(afterKeep.Compare(resultObj), ShouldEqual, 0) + }) } func TestKeepStrings(t *testing.T) { From ae7daad75345100fbc176b42ee6ce5984fa5316d Mon Sep 17 00:00:00 2001 From: = Date: Sun, 14 Oct 2018 21:26:30 +0300 Subject: [PATCH 045/115] some more changes in KEEP function --- pkg/stdlib/objects/keep.go | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/pkg/stdlib/objects/keep.go b/pkg/stdlib/objects/keep.go index b15a05cd..88f6e8ca 100644 --- a/pkg/stdlib/objects/keep.go +++ b/pkg/stdlib/objects/keep.go @@ -30,7 +30,7 @@ func Keep(_ context.Context, args ...core.Value) (core.Value, error) { keys = args[1].(*values.Array) } - err = validateArrayOfStrings(keys) + err = validateArrayOf(core.StringType, keys) if err != nil { return values.None, err @@ -43,25 +43,18 @@ func Keep(_ context.Context, args ...core.Value) (core.Value, error) { var val core.Value var exists values.Boolean - for idx := values.NewInt(0); idx < keys.Length(); idx++ { - key = keys.Get(idx).(values.String) + keys.ForEach(func(keyVal core.Value, idx int) bool { + key = keyVal.(values.String) + if val, exists = obj.Get(key); exists { if values.IsCloneable(val) { val = val.(core.Cloneable).Clone() } resultObj.Set(key, val) } - } - return resultObj, nil -} + return true + }) -func validateArrayOfStrings(arr *values.Array) (err error) { - for idx := values.NewInt(0); idx < arr.Length(); idx++ { - err = core.ValidateType(arr.Get(idx), core.StringType) - if err != nil { - break - } - } - return + return resultObj, nil } From 90bce769f1cc1a2d057c6e5c1df7177de6277d73 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 15 Oct 2018 03:08:37 +0300 Subject: [PATCH 046/115] init VALUES function --- pkg/stdlib/objects/lib.go | 11 +- pkg/stdlib/objects/values.go | 14 +++ pkg/stdlib/objects/values_test.go | 200 ++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 pkg/stdlib/objects/values.go create mode 100644 pkg/stdlib/objects/values_test.go diff --git a/pkg/stdlib/objects/lib.go b/pkg/stdlib/objects/lib.go index b03f27e9..47b6c430 100644 --- a/pkg/stdlib/objects/lib.go +++ b/pkg/stdlib/objects/lib.go @@ -4,10 +4,11 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "HAS": Has, - "KEYS": Keys, - "KEEP": Keep, - "MERGE": Merge, - "ZIP": Zip, + "HAS": Has, + "KEYS": Keys, + "KEEP": Keep, + "MERGE": Merge, + "ZIP": Zip, + "VALUES": Values, } } diff --git a/pkg/stdlib/objects/values.go b/pkg/stdlib/objects/values.go new file mode 100644 index 00000000..c1fd62ea --- /dev/null +++ b/pkg/stdlib/objects/values.go @@ -0,0 +1,14 @@ +package objects + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +// Values return the attribute values of the object as an array. +// @params obj (Object) - an object. +// @returns (Array of Value) - the values of document returned in any order. +func Values(_ context.Context, args ...core.Value) (core.Value, error) { + return nil, nil +} diff --git a/pkg/stdlib/objects/values_test.go b/pkg/stdlib/objects/values_test.go new file mode 100644 index 00000000..7a17023f --- /dev/null +++ b/pkg/stdlib/objects/values_test.go @@ -0,0 +1,200 @@ +package objects_test + +import ( + "context" + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/objects" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestValues(t *testing.T) { + Convey("Invalid arguments", t, func() { + Convey("When there is no arguments", func() { + actual, err := objects.Values(context.Background()) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When 2 arguments", func() { + obj := values.NewObjectWith( + values.NewObjectProperty("k1", values.NewInt(0)), + values.NewObjectProperty("k2", values.NewInt(1)), + ) + + actual, err := objects.Values(context.Background(), obj, obj) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + + actual, err = objects.Values(context.Background(), obj, values.NewInt(0)) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When there is not object argument", func() { + actual, err := objects.Values(context.Background(), values.NewInt(0)) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + }) + + Convey("When simple type attributes (same type)", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("k1", values.NewInt(0)), + values.NewObjectProperty("k2", values.NewInt(1)), + ) + expected := values.NewArrayWith( + values.NewInt(0), values.NewInt(1), + ).Sort() + + actual, err := objects.Values(context.Background(), obj) + actualSorted := actual.(*values.Array).Sort() + + So(err, ShouldBeNil) + So(actualSorted.Compare(expected), ShouldEqual, 0) + }) + + Convey("When simple type attributes (different types)", t, func() { + obj := values.NewObjectWith( + values.NewObjectProperty("k1", values.NewInt(0)), + values.NewObjectProperty("k2", values.NewString("v2")), + ) + expected := values.NewArrayWith( + values.NewInt(0), values.NewString("v2"), + ).Sort() + + actual, err := objects.Values(context.Background(), obj) + actualSorted := actual.(*values.Array).Sort() + + So(err, ShouldBeNil) + So(actualSorted.Compare(expected), ShouldEqual, 0) + }) + + Convey("When complex type attributes (array)", t, func() { + arr1 := values.NewArrayWith( + values.NewInt(0), values.NewInt(1), + ) + arr2 := values.NewArrayWith( + values.NewInt(2), values.NewInt(3), + ) + obj := values.NewObjectWith( + values.NewObjectProperty("k1", arr1), + values.NewObjectProperty("k2", arr2), + ) + expected := values.NewArrayWith(arr1, arr2).Sort() + + actual, err := objects.Values(context.Background(), obj) + actualSorted := actual.(*values.Array).Sort() + + So(err, ShouldBeNil) + So(actualSorted.Compare(expected), ShouldEqual, 0) + }) + + Convey("When complex type attributes (object)", t, func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("int0", values.NewInt(0)), + ) + obj2 := values.NewObjectWith( + values.NewObjectProperty("int1", values.NewInt(1)), + ) + obj := values.NewObjectWith( + values.NewObjectProperty("k1", obj1), + values.NewObjectProperty("k2", obj2), + ) + expected := values.NewArrayWith(obj1, obj2).Sort() + + actual, err := objects.Values(context.Background(), obj) + actualSorted := actual.(*values.Array).Sort() + + So(err, ShouldBeNil) + So(actualSorted.Compare(expected), ShouldEqual, 0) + }) + + Convey("When complex type attributes (object and array)", t, func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("k1", values.NewInt(0)), + ) + arr1 := values.NewArrayWith( + values.NewInt(0), values.NewInt(1), + ) + obj := values.NewObjectWith( + values.NewObjectProperty("obj", obj1), + values.NewObjectProperty("arr", arr1), + ) + expected := values.NewArrayWith(obj1, arr1).Sort() + + actual, err := objects.Values(context.Background(), obj) + actualSorted := actual.(*values.Array).Sort() + + So(err, ShouldBeNil) + So(actualSorted.Compare(expected), ShouldEqual, 0) + }) + + Convey("When both type attributes", t, func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("k1", values.NewInt(0)), + ) + arr1 := values.NewArrayWith( + values.NewInt(0), values.NewInt(1), + ) + int1 := values.NewInt(0) + obj := values.NewObjectWith( + values.NewObjectProperty("obj", obj1), + values.NewObjectProperty("arr", arr1), + values.NewObjectProperty("int", int1), + ) + expected := values.NewArrayWith(obj1, arr1, int1).Sort() + + actual, err := objects.Values(context.Background(), obj) + actualSorted := actual.(*values.Array).Sort() + + So(err, ShouldBeNil) + So(actualSorted.Compare(expected), ShouldEqual, 0) + }) + + Convey("Result is independent on the source object (array)", t, func() { + arr := values.NewArrayWith(values.NewInt(0)) + obj := values.NewObjectWith( + values.NewObjectProperty("arr", arr), + ) + expected := values.NewArrayWith(values.NewInt(0)) + + actual, err := objects.Values(context.Background(), obj) + actualSorted := actual.(*values.Array).Sort() + + So(err, ShouldBeNil) + + arr.Push(values.NewInt(1)) + + So(actualSorted.Compare(expected), ShouldEqual, 0) + }) + + Convey("Result is independent on the source object (object)", t, func() { + nested := values.NewObjectWith( + values.NewObjectProperty("int", values.NewInt(0)), + ) + obj := values.NewObjectWith( + values.NewObjectProperty("nested", nested), + ) + expected := values.NewArrayWith( + values.NewObjectWith( + values.NewObjectProperty("int", values.NewInt(0)), + ), + ) + + actual, err := objects.Values(context.Background(), obj) + actualSorted := actual.(*values.Array).Sort() + + So(err, ShouldBeNil) + + nested.Set("new", values.NewInt(1)) + + So(actualSorted.Compare(expected), ShouldEqual, 0) + }) +} From e8681af76bb293f613feee156e27de0152f4368b Mon Sep 17 00:00:00 2001 From: = Date: Tue, 16 Oct 2018 01:08:16 +0300 Subject: [PATCH 047/115] push test with bug --- pkg/runtime/values/array.go | 2 +- pkg/stdlib/objects/values.go | 25 ++++++++++++++++++++++++- pkg/stdlib/objects/values_test.go | 6 +++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/pkg/runtime/values/array.go b/pkg/runtime/values/array.go index 415e2867..65e6df77 100644 --- a/pkg/runtime/values/array.go +++ b/pkg/runtime/values/array.go @@ -228,7 +228,7 @@ func (t *Array) Sort() *Array { copy(c, t.value) sort.SliceStable(c, func(i, j int) bool { - return c[i].Compare(c[j]) == 0 + return c[i].Compare(c[j]) == -1 }) res := new(Array) diff --git a/pkg/stdlib/objects/values.go b/pkg/stdlib/objects/values.go index c1fd62ea..89f5d1da 100644 --- a/pkg/stdlib/objects/values.go +++ b/pkg/stdlib/objects/values.go @@ -3,6 +3,8 @@ package objects import ( "context" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/runtime/core" ) @@ -10,5 +12,26 @@ import ( // @params obj (Object) - an object. // @returns (Array of Value) - the values of document returned in any order. func Values(_ context.Context, args ...core.Value) (core.Value, error) { - return nil, nil + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.ObjectType) + if err != nil { + return values.None, err + } + + obj := args[0].(*values.Object) + vals := values.NewArray(0) + + obj.ForEach(func(val core.Value, key string) bool { + if values.IsCloneable(val) { + val = val.(core.Cloneable).Clone() + } + vals.Push(val) + return true + }) + + return vals, nil } diff --git a/pkg/stdlib/objects/values_test.go b/pkg/stdlib/objects/values_test.go index 7a17023f..360d9a39 100644 --- a/pkg/stdlib/objects/values_test.go +++ b/pkg/stdlib/objects/values_test.go @@ -163,7 +163,11 @@ func TestValues(t *testing.T) { obj := values.NewObjectWith( values.NewObjectProperty("arr", arr), ) - expected := values.NewArrayWith(values.NewInt(0)) + expected := values.NewArrayWith( + values.NewArrayWith( + values.NewInt(0), + ), + ) actual, err := objects.Values(context.Background(), obj) actualSorted := actual.(*values.Array).Sort() From 647bca48d5df16157e2b816417ee28704e6b5aee Mon Sep 17 00:00:00 2001 From: = Date: Wed, 17 Oct 2018 02:40:41 +0300 Subject: [PATCH 048/115] add stress test --- pkg/stdlib/objects/values_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pkg/stdlib/objects/values_test.go b/pkg/stdlib/objects/values_test.go index 360d9a39..fbb6f7d6 100644 --- a/pkg/stdlib/objects/values_test.go +++ b/pkg/stdlib/objects/values_test.go @@ -202,3 +202,26 @@ func TestValues(t *testing.T) { So(actualSorted.Compare(expected), ShouldEqual, 0) }) } + +func TestValuesStress(t *testing.T) { + Convey("Stress", t, func() { + for i := 0; i < 100; i++ { + obj1 := values.NewObjectWith( + values.NewObjectProperty("int0", values.NewInt(0)), + ) + obj2 := values.NewObjectWith( + values.NewObjectProperty("int1", values.NewInt(1)), + ) + obj := values.NewObjectWith( + values.NewObjectProperty("k1", obj1), + values.NewObjectProperty("k2", obj2), + ) + expected := values.NewArrayWith(obj1, obj2).Sort() + + actual, _ := objects.Values(context.Background(), obj) + actualSorted := actual.(*values.Array).Sort() + + So(actualSorted.Compare(expected), ShouldEqual, 0) + } + }) +} From a31e5d26a30a52fd7c7c87556b7005bb243a4fe1 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 19 Oct 2018 21:37:22 +0300 Subject: [PATCH 049/115] small changes in stress tests --- pkg/stdlib/objects/values_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/stdlib/objects/values_test.go b/pkg/stdlib/objects/values_test.go index fbb6f7d6..6a78c8de 100644 --- a/pkg/stdlib/objects/values_test.go +++ b/pkg/stdlib/objects/values_test.go @@ -218,9 +218,11 @@ func TestValuesStress(t *testing.T) { ) expected := values.NewArrayWith(obj1, obj2).Sort() - actual, _ := objects.Values(context.Background(), obj) + actual, err := objects.Values(context.Background(), obj) actualSorted := actual.(*values.Array).Sort() + So(err, ShouldBeNil) + So(actualSorted.Length(), ShouldEqual, expected.Length()) So(actualSorted.Compare(expected), ShouldEqual, 0) } }) From fcf3de82bbf476e205730f004960ccb9fbb2d7f9 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 21 Oct 2018 19:43:23 +0300 Subject: [PATCH 050/115] changes in object.Comapare --- pkg/runtime/values/object.go | 2 -- pkg/stdlib/objects/values_test.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index c5b56438..83fe22ff 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -77,8 +77,6 @@ func (t *Object) Compare(other core.Value) int { var exists bool other.ForEach(func(otherVal core.Value, key string) bool { - res = -1 - if val, exists = t.value[key]; exists { res = val.Compare(otherVal) } diff --git a/pkg/stdlib/objects/values_test.go b/pkg/stdlib/objects/values_test.go index 6a78c8de..2453edcd 100644 --- a/pkg/stdlib/objects/values_test.go +++ b/pkg/stdlib/objects/values_test.go @@ -216,7 +216,7 @@ func TestValuesStress(t *testing.T) { values.NewObjectProperty("k1", obj1), values.NewObjectProperty("k2", obj2), ) - expected := values.NewArrayWith(obj1, obj2).Sort() + expected := values.NewArrayWith(obj2, obj1).Sort() actual, err := objects.Values(context.Background(), obj) actualSorted := actual.(*values.Array).Sort() From a4a3b3ba544d8911b12c94d92cc37e9f27522341 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 22 Oct 2018 22:47:47 +0300 Subject: [PATCH 051/115] change object.Compare --- pkg/runtime/values/object.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 83fe22ff..8bc48a56 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -73,16 +73,22 @@ func (t *Object) Compare(other core.Value) int { var res = 0 - var val core.Value - var exists bool + sortedT := sort.StringSlice(t.Keys()) + sortedT.Sort() - other.ForEach(func(otherVal core.Value, key string) bool { - if val, exists = t.value[key]; exists { - res = val.Compare(otherVal) - } + sortedOther := sort.StringSlice(other.Keys()) + sortedOther.Sort() - return res == 0 - }) + for i := 0; i < len(t.value) && res == 0; i++ { + if sortedT[i] == sortedOther[i] { + continue + } + if sortedT[i] < sortedOther[i] { + res = 1 + continue + } + res = -1 + } return res default: From 24e07553b95467733661cc11bf0cc576ab48d54f Mon Sep 17 00:00:00 2001 From: = Date: Mon, 22 Oct 2018 23:11:32 +0300 Subject: [PATCH 052/115] add more tests for object.Compare --- pkg/runtime/values/object.go | 21 ++++++++--- pkg/runtime/values/object_test.go | 62 +++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 8bc48a56..b235d740 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -79,15 +79,24 @@ func (t *Object) Compare(other core.Value) int { sortedOther := sort.StringSlice(other.Keys()) sortedOther.Sort() + var tKey, otherKey String + var tVal, otherVal core.Value + for i := 0; i < len(t.value) && res == 0; i++ { - if sortedT[i] == sortedOther[i] { - continue - } - if sortedT[i] < sortedOther[i] { + tKey, otherKey = NewString(sortedT[i]), NewString(sortedOther[i]) + + switch tKey.Compare(otherKey) { + case 0: + tVal, _ = t.Get(tKey) + otherVal, _ = other.Get(otherKey) + res = tVal.Compare(otherVal) + case 1: + res = -1 + break + case -1: res = 1 - continue + break } - res = -1 } return res diff --git a/pkg/runtime/values/object_test.go b/pkg/runtime/values/object_test.go index ec7ac240..3092e2a7 100644 --- a/pkg/runtime/values/object_test.go +++ b/pkg/runtime/values/object_test.go @@ -157,6 +157,68 @@ func TestObject(t *testing.T) { So(obj1.Compare(obj2), ShouldEqual, -1) }) + + Convey("ArangoDB compatibility", func() { + Convey("It should return 1 when {a:1} and {b:2}", func() { + obj1 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1))) + obj2 := values.NewObjectWith(values.NewObjectProperty("b", values.NewInt(2))) + + So(obj1.Compare(obj2), ShouldEqual, 1) + }) + + Convey("It should return 0 when {a:1} and {a:1}", func() { + obj1 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1))) + obj2 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1))) + + So(obj1.Compare(obj2), ShouldEqual, 0) + }) + + Convey("It should return 0 {a:1, c:2} and {c:2, a:1}", func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("c", values.NewInt(2)), + ) + obj2 := values.NewObjectWith( + values.NewObjectProperty("c", values.NewInt(2)), + values.NewObjectProperty("a", values.NewInt(1)), + ) + + So(obj1.Compare(obj2), ShouldEqual, 0) + }) + + Convey("It should return -1 when {a:1} and {a:2}", func() { + obj1 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1))) + obj2 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(2))) + + So(obj1.Compare(obj2), ShouldEqual, -1) + }) + + Convey("It should return 1 when {a:1, c:2} and {c:2, b:2}", func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("c", values.NewInt(2)), + ) + obj2 := values.NewObjectWith( + values.NewObjectProperty("c", values.NewInt(2)), + values.NewObjectProperty("b", values.NewInt(2)), + ) + + So(obj1.Compare(obj2), ShouldEqual, 1) + }) + + Convey("It should return 1 {a:1, c:3} and {c:2, a:1}", func() { + obj1 := values.NewObjectWith( + values.NewObjectProperty("a", values.NewInt(1)), + values.NewObjectProperty("c", values.NewInt(3)), + ) + obj2 := values.NewObjectWith( + values.NewObjectProperty("c", values.NewInt(2)), + values.NewObjectProperty("a", values.NewInt(1)), + ) + + So(obj1.Compare(obj2), ShouldEqual, 1) + }) + }) }) Convey(".Hash", t, func() { From eff5501495baa7f2c6492201af2fcbabdfc8c0e1 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 22 Oct 2018 23:15:53 +0300 Subject: [PATCH 053/115] added comments to object.Compare function --- pkg/runtime/values/object.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index b235d740..138b6d53 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -56,6 +56,9 @@ func (t *Object) String() string { return string(marshaled) } +// Compare compares the source object with other core.Value +// The behavior of the Compare is similar +// to the comparison of objects in ArangoDB func (t *Object) Compare(other core.Value) int { switch other.Type() { case core.ObjectType: From bd24a671ac4307cafc0ed9cd4186d24c2bef71da Mon Sep 17 00:00:00 2001 From: = Date: Mon, 22 Oct 2018 23:38:04 +0300 Subject: [PATCH 054/115] change object.Comapare --- pkg/runtime/values/object.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 138b6d53..005a981b 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -82,24 +82,23 @@ func (t *Object) Compare(other core.Value) int { sortedOther := sort.StringSlice(other.Keys()) sortedOther.Sort() - var tKey, otherKey String + // var tKey, otherKey string var tVal, otherVal core.Value for i := 0; i < len(t.value) && res == 0; i++ { - tKey, otherKey = NewString(sortedT[i]), NewString(sortedOther[i]) - - switch tKey.Compare(otherKey) { - case 0: - tVal, _ = t.Get(tKey) - otherVal, _ = other.Get(otherKey) + if sortedT[i] == sortedOther[i] { + tVal, _ = t.Get(NewString(sortedT[i])) + otherVal, _ = other.Get(NewString(sortedOther[i])) res = tVal.Compare(otherVal) - case 1: - res = -1 - break - case -1: + continue + } + + if sortedT[i] < sortedOther[i] { res = 1 break } + + res = -1 } return res From f41d6bb798af124f1cda5cf165093e840b5fc508 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 22 Oct 2018 23:38:38 +0300 Subject: [PATCH 055/115] delete useless comment --- pkg/runtime/values/object.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 005a981b..1eef9120 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -82,7 +82,6 @@ func (t *Object) Compare(other core.Value) int { sortedOther := sort.StringSlice(other.Keys()) sortedOther.Sort() - // var tKey, otherKey string var tVal, otherVal core.Value for i := 0; i < len(t.value) && res == 0; i++ { From 657e97391661951652aa2ae68d69657618ca43c0 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 23 Oct 2018 00:33:21 +0300 Subject: [PATCH 056/115] one more change in object.Compare --- pkg/runtime/values/object.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 1eef9120..504d0c07 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -83,21 +83,25 @@ func (t *Object) Compare(other core.Value) int { sortedOther.Sort() var tVal, otherVal core.Value + var tKey, otherKey string for i := 0; i < len(t.value) && res == 0; i++ { - if sortedT[i] == sortedOther[i] { - tVal, _ = t.Get(NewString(sortedT[i])) - otherVal, _ = other.Get(NewString(sortedOther[i])) + tKey, otherKey = sortedT[i], sortedOther[i] + + if tKey == otherKey { + tVal, _ = t.Get(NewString(tKey)) + otherVal, _ = other.Get(NewString(tKey)) res = tVal.Compare(otherVal) continue } - if sortedT[i] < sortedOther[i] { + if tKey < otherKey { res = 1 - break + } else { + res = -1 } - res = -1 + break } return res From b0e2c06f51296851d6d407f3fddeb85aecb03a63 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 30 Oct 2018 21:16:18 +0300 Subject: [PATCH 057/115] init datetime --- pkg/stdlib/datetime/now.go | 20 ++++++++++++++++ pkg/stdlib/datetime/now_test.go | 42 +++++++++++++++++++++++++++++++++ pkg/stdlib/strings/fmt_test.go | 1 - 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 pkg/stdlib/datetime/now.go create mode 100644 pkg/stdlib/datetime/now_test.go diff --git a/pkg/stdlib/datetime/now.go b/pkg/stdlib/datetime/now.go new file mode 100644 index 00000000..8abd0b7e --- /dev/null +++ b/pkg/stdlib/datetime/now.go @@ -0,0 +1,20 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +// Now returns new DateTime object with Time equal to time.Now(). +// @returns (DateTime) - New DateTime object. +func Now(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 0, 0) + if err != nil { + return values.None, err + } + + return values.NewCurrentDateTime(), nil +} diff --git a/pkg/stdlib/datetime/now_test.go b/pkg/stdlib/datetime/now_test.go new file mode 100644 index 00000000..28b521ec --- /dev/null +++ b/pkg/stdlib/datetime/now_test.go @@ -0,0 +1,42 @@ +package datetime_test + +import ( + "context" + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" + + . "github.com/smartystreets/goconvey/convey" +) + +type testCase struct { + Name string + Expected values.DateTime + Args []core.Value + ShouldErr bool +} + +func TestNow(t *testing.T) { + +} + +func (tc *testCase) Do(t *testing.T) { + Convey(tc.Name, t, func() { + var expected core.Value + + expected = values.NewDateTime(tc.Expected.Time) + + dt, err := datetime.Now(context.Background(), tc.Args...) + + if tc.ShouldErr { + So(err, ShouldBeError) + expected = values.None + } else { + So(err, ShouldBeNil) + } + + So(dt, ShouldEqual, expected) + }) +} diff --git a/pkg/stdlib/strings/fmt_test.go b/pkg/stdlib/strings/fmt_test.go index cf4603ff..7995cd64 100644 --- a/pkg/stdlib/strings/fmt_test.go +++ b/pkg/stdlib/strings/fmt_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/stdlib/strings" . "github.com/smartystreets/goconvey/convey" From 6f82025e0e77e23a337e84db948cab2ddcd770c2 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 30 Oct 2018 22:23:20 +0300 Subject: [PATCH 058/115] added test for datetime --- pkg/stdlib/datetime/now_test.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/pkg/stdlib/datetime/now_test.go b/pkg/stdlib/datetime/now_test.go index 28b521ec..acc6d8e8 100644 --- a/pkg/stdlib/datetime/now_test.go +++ b/pkg/stdlib/datetime/now_test.go @@ -3,6 +3,7 @@ package datetime_test import ( "context" "testing" + "time" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" @@ -13,20 +14,34 @@ import ( type testCase struct { Name string - Expected values.DateTime + Expected core.Value + TimeArg time.Time Args []core.Value ShouldErr bool } func TestNow(t *testing.T) { - + tcs := []*testCase{ + &testCase{ + Name: "When too many arguments", + Expected: values.None, + Args: []core.Value{ + values.NewCurrentDateTime(), + }, + ShouldErr: true, + }, + } + + for _, tc := range tcs { + tc.Do(t) + } } func (tc *testCase) Do(t *testing.T) { Convey(tc.Name, t, func() { var expected core.Value - expected = values.NewDateTime(tc.Expected.Time) + expected = values.NewDateTime(tc.TimeArg) dt, err := datetime.Now(context.Background(), tc.Args...) From 09546ae2ecd78dd62bcae9339eaf3be465c943b2 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 30 Oct 2018 22:39:42 +0300 Subject: [PATCH 059/115] added lib.go --- pkg/stdlib/datetime/lib.go | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 pkg/stdlib/datetime/lib.go diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go new file mode 100644 index 00000000..d3d3ea33 --- /dev/null +++ b/pkg/stdlib/datetime/lib.go @@ -0,0 +1,9 @@ +package datetime + +import "github.com/MontFerret/ferret/pkg/runtime/core" + +func NewLib() map[string]core.Function { + return map[string]core.Function{ + "NOW": Now, + } +} From d87079898d9391aedc29c2cbf9cba83cc4141b5c Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:20:21 +0300 Subject: [PATCH 060/115] add helpers functions --- pkg/stdlib/datetime/helpers_test.go | 65 +++++++++++++++++++++++++++++ pkg/stdlib/datetime/now_test.go | 36 ++-------------- 2 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 pkg/stdlib/datetime/helpers_test.go diff --git a/pkg/stdlib/datetime/helpers_test.go b/pkg/stdlib/datetime/helpers_test.go new file mode 100644 index 00000000..1fe4b0b5 --- /dev/null +++ b/pkg/stdlib/datetime/helpers_test.go @@ -0,0 +1,65 @@ +package datetime_test + +import ( + "context" + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + + . "github.com/smartystreets/goconvey/convey" +) + +type testCase struct { + Name string + Expected core.Value + TimeArg time.Time + Args []core.Value + ShouldErr bool +} + +func (tc *testCase) Do(t *testing.T, fn core.Function) { + Convey(tc.Name, t, func() { + expected := tc.Expected + + actual, err := fn(context.Background(), tc.Args...) + + if tc.ShouldErr { + So(err, ShouldBeError) + expected = values.None + } else { + So(err, ShouldBeNil) + } + + So(actual.Compare(expected), ShouldEqual, 0) + }) +} + +func mustDefaultLayoutDt(timeString string) values.DateTime { + dt, err := defaultLayoutDt(timeString) + if err != nil { + panic(err) + } + return dt +} + +func mustLayoutDt(layout, value string) values.DateTime { + dt, err := layoutDt(layout, value) + if err != nil { + panic(err) + } + return dt +} + +func defaultLayoutDt(timeString string) (values.DateTime, error) { + return layoutDt(values.DefaultTimeLayout, timeString) +} + +func layoutDt(layout, value string) (values.DateTime, error) { + t, err := time.Parse(layout, value) + if err != nil { + return values.DateTime{}, err + } + return values.NewDateTime(t), nil +} diff --git a/pkg/stdlib/datetime/now_test.go b/pkg/stdlib/datetime/now_test.go index acc6d8e8..559b12b1 100644 --- a/pkg/stdlib/datetime/now_test.go +++ b/pkg/stdlib/datetime/now_test.go @@ -1,25 +1,14 @@ package datetime_test import ( - "context" "testing" - "time" - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/stdlib/datetime" - . "github.com/smartystreets/goconvey/convey" + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" ) -type testCase struct { - Name string - Expected core.Value - TimeArg time.Time - Args []core.Value - ShouldErr bool -} - func TestNow(t *testing.T) { tcs := []*testCase{ &testCase{ @@ -33,25 +22,6 @@ func TestNow(t *testing.T) { } for _, tc := range tcs { - tc.Do(t) + tc.Do(t, datetime.Now) } } - -func (tc *testCase) Do(t *testing.T) { - Convey(tc.Name, t, func() { - var expected core.Value - - expected = values.NewDateTime(tc.TimeArg) - - dt, err := datetime.Now(context.Background(), tc.Args...) - - if tc.ShouldErr { - So(err, ShouldBeError) - expected = values.None - } else { - So(err, ShouldBeNil) - } - - So(dt, ShouldEqual, expected) - }) -} From 1cb998e3e10559654932ee9f03071119d5a034c2 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:21:38 +0300 Subject: [PATCH 061/115] made values.DefaultTimeLayout public --- pkg/runtime/values/date_time.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/runtime/values/date_time.go b/pkg/runtime/values/date_time.go index 69981ce5..1ab4d585 100644 --- a/pkg/runtime/values/date_time.go +++ b/pkg/runtime/values/date_time.go @@ -7,7 +7,7 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/core" ) -const defaultTimeLayout = time.RFC3339 +const DefaultTimeLayout = time.RFC3339 type DateTime struct { time.Time @@ -26,7 +26,7 @@ func NewDateTime(time time.Time) DateTime { } func ParseDateTime(input interface{}) (DateTime, error) { - return ParseDateTimeWith(input, defaultTimeLayout) + return ParseDateTimeWith(input, DefaultTimeLayout) } func ParseDateTimeWith(input interface{}, layout string) (DateTime, error) { From f3b1372e2e6020d7802f14d05aa6509f2e574ea0 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:34:59 +0300 Subject: [PATCH 062/115] added DATE function --- pkg/stdlib/datetime/date.go | 33 +++++++++++++++++++++ pkg/stdlib/datetime/date_test.go | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 pkg/stdlib/datetime/date.go create mode 100644 pkg/stdlib/datetime/date_test.go diff --git a/pkg/stdlib/datetime/date.go b/pkg/stdlib/datetime/date.go new file mode 100644 index 00000000..ce7a8ae4 --- /dev/null +++ b/pkg/stdlib/datetime/date.go @@ -0,0 +1,33 @@ +package datetime + +import ( + "context" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// Date convert RFC3339 date time string to DateTime object. +// @params timeString (String) - string in RFC3339 format. +// @return (DateTime) - new DateTime object derived from timeString. +func Date(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.StringType) + if err != nil { + return values.None, err + } + + timeStrings := args[0].(values.String) + + t, err := time.Parse(values.DefaultTimeLayout, timeStrings.String()) + if err != nil { + return values.None, err + } + + return values.NewDateTime(t), nil +} diff --git a/pkg/stdlib/datetime/date_test.go b/pkg/stdlib/datetime/date_test.go new file mode 100644 index 00000000..221fcbb3 --- /dev/null +++ b/pkg/stdlib/datetime/date_test.go @@ -0,0 +1,49 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDate(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When incorrect timeStrings", + Expected: values.None, + Args: []core.Value{ + values.NewString("bla-bla"), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When correct timeString in RFC3339 format", + Expected: mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + Args: []core.Value{ + values.NewString("1999-02-07T15:04:05Z"), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.Date) + } +} From f5b7cb95b63bcf23d1a4cb8f7e75cbfed59bc5ce Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:36:03 +0300 Subject: [PATCH 063/115] added DATE_DAYOFWEEK function --- pkg/stdlib/datetime/dayofweek.go | 28 +++++++++++++++++ pkg/stdlib/datetime/dayofweek_test.go | 43 +++++++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 4 ++- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 pkg/stdlib/datetime/dayofweek.go create mode 100644 pkg/stdlib/datetime/dayofweek_test.go diff --git a/pkg/stdlib/datetime/dayofweek.go b/pkg/stdlib/datetime/dayofweek.go new file mode 100644 index 00000000..64b363c4 --- /dev/null +++ b/pkg/stdlib/datetime/dayofweek.go @@ -0,0 +1,28 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +// DateDayOfWeek returns number of the weekday from the date. Sunday is the 0th day of week. +// @params date (DateTime) - source DateTime. +// @return (Int) - return number of the weekday. +func DateDayOfWeek(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + wday := args[0].(values.DateTime).Weekday() + + return values.NewInt(int(wday)), nil +} diff --git a/pkg/stdlib/datetime/dayofweek_test.go b/pkg/stdlib/datetime/dayofweek_test.go new file mode 100644 index 00000000..617cfef6 --- /dev/null +++ b/pkg/stdlib/datetime/dayofweek_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateDayOfWeek(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When Sunday (0th day)", + Expected: values.NewInt(0), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When Monday (1th day)", + Expected: values.NewInt(1), + Args: []core.Value{mustDefaultLayoutDt("1999-02-08T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDayOfWeek) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index d3d3ea33..b398e909 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -4,6 +4,8 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "NOW": Now, + "NOW": Now, + "DATE": Date, + "DATE_DAYOFWEEK": DateDayOfWeek, } } From db2ab4b19e83919565a1edb83201d09dbc152841 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:36:39 +0300 Subject: [PATCH 064/115] added DATE_YEAR function --- pkg/stdlib/datetime/lib.go | 1 + pkg/stdlib/datetime/year.go | 27 ++++++++++++++++++++ pkg/stdlib/datetime/year_test.go | 43 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 pkg/stdlib/datetime/year.go create mode 100644 pkg/stdlib/datetime/year_test.go diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index b398e909..96e319f3 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -7,5 +7,6 @@ func NewLib() map[string]core.Function { "NOW": Now, "DATE": Date, "DATE_DAYOFWEEK": DateDayOfWeek, + "DATE_YEAR": DateYear, } } diff --git a/pkg/stdlib/datetime/year.go b/pkg/stdlib/datetime/year.go new file mode 100644 index 00000000..41b66dc6 --- /dev/null +++ b/pkg/stdlib/datetime/year.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateYear returns the year extracted from the given date. +// @params date (DateTime) - source DateTime. +// @return (Int) - a year number. +func DateYear(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + year := args[0].(values.DateTime).Year() + + return values.NewInt(year), nil +} diff --git a/pkg/stdlib/datetime/year_test.go b/pkg/stdlib/datetime/year_test.go new file mode 100644 index 00000000..29b9ab0b --- /dev/null +++ b/pkg/stdlib/datetime/year_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateYear(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 1999th year", + Expected: values.NewInt(1999), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 1629th year", + Expected: values.NewInt(1629), + Args: []core.Value{mustDefaultLayoutDt("1629-02-08T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateYear) + } +} From 91c10ceb8f8e72739f505610591f4306f0f38696 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 1 Nov 2018 22:53:47 +0300 Subject: [PATCH 065/115] added DATE_MONTH function --- pkg/stdlib/datetime/lib.go | 1 + pkg/stdlib/datetime/month.go | 27 ++++++++++++++++++++++ pkg/stdlib/datetime/month_test.go | 38 +++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 pkg/stdlib/datetime/month.go create mode 100644 pkg/stdlib/datetime/month_test.go diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 96e319f3..31df3d5a 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -8,5 +8,6 @@ func NewLib() map[string]core.Function { "DATE": Date, "DATE_DAYOFWEEK": DateDayOfWeek, "DATE_YEAR": DateYear, + "DATE_MONTH": DateMonth, } } diff --git a/pkg/stdlib/datetime/month.go b/pkg/stdlib/datetime/month.go new file mode 100644 index 00000000..3633a87f --- /dev/null +++ b/pkg/stdlib/datetime/month.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateMonth returns the month of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a month number. +func DateMonth(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + month := args[0].(values.DateTime).Month() + + return values.NewInt(int(month)), nil +} diff --git a/pkg/stdlib/datetime/month_test.go b/pkg/stdlib/datetime/month_test.go new file mode 100644 index 00000000..ff192648 --- /dev/null +++ b/pkg/stdlib/datetime/month_test.go @@ -0,0 +1,38 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateMonth(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 2th month", + Expected: values.NewInt(2), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateMonth) + } +} From a73793ac448adf7d0823591e585124b9d8bf61c9 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 1 Nov 2018 23:02:04 +0300 Subject: [PATCH 066/115] added one more testCase for DATE_MONTH --- pkg/stdlib/datetime/month_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/stdlib/datetime/month_test.go b/pkg/stdlib/datetime/month_test.go index ff192648..56927880 100644 --- a/pkg/stdlib/datetime/month_test.go +++ b/pkg/stdlib/datetime/month_test.go @@ -30,6 +30,11 @@ func TestDateMonth(t *testing.T) { Expected: values.NewInt(2), Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, }, + &testCase{ + Name: "When 12th month", + Expected: values.NewInt(12), + Args: []core.Value{mustDefaultLayoutDt("1999-12-07T15:04:05Z")}, + }, } for _, tc := range tcs { From efc53b6ce8ebe3cd61072e6c2e2833110015a4c2 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 1 Nov 2018 23:02:26 +0300 Subject: [PATCH 067/115] added DATE_DAY function --- pkg/stdlib/datetime/day.go | 27 +++++++++++++++++++++ pkg/stdlib/datetime/day_test.go | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 pkg/stdlib/datetime/day.go create mode 100644 pkg/stdlib/datetime/day_test.go diff --git a/pkg/stdlib/datetime/day.go b/pkg/stdlib/datetime/day.go new file mode 100644 index 00000000..fe246d00 --- /dev/null +++ b/pkg/stdlib/datetime/day.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateDay returns the day of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a day number. +func DateDay(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + day := args[0].(values.DateTime).Day() + + return values.NewInt(day), nil +} diff --git a/pkg/stdlib/datetime/day_test.go b/pkg/stdlib/datetime/day_test.go new file mode 100644 index 00000000..bb7ded3d --- /dev/null +++ b/pkg/stdlib/datetime/day_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateDay(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 7th day", + Expected: values.NewInt(7), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 28th day", + Expected: values.NewInt(28), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDay) + } +} From e60f23d64709a6caca544f8c190da7055e5387f1 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 1 Nov 2018 23:12:22 +0300 Subject: [PATCH 068/115] added DateDay to lib --- pkg/stdlib/datetime/lib.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 31df3d5a..7bf130d7 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -9,5 +9,6 @@ func NewLib() map[string]core.Function { "DATE_DAYOFWEEK": DateDayOfWeek, "DATE_YEAR": DateYear, "DATE_MONTH": DateMonth, + "DATE_DAY": DateDay, } } From a998edc27daba96cbe832dc38336ba31671f728c Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 17:22:17 +0300 Subject: [PATCH 069/115] added DATE_HOUR, DATE_MINUTE and DATE_SECOND functions --- pkg/stdlib/datetime/hour.go | 27 +++++++++++++++++++ pkg/stdlib/datetime/hour_test.go | 43 ++++++++++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 3 +++ pkg/stdlib/datetime/minute.go | 27 +++++++++++++++++++ pkg/stdlib/datetime/minute_test.go | 43 ++++++++++++++++++++++++++++++ pkg/stdlib/datetime/second.go | 27 +++++++++++++++++++ pkg/stdlib/datetime/second_test.go | 43 ++++++++++++++++++++++++++++++ 7 files changed, 213 insertions(+) create mode 100644 pkg/stdlib/datetime/hour.go create mode 100644 pkg/stdlib/datetime/hour_test.go create mode 100644 pkg/stdlib/datetime/minute.go create mode 100644 pkg/stdlib/datetime/minute_test.go create mode 100644 pkg/stdlib/datetime/second.go create mode 100644 pkg/stdlib/datetime/second_test.go diff --git a/pkg/stdlib/datetime/hour.go b/pkg/stdlib/datetime/hour.go new file mode 100644 index 00000000..bb56e924 --- /dev/null +++ b/pkg/stdlib/datetime/hour.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateHour returns the hour of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a hour number. +func DateHour(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + hour := args[0].(values.DateTime).Hour() + + return values.NewInt(hour), nil +} diff --git a/pkg/stdlib/datetime/hour_test.go b/pkg/stdlib/datetime/hour_test.go new file mode 100644 index 00000000..1118c1ea --- /dev/null +++ b/pkg/stdlib/datetime/hour_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateHour(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 7th hour", + Expected: values.NewInt(7), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T07:04:05Z")}, + }, + &testCase{ + Name: "When 15th day", + Expected: values.NewInt(15), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateHour) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 7bf130d7..1fd5d27e 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -10,5 +10,8 @@ func NewLib() map[string]core.Function { "DATE_YEAR": DateYear, "DATE_MONTH": DateMonth, "DATE_DAY": DateDay, + "DATE_HOUR": DateHour, + "DATE_MINUTE": DateMinute, + "DATE_SECOND": DateSecond, } } diff --git a/pkg/stdlib/datetime/minute.go b/pkg/stdlib/datetime/minute.go new file mode 100644 index 00000000..c5f9fe5f --- /dev/null +++ b/pkg/stdlib/datetime/minute.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateMinute returns the minute of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a minute number. +func DateMinute(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + min := args[0].(values.DateTime).Minute() + + return values.NewInt(min), nil +} diff --git a/pkg/stdlib/datetime/minute_test.go b/pkg/stdlib/datetime/minute_test.go new file mode 100644 index 00000000..f2fbbffd --- /dev/null +++ b/pkg/stdlib/datetime/minute_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateMinute(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 4th minute", + Expected: values.NewInt(4), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 59th minute", + Expected: values.NewInt(59), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateMinute) + } +} diff --git a/pkg/stdlib/datetime/second.go b/pkg/stdlib/datetime/second.go new file mode 100644 index 00000000..ca3e2d18 --- /dev/null +++ b/pkg/stdlib/datetime/second.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateSecond returns the second of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a second number. +func DateSecond(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + sec := args[0].(values.DateTime).Second() + + return values.NewInt(sec), nil +} diff --git a/pkg/stdlib/datetime/second_test.go b/pkg/stdlib/datetime/second_test.go new file mode 100644 index 00000000..64f194b4 --- /dev/null +++ b/pkg/stdlib/datetime/second_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateSecond(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 5th second", + Expected: values.NewInt(5), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 59th second", + Expected: values.NewInt(59), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:59Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateSecond) + } +} From 9dc392d11ee4e896603ba06f2cb900ec40672c18 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 18:54:33 +0300 Subject: [PATCH 070/115] added DATE_DAYOFYEAR, DATE_LEAPYEAR, DATE_MILLISECOND functions --- pkg/stdlib/datetime/dayofyear.go | 29 +++++++++++++ pkg/stdlib/datetime/dayofyear_test.go | 43 ++++++++++++++++++++ pkg/stdlib/datetime/leapyear.go | 31 ++++++++++++++ pkg/stdlib/datetime/leapyear_test.go | 43 ++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 21 +++++----- pkg/stdlib/datetime/millisecond.go | 27 +++++++++++++ pkg/stdlib/datetime/millisecond_test.go | 54 +++++++++++++++++++++++++ 7 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 pkg/stdlib/datetime/dayofyear.go create mode 100644 pkg/stdlib/datetime/dayofyear_test.go create mode 100644 pkg/stdlib/datetime/leapyear.go create mode 100644 pkg/stdlib/datetime/leapyear_test.go create mode 100644 pkg/stdlib/datetime/millisecond.go create mode 100644 pkg/stdlib/datetime/millisecond_test.go diff --git a/pkg/stdlib/datetime/dayofyear.go b/pkg/stdlib/datetime/dayofyear.go new file mode 100644 index 00000000..61218a3e --- /dev/null +++ b/pkg/stdlib/datetime/dayofyear.go @@ -0,0 +1,29 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +// DateDayOfYear returns the day of year number of date. +// The return value range from 1 to 365 (366 in a leap year). +// @params date (DateTime) - source DateTime. +// @return (Int) - a day of year number. +func DateDayOfYear(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + dayOfYear := args[0].(values.DateTime).YearDay() + + return values.NewInt(dayOfYear), nil +} diff --git a/pkg/stdlib/datetime/dayofyear_test.go b/pkg/stdlib/datetime/dayofyear_test.go new file mode 100644 index 00000000..7f640295 --- /dev/null +++ b/pkg/stdlib/datetime/dayofyear_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateDayOfYear(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 38th day of the year", + Expected: values.NewInt(38), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 59th day of the year", + Expected: values.NewInt(59), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDayOfYear) + } +} diff --git a/pkg/stdlib/datetime/leapyear.go b/pkg/stdlib/datetime/leapyear.go new file mode 100644 index 00000000..0bca5a24 --- /dev/null +++ b/pkg/stdlib/datetime/leapyear.go @@ -0,0 +1,31 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateLeapYear returns true if date is in a leap year else false. +// @params date (DateTime) - source DateTime. +// @return (Boolean) - date is in a leap year. +func DateLeapYear(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + year := args[0].(values.DateTime).Year() + + return values.NewBoolean(isLeap(year)), nil +} + +func isLeap(year int) bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) +} diff --git a/pkg/stdlib/datetime/leapyear_test.go b/pkg/stdlib/datetime/leapyear_test.go new file mode 100644 index 00000000..16b0ce38 --- /dev/null +++ b/pkg/stdlib/datetime/leapyear_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateLeapYear(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When a leap year", + Expected: values.NewBoolean(false), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When not a leap year", + Expected: values.NewBoolean(true), + Args: []core.Value{mustDefaultLayoutDt("1972-12-07T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateLeapYear) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 1fd5d27e..2feff7b4 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -4,14 +4,17 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "NOW": Now, - "DATE": Date, - "DATE_DAYOFWEEK": DateDayOfWeek, - "DATE_YEAR": DateYear, - "DATE_MONTH": DateMonth, - "DATE_DAY": DateDay, - "DATE_HOUR": DateHour, - "DATE_MINUTE": DateMinute, - "DATE_SECOND": DateSecond, + "NOW": Now, + "DATE": Date, + "DATE_DAYOFWEEK": DateDayOfWeek, + "DATE_YEAR": DateYear, + "DATE_MONTH": DateMonth, + "DATE_DAY": DateDay, + "DATE_HOUR": DateHour, + "DATE_MINUTE": DateMinute, + "DATE_SECOND": DateSecond, + "DATE_MILLISECOND": DateMillisecond, + "DATE_DAYOFYEAR": DateDayOfYear, + "DATE_LEAPYEAR": DateLeapYear, } } diff --git a/pkg/stdlib/datetime/millisecond.go b/pkg/stdlib/datetime/millisecond.go new file mode 100644 index 00000000..78aa679c --- /dev/null +++ b/pkg/stdlib/datetime/millisecond.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateMillisecond returns the millisecond of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a millisecond number. +func DateMillisecond(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + msec := args[0].(values.DateTime).Nanosecond() / 1000000 + + return values.NewInt(msec), nil +} diff --git a/pkg/stdlib/datetime/millisecond_test.go b/pkg/stdlib/datetime/millisecond_test.go new file mode 100644 index 00000000..b8d42bf4 --- /dev/null +++ b/pkg/stdlib/datetime/millisecond_test.go @@ -0,0 +1,54 @@ +package datetime_test + +import ( + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateMillisecond(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 129 millisecond", + Expected: values.NewInt(129), + Args: []core.Value{ + values.NewDateTime(time.Date(2018, 11, 4, 17, 3, 12, 129410001, time.Local)), + }, + }, + &testCase{ + Name: "When 0 milliseconds [0]", + Expected: values.NewInt(0), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:59Z")}, + }, + // any nanosec < 1000000 equal to 0 milliseconds + &testCase{ + Name: "When 0 milliseconds [1]", + Expected: values.NewInt(0), + Args: []core.Value{ + values.NewDateTime(time.Date(0, 0, 0, 0, 0, 0, 999999, time.Local)), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateMillisecond) + } +} From ef033e9d5c0a32bf5b2a3768a6815b2ab1d71eac Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 19:24:46 +0300 Subject: [PATCH 071/115] fix names in tests --- pkg/stdlib/datetime/leapyear_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/stdlib/datetime/leapyear_test.go b/pkg/stdlib/datetime/leapyear_test.go index 16b0ce38..d25f2984 100644 --- a/pkg/stdlib/datetime/leapyear_test.go +++ b/pkg/stdlib/datetime/leapyear_test.go @@ -26,12 +26,12 @@ func TestDateLeapYear(t *testing.T) { ShouldErr: true, }, &testCase{ - Name: "When a leap year", + Name: "When not a leap year", Expected: values.NewBoolean(false), Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, }, &testCase{ - Name: "When not a leap year", + Name: "When a leap year", Expected: values.NewBoolean(true), Args: []core.Value{mustDefaultLayoutDt("1972-12-07T15:04:05Z")}, }, From b5e79e7794583d2dbde6035ac377c02c0a2a161f Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 19:29:10 +0300 Subject: [PATCH 072/115] one more case into dayofyear_test --- pkg/stdlib/datetime/dayofyear_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/stdlib/datetime/dayofyear_test.go b/pkg/stdlib/datetime/dayofyear_test.go index 7f640295..a847befb 100644 --- a/pkg/stdlib/datetime/dayofyear_test.go +++ b/pkg/stdlib/datetime/dayofyear_test.go @@ -2,6 +2,7 @@ package datetime_test import ( "testing" + "time" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" @@ -35,6 +36,13 @@ func TestDateDayOfYear(t *testing.T) { Expected: values.NewInt(59), Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:05Z")}, }, + &testCase{ + Name: "When 366th day of the year", + Expected: values.NewInt(366), + Args: []core.Value{ + values.NewDateTime(time.Date(1972, time.December, 31, 0, 0, 0, 0, time.Local)), + }, + }, } for _, tc := range tcs { From c6c88e18e982235ad648191e0c7d70b9c99b4c40 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 23:29:41 +0300 Subject: [PATCH 073/115] added DATE_QUARTER function --- pkg/stdlib/datetime/lib.go | 1 + pkg/stdlib/datetime/quarter.go | 38 +++++++++++++++++++++++++ pkg/stdlib/datetime/quarter_test.go | 44 +++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 pkg/stdlib/datetime/quarter.go create mode 100644 pkg/stdlib/datetime/quarter_test.go diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 2feff7b4..e0c68d9a 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -16,5 +16,6 @@ func NewLib() map[string]core.Function { "DATE_MILLISECOND": DateMillisecond, "DATE_DAYOFYEAR": DateDayOfYear, "DATE_LEAPYEAR": DateLeapYear, + "DATE_QUARTER": DateQuarter, } } diff --git a/pkg/stdlib/datetime/quarter.go b/pkg/stdlib/datetime/quarter.go new file mode 100644 index 00000000..849d8395 --- /dev/null +++ b/pkg/stdlib/datetime/quarter.go @@ -0,0 +1,38 @@ +package datetime + +import ( + "context" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateQuarter Return which quarter date belongs to. +// @params date (DateTime) - source DateTime. +// @return (Int) - a quarter number. +func DateQuarter(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + month := args[0].(values.DateTime).Month() + quarter := values.NewInt(1) + + switch month { + case time.April, time.May, time.June: + quarter = values.NewInt(2) + case time.July, time.August, time.September: + quarter = values.NewInt(3) + case time.October, time.November, time.December: + quarter = values.NewInt(4) + } + + return quarter, nil +} diff --git a/pkg/stdlib/datetime/quarter_test.go b/pkg/stdlib/datetime/quarter_test.go new file mode 100644 index 00000000..3b29c7f2 --- /dev/null +++ b/pkg/stdlib/datetime/quarter_test.go @@ -0,0 +1,44 @@ +package datetime_test + +import ( + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateQuarter(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + } + + for month := time.January; month <= time.December; month++ { + tcs = append(tcs, &testCase{ + Name: "When " + month.String(), + Expected: values.NewInt(((int(month) - 1) / 3) + 1), + Args: []core.Value{ + values.NewDateTime(time.Date(1999, month, 1, 1, 1, 1, 1, time.Local)), + }, + }) + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateQuarter) + } +} From b8a40325acc7a4ea52493431a0d57b611ad180da Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 00:08:13 +0300 Subject: [PATCH 074/115] added DATE_DAYS_IN_MONTH function --- pkg/stdlib/datetime/daysinmonth.go | 49 +++++++++++++++++++ pkg/stdlib/datetime/daysinmonth_test.go | 62 +++++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 27 +++++------ pkg/stdlib/datetime/quarter.go | 2 +- 4 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 pkg/stdlib/datetime/daysinmonth.go create mode 100644 pkg/stdlib/datetime/daysinmonth_test.go diff --git a/pkg/stdlib/datetime/daysinmonth.go b/pkg/stdlib/datetime/daysinmonth.go new file mode 100644 index 00000000..1a1f7429 --- /dev/null +++ b/pkg/stdlib/datetime/daysinmonth.go @@ -0,0 +1,49 @@ +package datetime + +import ( + "context" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +var daysCount = map[time.Month]int{ + time.January: 31, + time.February: 28, + time.March: 31, + time.April: 30, + time.May: 31, + time.June: 30, + time.July: 30, + time.August: 31, + time.September: 30, + time.October: 31, + time.November: 30, + time.December: 31, +} + +// DateDaysInMonth returns the number of days in the month of date. +// @params date (DateTime) - source DateTime. +// @return (Int) - number of the days. +func DateDaysInMonth(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + dt := args[0].(values.DateTime) + month := dt.Month() + count := daysCount[month] + + if month == time.February && isLeap(dt.Year()) { + count++ + } + + return values.NewInt(count), nil +} diff --git a/pkg/stdlib/datetime/daysinmonth_test.go b/pkg/stdlib/datetime/daysinmonth_test.go new file mode 100644 index 00000000..8b05a3d1 --- /dev/null +++ b/pkg/stdlib/datetime/daysinmonth_test.go @@ -0,0 +1,62 @@ +package datetime_test + +import ( + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateDateDaysInMonth(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When Feb and a leap year", + Expected: values.NewInt(29), + Args: []core.Value{ + values.NewDateTime(time.Date(1972, time.February, 1, 1, 1, 1, 1, time.Local)), + }, + }, + &testCase{ + Name: "When Feb and not a leap year", + Expected: values.NewInt(28), + Args: []core.Value{ + values.NewDateTime(time.Date(1999, time.February, 1, 1, 1, 1, 1, time.Local)), + }, + }, + &testCase{ + Name: "When January", + Expected: values.NewInt(31), + Args: []core.Value{ + values.NewDateTime(time.Date(1999, time.January, 1, 1, 1, 1, 1, time.Local)), + }, + }, + &testCase{ + Name: "When November", + Expected: values.NewInt(30), + Args: []core.Value{ + values.NewDateTime(time.Date(1999, time.November, 1, 1, 1, 1, 1, time.Local)), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDaysInMonth) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index e0c68d9a..00de6563 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -4,18 +4,19 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "NOW": Now, - "DATE": Date, - "DATE_DAYOFWEEK": DateDayOfWeek, - "DATE_YEAR": DateYear, - "DATE_MONTH": DateMonth, - "DATE_DAY": DateDay, - "DATE_HOUR": DateHour, - "DATE_MINUTE": DateMinute, - "DATE_SECOND": DateSecond, - "DATE_MILLISECOND": DateMillisecond, - "DATE_DAYOFYEAR": DateDayOfYear, - "DATE_LEAPYEAR": DateLeapYear, - "DATE_QUARTER": DateQuarter, + "NOW": Now, + "DATE": Date, + "DATE_DAYOFWEEK": DateDayOfWeek, + "DATE_YEAR": DateYear, + "DATE_MONTH": DateMonth, + "DATE_DAY": DateDay, + "DATE_HOUR": DateHour, + "DATE_MINUTE": DateMinute, + "DATE_SECOND": DateSecond, + "DATE_MILLISECOND": DateMillisecond, + "DATE_DAYOFYEAR": DateDayOfYear, + "DATE_LEAPYEAR": DateLeapYear, + "DATE_QUARTER": DateQuarter, + "DATE_DAYS_IN_MONTH": DateDaysInMonth, } } diff --git a/pkg/stdlib/datetime/quarter.go b/pkg/stdlib/datetime/quarter.go index 849d8395..9d3fa520 100644 --- a/pkg/stdlib/datetime/quarter.go +++ b/pkg/stdlib/datetime/quarter.go @@ -8,7 +8,7 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) -// DateQuarter Return which quarter date belongs to. +// DateQuarter returns which quarter date belongs to. // @params date (DateTime) - source DateTime. // @return (Int) - a quarter number. func DateQuarter(_ context.Context, args ...core.Value) (core.Value, error) { From f249a59057acac742cabdfba79027f9e2aad7cf1 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 01:21:30 +0300 Subject: [PATCH 075/115] added DATE_FORMAT function --- pkg/stdlib/datetime/format.go | 33 +++++++++ pkg/stdlib/datetime/format_test.go | 109 +++++++++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 1 + 3 files changed, 143 insertions(+) create mode 100644 pkg/stdlib/datetime/format.go create mode 100644 pkg/stdlib/datetime/format_test.go diff --git a/pkg/stdlib/datetime/format.go b/pkg/stdlib/datetime/format.go new file mode 100644 index 00000000..a54afe33 --- /dev/null +++ b/pkg/stdlib/datetime/format.go @@ -0,0 +1,33 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateFormat format date according to the given format string. +// @params date (DateTime) - source DateTime object. +// @return (String) - formatted date. +func DateFormat(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, 2) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[1], core.StringType) + if err != nil { + return values.None, err + } + + date := args[0].(values.DateTime) + format := args[1].(values.String).String() + + return values.NewString(date.Format(format)), nil +} diff --git a/pkg/stdlib/datetime/format_test.go b/pkg/stdlib/datetime/format_test.go new file mode 100644 index 00000000..7f4149fd --- /dev/null +++ b/pkg/stdlib/datetime/format_test.go @@ -0,0 +1,109 @@ +package datetime_test + +import ( + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateFormat(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 2 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + values.NewArray(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When less than 2 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When first argument is wrong", + Expected: values.None, + Args: []core.Value{ + values.NewInt(0), + values.NewString(time.RFC822), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When second argument is wrong", + Expected: values.None, + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When DefaultTimeLayout", + Expected: values.NewString("1999-02-07T15:04:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewString(values.DefaultTimeLayout), + }, + }, + &testCase{ + Name: "When RFC3339Nano", + Expected: values.NewString("2018-11-05T00:54:15.000005125+03:00"), + Args: []core.Value{ + values.NewDateTime( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), + ), + values.NewString(time.RFC3339Nano), + }, + }, + &testCase{ + Name: "When custom format", + Expected: values.NewString("2018-11-05"), + Args: []core.Value{ + values.NewDateTime( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), + ), + values.NewString("2006-01-02"), + }, + }, + &testCase{ + Name: "When empty string", + Expected: values.NewString(""), + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewString(""), + }, + }, + &testCase{ + Name: "When random string without numbers", + Expected: values.NewString("qwerty"), + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewString("qwerty"), + }, + }, + &testCase{ + Name: "When random string with numbers", + Expected: values.NewString("qwerty2018uio"), + Args: []core.Value{ + values.NewDateTime( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), + ), + values.NewString("qwerty2006uio"), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateFormat) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 00de6563..558a0c0b 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -18,5 +18,6 @@ func NewLib() map[string]core.Function { "DATE_LEAPYEAR": DateLeapYear, "DATE_QUARTER": DateQuarter, "DATE_DAYS_IN_MONTH": DateDaysInMonth, + "DATE_FORMAT": DateFormat, } } From 2ff03ed88b4e679b033a230b320a61e1b36336ed Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 04:35:11 +0300 Subject: [PATCH 076/115] added -v flag into go test --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0d44accb..42376c5e 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ install: dep ensure test: - go test -race ${DIR_PKG}/... + go test -race -v ${DIR_PKG}/... cover: go test -race -coverprofile=coverage.txt -covermode=atomic ${DIR_PKG}/... From 23676c9d1c6e09c2f121085ed52335680fbcde24 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 18:28:09 +0300 Subject: [PATCH 077/115] update DATE_FORMAT test cases --- pkg/stdlib/datetime/format_test.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/stdlib/datetime/format_test.go b/pkg/stdlib/datetime/format_test.go index 7f4149fd..2e5dfc5d 100644 --- a/pkg/stdlib/datetime/format_test.go +++ b/pkg/stdlib/datetime/format_test.go @@ -56,8 +56,11 @@ func TestDateFormat(t *testing.T) { }, }, &testCase{ - Name: "When RFC3339Nano", - Expected: values.NewString("2018-11-05T00:54:15.000005125+03:00"), + Name: "When RFC3339Nano", + Expected: values.NewString( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local). + Format(time.RFC3339Nano), + ), Args: []core.Value{ values.NewDateTime( time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), @@ -66,8 +69,11 @@ func TestDateFormat(t *testing.T) { }, }, &testCase{ - Name: "When custom format", - Expected: values.NewString("2018-11-05"), + Name: "When custom format", + Expected: values.NewString( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local). + Format("2006-01-02"), + ), Args: []core.Value{ values.NewDateTime( time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), From d52893aefe6de8d3ab5293869428449712ff3a83 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 21:24:31 +0300 Subject: [PATCH 078/115] added one more test case --- pkg/stdlib/datetime/date_test.go | 6 ++++++ pkg/stdlib/datetime/day_test.go | 6 ++++++ pkg/stdlib/datetime/dayofweek_test.go | 6 ++++++ pkg/stdlib/datetime/dayofyear_test.go | 6 ++++++ pkg/stdlib/datetime/daysinmonth_test.go | 6 ++++++ pkg/stdlib/datetime/hour_test.go | 6 ++++++ pkg/stdlib/datetime/leapyear_test.go | 6 ++++++ pkg/stdlib/datetime/millisecond_test.go | 6 ++++++ pkg/stdlib/datetime/minute_test.go | 6 ++++++ pkg/stdlib/datetime/month_test.go | 6 ++++++ pkg/stdlib/datetime/quarter_test.go | 6 ++++++ pkg/stdlib/datetime/second_test.go | 6 ++++++ pkg/stdlib/datetime/year_test.go | 6 ++++++ 13 files changed, 78 insertions(+) diff --git a/pkg/stdlib/datetime/date_test.go b/pkg/stdlib/datetime/date_test.go index 221fcbb3..ab4298e3 100644 --- a/pkg/stdlib/datetime/date_test.go +++ b/pkg/stdlib/datetime/date_test.go @@ -26,6 +26,12 @@ func TestDate(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When incorrect timeStrings", Expected: values.None, diff --git a/pkg/stdlib/datetime/day_test.go b/pkg/stdlib/datetime/day_test.go index bb7ded3d..192ad8c0 100644 --- a/pkg/stdlib/datetime/day_test.go +++ b/pkg/stdlib/datetime/day_test.go @@ -25,6 +25,12 @@ func TestDateDay(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 7th day", Expected: values.NewInt(7), diff --git a/pkg/stdlib/datetime/dayofweek_test.go b/pkg/stdlib/datetime/dayofweek_test.go index 617cfef6..dcef7588 100644 --- a/pkg/stdlib/datetime/dayofweek_test.go +++ b/pkg/stdlib/datetime/dayofweek_test.go @@ -25,6 +25,12 @@ func TestDateDayOfWeek(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When Sunday (0th day)", Expected: values.NewInt(0), diff --git a/pkg/stdlib/datetime/dayofyear_test.go b/pkg/stdlib/datetime/dayofyear_test.go index a847befb..50c16f3f 100644 --- a/pkg/stdlib/datetime/dayofyear_test.go +++ b/pkg/stdlib/datetime/dayofyear_test.go @@ -26,6 +26,12 @@ func TestDateDayOfYear(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 38th day of the year", Expected: values.NewInt(38), diff --git a/pkg/stdlib/datetime/daysinmonth_test.go b/pkg/stdlib/datetime/daysinmonth_test.go index 8b05a3d1..ac5ab7d2 100644 --- a/pkg/stdlib/datetime/daysinmonth_test.go +++ b/pkg/stdlib/datetime/daysinmonth_test.go @@ -26,6 +26,12 @@ func TestDateDateDaysInMonth(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When Feb and a leap year", Expected: values.NewInt(29), diff --git a/pkg/stdlib/datetime/hour_test.go b/pkg/stdlib/datetime/hour_test.go index 1118c1ea..f1206415 100644 --- a/pkg/stdlib/datetime/hour_test.go +++ b/pkg/stdlib/datetime/hour_test.go @@ -25,6 +25,12 @@ func TestDateHour(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 7th hour", Expected: values.NewInt(7), diff --git a/pkg/stdlib/datetime/leapyear_test.go b/pkg/stdlib/datetime/leapyear_test.go index d25f2984..5ffaea54 100644 --- a/pkg/stdlib/datetime/leapyear_test.go +++ b/pkg/stdlib/datetime/leapyear_test.go @@ -25,6 +25,12 @@ func TestDateLeapYear(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When not a leap year", Expected: values.NewBoolean(false), diff --git a/pkg/stdlib/datetime/millisecond_test.go b/pkg/stdlib/datetime/millisecond_test.go index b8d42bf4..02ee66c0 100644 --- a/pkg/stdlib/datetime/millisecond_test.go +++ b/pkg/stdlib/datetime/millisecond_test.go @@ -26,6 +26,12 @@ func TestDateMillisecond(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 129 millisecond", Expected: values.NewInt(129), diff --git a/pkg/stdlib/datetime/minute_test.go b/pkg/stdlib/datetime/minute_test.go index f2fbbffd..46a6eff3 100644 --- a/pkg/stdlib/datetime/minute_test.go +++ b/pkg/stdlib/datetime/minute_test.go @@ -25,6 +25,12 @@ func TestDateMinute(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 4th minute", Expected: values.NewInt(4), diff --git a/pkg/stdlib/datetime/month_test.go b/pkg/stdlib/datetime/month_test.go index 56927880..c49e138e 100644 --- a/pkg/stdlib/datetime/month_test.go +++ b/pkg/stdlib/datetime/month_test.go @@ -25,6 +25,12 @@ func TestDateMonth(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 2th month", Expected: values.NewInt(2), diff --git a/pkg/stdlib/datetime/quarter_test.go b/pkg/stdlib/datetime/quarter_test.go index 3b29c7f2..96aca870 100644 --- a/pkg/stdlib/datetime/quarter_test.go +++ b/pkg/stdlib/datetime/quarter_test.go @@ -26,6 +26,12 @@ func TestDateQuarter(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, } for month := time.January; month <= time.December; month++ { diff --git a/pkg/stdlib/datetime/second_test.go b/pkg/stdlib/datetime/second_test.go index 64f194b4..c7e596c8 100644 --- a/pkg/stdlib/datetime/second_test.go +++ b/pkg/stdlib/datetime/second_test.go @@ -25,6 +25,12 @@ func TestDateSecond(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 5th second", Expected: values.NewInt(5), diff --git a/pkg/stdlib/datetime/year_test.go b/pkg/stdlib/datetime/year_test.go index 29b9ab0b..fbddcaa9 100644 --- a/pkg/stdlib/datetime/year_test.go +++ b/pkg/stdlib/datetime/year_test.go @@ -25,6 +25,12 @@ func TestDateYear(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 1999th year", Expected: values.NewInt(1999), From 94dc1c6c83213f7351e56c5badf159a604e3c64c Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:20:21 +0300 Subject: [PATCH 079/115] add helpers functions --- pkg/stdlib/datetime/helpers_test.go | 65 +++++++++++++++++++++++++++++ pkg/stdlib/datetime/now_test.go | 36 ++-------------- 2 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 pkg/stdlib/datetime/helpers_test.go diff --git a/pkg/stdlib/datetime/helpers_test.go b/pkg/stdlib/datetime/helpers_test.go new file mode 100644 index 00000000..1fe4b0b5 --- /dev/null +++ b/pkg/stdlib/datetime/helpers_test.go @@ -0,0 +1,65 @@ +package datetime_test + +import ( + "context" + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + + . "github.com/smartystreets/goconvey/convey" +) + +type testCase struct { + Name string + Expected core.Value + TimeArg time.Time + Args []core.Value + ShouldErr bool +} + +func (tc *testCase) Do(t *testing.T, fn core.Function) { + Convey(tc.Name, t, func() { + expected := tc.Expected + + actual, err := fn(context.Background(), tc.Args...) + + if tc.ShouldErr { + So(err, ShouldBeError) + expected = values.None + } else { + So(err, ShouldBeNil) + } + + So(actual.Compare(expected), ShouldEqual, 0) + }) +} + +func mustDefaultLayoutDt(timeString string) values.DateTime { + dt, err := defaultLayoutDt(timeString) + if err != nil { + panic(err) + } + return dt +} + +func mustLayoutDt(layout, value string) values.DateTime { + dt, err := layoutDt(layout, value) + if err != nil { + panic(err) + } + return dt +} + +func defaultLayoutDt(timeString string) (values.DateTime, error) { + return layoutDt(values.DefaultTimeLayout, timeString) +} + +func layoutDt(layout, value string) (values.DateTime, error) { + t, err := time.Parse(layout, value) + if err != nil { + return values.DateTime{}, err + } + return values.NewDateTime(t), nil +} diff --git a/pkg/stdlib/datetime/now_test.go b/pkg/stdlib/datetime/now_test.go index acc6d8e8..559b12b1 100644 --- a/pkg/stdlib/datetime/now_test.go +++ b/pkg/stdlib/datetime/now_test.go @@ -1,25 +1,14 @@ package datetime_test import ( - "context" "testing" - "time" - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/stdlib/datetime" - . "github.com/smartystreets/goconvey/convey" + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" ) -type testCase struct { - Name string - Expected core.Value - TimeArg time.Time - Args []core.Value - ShouldErr bool -} - func TestNow(t *testing.T) { tcs := []*testCase{ &testCase{ @@ -33,25 +22,6 @@ func TestNow(t *testing.T) { } for _, tc := range tcs { - tc.Do(t) + tc.Do(t, datetime.Now) } } - -func (tc *testCase) Do(t *testing.T) { - Convey(tc.Name, t, func() { - var expected core.Value - - expected = values.NewDateTime(tc.TimeArg) - - dt, err := datetime.Now(context.Background(), tc.Args...) - - if tc.ShouldErr { - So(err, ShouldBeError) - expected = values.None - } else { - So(err, ShouldBeNil) - } - - So(dt, ShouldEqual, expected) - }) -} From c2bae87282cdc3c2495570760f66f9b6e095727b Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:21:38 +0300 Subject: [PATCH 080/115] made values.DefaultTimeLayout public --- pkg/runtime/values/date_time.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/runtime/values/date_time.go b/pkg/runtime/values/date_time.go index 69981ce5..1ab4d585 100644 --- a/pkg/runtime/values/date_time.go +++ b/pkg/runtime/values/date_time.go @@ -7,7 +7,7 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/core" ) -const defaultTimeLayout = time.RFC3339 +const DefaultTimeLayout = time.RFC3339 type DateTime struct { time.Time @@ -26,7 +26,7 @@ func NewDateTime(time time.Time) DateTime { } func ParseDateTime(input interface{}) (DateTime, error) { - return ParseDateTimeWith(input, defaultTimeLayout) + return ParseDateTimeWith(input, DefaultTimeLayout) } func ParseDateTimeWith(input interface{}, layout string) (DateTime, error) { From d0172ed507756f3db9ac2ec6d60af7f317f91ec7 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:34:59 +0300 Subject: [PATCH 081/115] added DATE function --- pkg/stdlib/datetime/date.go | 33 +++++++++++++++++++++ pkg/stdlib/datetime/date_test.go | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 pkg/stdlib/datetime/date.go create mode 100644 pkg/stdlib/datetime/date_test.go diff --git a/pkg/stdlib/datetime/date.go b/pkg/stdlib/datetime/date.go new file mode 100644 index 00000000..ce7a8ae4 --- /dev/null +++ b/pkg/stdlib/datetime/date.go @@ -0,0 +1,33 @@ +package datetime + +import ( + "context" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// Date convert RFC3339 date time string to DateTime object. +// @params timeString (String) - string in RFC3339 format. +// @return (DateTime) - new DateTime object derived from timeString. +func Date(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.StringType) + if err != nil { + return values.None, err + } + + timeStrings := args[0].(values.String) + + t, err := time.Parse(values.DefaultTimeLayout, timeStrings.String()) + if err != nil { + return values.None, err + } + + return values.NewDateTime(t), nil +} diff --git a/pkg/stdlib/datetime/date_test.go b/pkg/stdlib/datetime/date_test.go new file mode 100644 index 00000000..221fcbb3 --- /dev/null +++ b/pkg/stdlib/datetime/date_test.go @@ -0,0 +1,49 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDate(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When incorrect timeStrings", + Expected: values.None, + Args: []core.Value{ + values.NewString("bla-bla"), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When correct timeString in RFC3339 format", + Expected: mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + Args: []core.Value{ + values.NewString("1999-02-07T15:04:05Z"), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.Date) + } +} From 86bcda76ae1344b82663d4827d1c633f6ec65c3b Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:36:03 +0300 Subject: [PATCH 082/115] added DATE_DAYOFWEEK function --- pkg/stdlib/datetime/dayofweek.go | 28 +++++++++++++++++ pkg/stdlib/datetime/dayofweek_test.go | 43 +++++++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 4 ++- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 pkg/stdlib/datetime/dayofweek.go create mode 100644 pkg/stdlib/datetime/dayofweek_test.go diff --git a/pkg/stdlib/datetime/dayofweek.go b/pkg/stdlib/datetime/dayofweek.go new file mode 100644 index 00000000..64b363c4 --- /dev/null +++ b/pkg/stdlib/datetime/dayofweek.go @@ -0,0 +1,28 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +// DateDayOfWeek returns number of the weekday from the date. Sunday is the 0th day of week. +// @params date (DateTime) - source DateTime. +// @return (Int) - return number of the weekday. +func DateDayOfWeek(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + wday := args[0].(values.DateTime).Weekday() + + return values.NewInt(int(wday)), nil +} diff --git a/pkg/stdlib/datetime/dayofweek_test.go b/pkg/stdlib/datetime/dayofweek_test.go new file mode 100644 index 00000000..617cfef6 --- /dev/null +++ b/pkg/stdlib/datetime/dayofweek_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateDayOfWeek(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When Sunday (0th day)", + Expected: values.NewInt(0), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When Monday (1th day)", + Expected: values.NewInt(1), + Args: []core.Value{mustDefaultLayoutDt("1999-02-08T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDayOfWeek) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index d3d3ea33..b398e909 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -4,6 +4,8 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "NOW": Now, + "NOW": Now, + "DATE": Date, + "DATE_DAYOFWEEK": DateDayOfWeek, } } From de0c703483548e99ae7cd2f5d1e20c9cc78d543e Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:36:39 +0300 Subject: [PATCH 083/115] added DATE_YEAR function --- pkg/stdlib/datetime/lib.go | 1 + pkg/stdlib/datetime/year.go | 27 ++++++++++++++++++++ pkg/stdlib/datetime/year_test.go | 43 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 pkg/stdlib/datetime/year.go create mode 100644 pkg/stdlib/datetime/year_test.go diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index b398e909..96e319f3 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -7,5 +7,6 @@ func NewLib() map[string]core.Function { "NOW": Now, "DATE": Date, "DATE_DAYOFWEEK": DateDayOfWeek, + "DATE_YEAR": DateYear, } } diff --git a/pkg/stdlib/datetime/year.go b/pkg/stdlib/datetime/year.go new file mode 100644 index 00000000..41b66dc6 --- /dev/null +++ b/pkg/stdlib/datetime/year.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateYear returns the year extracted from the given date. +// @params date (DateTime) - source DateTime. +// @return (Int) - a year number. +func DateYear(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + year := args[0].(values.DateTime).Year() + + return values.NewInt(year), nil +} diff --git a/pkg/stdlib/datetime/year_test.go b/pkg/stdlib/datetime/year_test.go new file mode 100644 index 00000000..29b9ab0b --- /dev/null +++ b/pkg/stdlib/datetime/year_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateYear(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 1999th year", + Expected: values.NewInt(1999), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 1629th year", + Expected: values.NewInt(1629), + Args: []core.Value{mustDefaultLayoutDt("1629-02-08T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateYear) + } +} From bd7378dec2667d7e590256243c976c83ac00bf05 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 1 Nov 2018 22:53:47 +0300 Subject: [PATCH 084/115] added DATE_MONTH function --- pkg/stdlib/datetime/lib.go | 1 + pkg/stdlib/datetime/month.go | 27 ++++++++++++++++++++++ pkg/stdlib/datetime/month_test.go | 38 +++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 pkg/stdlib/datetime/month.go create mode 100644 pkg/stdlib/datetime/month_test.go diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 96e319f3..31df3d5a 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -8,5 +8,6 @@ func NewLib() map[string]core.Function { "DATE": Date, "DATE_DAYOFWEEK": DateDayOfWeek, "DATE_YEAR": DateYear, + "DATE_MONTH": DateMonth, } } diff --git a/pkg/stdlib/datetime/month.go b/pkg/stdlib/datetime/month.go new file mode 100644 index 00000000..3633a87f --- /dev/null +++ b/pkg/stdlib/datetime/month.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateMonth returns the month of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a month number. +func DateMonth(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + month := args[0].(values.DateTime).Month() + + return values.NewInt(int(month)), nil +} diff --git a/pkg/stdlib/datetime/month_test.go b/pkg/stdlib/datetime/month_test.go new file mode 100644 index 00000000..ff192648 --- /dev/null +++ b/pkg/stdlib/datetime/month_test.go @@ -0,0 +1,38 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateMonth(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 2th month", + Expected: values.NewInt(2), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateMonth) + } +} From 6d6591e25c48857c643fb705adaec8881e9957a6 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 1 Nov 2018 23:02:04 +0300 Subject: [PATCH 085/115] added one more testCase for DATE_MONTH --- pkg/stdlib/datetime/month_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/stdlib/datetime/month_test.go b/pkg/stdlib/datetime/month_test.go index ff192648..56927880 100644 --- a/pkg/stdlib/datetime/month_test.go +++ b/pkg/stdlib/datetime/month_test.go @@ -30,6 +30,11 @@ func TestDateMonth(t *testing.T) { Expected: values.NewInt(2), Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, }, + &testCase{ + Name: "When 12th month", + Expected: values.NewInt(12), + Args: []core.Value{mustDefaultLayoutDt("1999-12-07T15:04:05Z")}, + }, } for _, tc := range tcs { From d027d3f1cef8e9dfd6045c76d616fd77560cd080 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 1 Nov 2018 23:02:26 +0300 Subject: [PATCH 086/115] added DATE_DAY function --- pkg/stdlib/datetime/day.go | 27 +++++++++++++++++++++ pkg/stdlib/datetime/day_test.go | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 pkg/stdlib/datetime/day.go create mode 100644 pkg/stdlib/datetime/day_test.go diff --git a/pkg/stdlib/datetime/day.go b/pkg/stdlib/datetime/day.go new file mode 100644 index 00000000..fe246d00 --- /dev/null +++ b/pkg/stdlib/datetime/day.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateDay returns the day of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a day number. +func DateDay(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + day := args[0].(values.DateTime).Day() + + return values.NewInt(day), nil +} diff --git a/pkg/stdlib/datetime/day_test.go b/pkg/stdlib/datetime/day_test.go new file mode 100644 index 00000000..bb7ded3d --- /dev/null +++ b/pkg/stdlib/datetime/day_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateDay(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 7th day", + Expected: values.NewInt(7), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 28th day", + Expected: values.NewInt(28), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDay) + } +} From 9725975434acdc5802c3c0af53ed0c08eb1fdceb Mon Sep 17 00:00:00 2001 From: = Date: Thu, 1 Nov 2018 23:12:22 +0300 Subject: [PATCH 087/115] added DateDay to lib --- pkg/stdlib/datetime/lib.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 31df3d5a..7bf130d7 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -9,5 +9,6 @@ func NewLib() map[string]core.Function { "DATE_DAYOFWEEK": DateDayOfWeek, "DATE_YEAR": DateYear, "DATE_MONTH": DateMonth, + "DATE_DAY": DateDay, } } From 0637fb0ab638c71ffb722770f6ebe568ebd54ecb Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 17:22:17 +0300 Subject: [PATCH 088/115] added DATE_HOUR, DATE_MINUTE and DATE_SECOND functions --- pkg/stdlib/datetime/hour.go | 27 +++++++++++++++++++ pkg/stdlib/datetime/hour_test.go | 43 ++++++++++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 3 +++ pkg/stdlib/datetime/minute.go | 27 +++++++++++++++++++ pkg/stdlib/datetime/minute_test.go | 43 ++++++++++++++++++++++++++++++ pkg/stdlib/datetime/second.go | 27 +++++++++++++++++++ pkg/stdlib/datetime/second_test.go | 43 ++++++++++++++++++++++++++++++ 7 files changed, 213 insertions(+) create mode 100644 pkg/stdlib/datetime/hour.go create mode 100644 pkg/stdlib/datetime/hour_test.go create mode 100644 pkg/stdlib/datetime/minute.go create mode 100644 pkg/stdlib/datetime/minute_test.go create mode 100644 pkg/stdlib/datetime/second.go create mode 100644 pkg/stdlib/datetime/second_test.go diff --git a/pkg/stdlib/datetime/hour.go b/pkg/stdlib/datetime/hour.go new file mode 100644 index 00000000..bb56e924 --- /dev/null +++ b/pkg/stdlib/datetime/hour.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateHour returns the hour of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a hour number. +func DateHour(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + hour := args[0].(values.DateTime).Hour() + + return values.NewInt(hour), nil +} diff --git a/pkg/stdlib/datetime/hour_test.go b/pkg/stdlib/datetime/hour_test.go new file mode 100644 index 00000000..1118c1ea --- /dev/null +++ b/pkg/stdlib/datetime/hour_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateHour(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 7th hour", + Expected: values.NewInt(7), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T07:04:05Z")}, + }, + &testCase{ + Name: "When 15th day", + Expected: values.NewInt(15), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateHour) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 7bf130d7..1fd5d27e 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -10,5 +10,8 @@ func NewLib() map[string]core.Function { "DATE_YEAR": DateYear, "DATE_MONTH": DateMonth, "DATE_DAY": DateDay, + "DATE_HOUR": DateHour, + "DATE_MINUTE": DateMinute, + "DATE_SECOND": DateSecond, } } diff --git a/pkg/stdlib/datetime/minute.go b/pkg/stdlib/datetime/minute.go new file mode 100644 index 00000000..c5f9fe5f --- /dev/null +++ b/pkg/stdlib/datetime/minute.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateMinute returns the minute of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a minute number. +func DateMinute(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + min := args[0].(values.DateTime).Minute() + + return values.NewInt(min), nil +} diff --git a/pkg/stdlib/datetime/minute_test.go b/pkg/stdlib/datetime/minute_test.go new file mode 100644 index 00000000..f2fbbffd --- /dev/null +++ b/pkg/stdlib/datetime/minute_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateMinute(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 4th minute", + Expected: values.NewInt(4), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 59th minute", + Expected: values.NewInt(59), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateMinute) + } +} diff --git a/pkg/stdlib/datetime/second.go b/pkg/stdlib/datetime/second.go new file mode 100644 index 00000000..ca3e2d18 --- /dev/null +++ b/pkg/stdlib/datetime/second.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateSecond returns the second of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a second number. +func DateSecond(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + sec := args[0].(values.DateTime).Second() + + return values.NewInt(sec), nil +} diff --git a/pkg/stdlib/datetime/second_test.go b/pkg/stdlib/datetime/second_test.go new file mode 100644 index 00000000..64f194b4 --- /dev/null +++ b/pkg/stdlib/datetime/second_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateSecond(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 5th second", + Expected: values.NewInt(5), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 59th second", + Expected: values.NewInt(59), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:59Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateSecond) + } +} From b79ab02d10cd85ae2575febd6adb06edad2c6800 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 18:54:33 +0300 Subject: [PATCH 089/115] added DATE_DAYOFYEAR, DATE_LEAPYEAR, DATE_MILLISECOND functions --- pkg/stdlib/datetime/dayofyear.go | 29 +++++++++++++ pkg/stdlib/datetime/dayofyear_test.go | 43 ++++++++++++++++++++ pkg/stdlib/datetime/leapyear.go | 31 ++++++++++++++ pkg/stdlib/datetime/leapyear_test.go | 43 ++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 21 +++++----- pkg/stdlib/datetime/millisecond.go | 27 +++++++++++++ pkg/stdlib/datetime/millisecond_test.go | 54 +++++++++++++++++++++++++ 7 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 pkg/stdlib/datetime/dayofyear.go create mode 100644 pkg/stdlib/datetime/dayofyear_test.go create mode 100644 pkg/stdlib/datetime/leapyear.go create mode 100644 pkg/stdlib/datetime/leapyear_test.go create mode 100644 pkg/stdlib/datetime/millisecond.go create mode 100644 pkg/stdlib/datetime/millisecond_test.go diff --git a/pkg/stdlib/datetime/dayofyear.go b/pkg/stdlib/datetime/dayofyear.go new file mode 100644 index 00000000..61218a3e --- /dev/null +++ b/pkg/stdlib/datetime/dayofyear.go @@ -0,0 +1,29 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +// DateDayOfYear returns the day of year number of date. +// The return value range from 1 to 365 (366 in a leap year). +// @params date (DateTime) - source DateTime. +// @return (Int) - a day of year number. +func DateDayOfYear(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + dayOfYear := args[0].(values.DateTime).YearDay() + + return values.NewInt(dayOfYear), nil +} diff --git a/pkg/stdlib/datetime/dayofyear_test.go b/pkg/stdlib/datetime/dayofyear_test.go new file mode 100644 index 00000000..7f640295 --- /dev/null +++ b/pkg/stdlib/datetime/dayofyear_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateDayOfYear(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 38th day of the year", + Expected: values.NewInt(38), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 59th day of the year", + Expected: values.NewInt(59), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDayOfYear) + } +} diff --git a/pkg/stdlib/datetime/leapyear.go b/pkg/stdlib/datetime/leapyear.go new file mode 100644 index 00000000..0bca5a24 --- /dev/null +++ b/pkg/stdlib/datetime/leapyear.go @@ -0,0 +1,31 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateLeapYear returns true if date is in a leap year else false. +// @params date (DateTime) - source DateTime. +// @return (Boolean) - date is in a leap year. +func DateLeapYear(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + year := args[0].(values.DateTime).Year() + + return values.NewBoolean(isLeap(year)), nil +} + +func isLeap(year int) bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) +} diff --git a/pkg/stdlib/datetime/leapyear_test.go b/pkg/stdlib/datetime/leapyear_test.go new file mode 100644 index 00000000..16b0ce38 --- /dev/null +++ b/pkg/stdlib/datetime/leapyear_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateLeapYear(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When a leap year", + Expected: values.NewBoolean(false), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When not a leap year", + Expected: values.NewBoolean(true), + Args: []core.Value{mustDefaultLayoutDt("1972-12-07T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateLeapYear) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 1fd5d27e..2feff7b4 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -4,14 +4,17 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "NOW": Now, - "DATE": Date, - "DATE_DAYOFWEEK": DateDayOfWeek, - "DATE_YEAR": DateYear, - "DATE_MONTH": DateMonth, - "DATE_DAY": DateDay, - "DATE_HOUR": DateHour, - "DATE_MINUTE": DateMinute, - "DATE_SECOND": DateSecond, + "NOW": Now, + "DATE": Date, + "DATE_DAYOFWEEK": DateDayOfWeek, + "DATE_YEAR": DateYear, + "DATE_MONTH": DateMonth, + "DATE_DAY": DateDay, + "DATE_HOUR": DateHour, + "DATE_MINUTE": DateMinute, + "DATE_SECOND": DateSecond, + "DATE_MILLISECOND": DateMillisecond, + "DATE_DAYOFYEAR": DateDayOfYear, + "DATE_LEAPYEAR": DateLeapYear, } } diff --git a/pkg/stdlib/datetime/millisecond.go b/pkg/stdlib/datetime/millisecond.go new file mode 100644 index 00000000..78aa679c --- /dev/null +++ b/pkg/stdlib/datetime/millisecond.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateMillisecond returns the millisecond of date as a number. +// @params date (DateTime) - source DateTime. +// @return (Int) - a millisecond number. +func DateMillisecond(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + msec := args[0].(values.DateTime).Nanosecond() / 1000000 + + return values.NewInt(msec), nil +} diff --git a/pkg/stdlib/datetime/millisecond_test.go b/pkg/stdlib/datetime/millisecond_test.go new file mode 100644 index 00000000..b8d42bf4 --- /dev/null +++ b/pkg/stdlib/datetime/millisecond_test.go @@ -0,0 +1,54 @@ +package datetime_test + +import ( + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateMillisecond(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 129 millisecond", + Expected: values.NewInt(129), + Args: []core.Value{ + values.NewDateTime(time.Date(2018, 11, 4, 17, 3, 12, 129410001, time.Local)), + }, + }, + &testCase{ + Name: "When 0 milliseconds [0]", + Expected: values.NewInt(0), + Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:59Z")}, + }, + // any nanosec < 1000000 equal to 0 milliseconds + &testCase{ + Name: "When 0 milliseconds [1]", + Expected: values.NewInt(0), + Args: []core.Value{ + values.NewDateTime(time.Date(0, 0, 0, 0, 0, 0, 999999, time.Local)), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateMillisecond) + } +} From 42fd22aefeeeb45be9e570af63f462a3fdfc9e84 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 19:24:46 +0300 Subject: [PATCH 090/115] fix names in tests --- pkg/stdlib/datetime/leapyear_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/stdlib/datetime/leapyear_test.go b/pkg/stdlib/datetime/leapyear_test.go index 16b0ce38..d25f2984 100644 --- a/pkg/stdlib/datetime/leapyear_test.go +++ b/pkg/stdlib/datetime/leapyear_test.go @@ -26,12 +26,12 @@ func TestDateLeapYear(t *testing.T) { ShouldErr: true, }, &testCase{ - Name: "When a leap year", + Name: "When not a leap year", Expected: values.NewBoolean(false), Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, }, &testCase{ - Name: "When not a leap year", + Name: "When a leap year", Expected: values.NewBoolean(true), Args: []core.Value{mustDefaultLayoutDt("1972-12-07T15:04:05Z")}, }, From 4224b9f5c1426a25c86b8bcff3c67da68553d9b5 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 19:29:10 +0300 Subject: [PATCH 091/115] one more case into dayofyear_test --- pkg/stdlib/datetime/dayofyear_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/stdlib/datetime/dayofyear_test.go b/pkg/stdlib/datetime/dayofyear_test.go index 7f640295..a847befb 100644 --- a/pkg/stdlib/datetime/dayofyear_test.go +++ b/pkg/stdlib/datetime/dayofyear_test.go @@ -2,6 +2,7 @@ package datetime_test import ( "testing" + "time" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" @@ -35,6 +36,13 @@ func TestDateDayOfYear(t *testing.T) { Expected: values.NewInt(59), Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:05Z")}, }, + &testCase{ + Name: "When 366th day of the year", + Expected: values.NewInt(366), + Args: []core.Value{ + values.NewDateTime(time.Date(1972, time.December, 31, 0, 0, 0, 0, time.Local)), + }, + }, } for _, tc := range tcs { From 0a43c229aa2f66ce9454d4d8ea9464375960f328 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 4 Nov 2018 23:29:41 +0300 Subject: [PATCH 092/115] added DATE_QUARTER function --- pkg/stdlib/datetime/lib.go | 1 + pkg/stdlib/datetime/quarter.go | 38 +++++++++++++++++++++++++ pkg/stdlib/datetime/quarter_test.go | 44 +++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 pkg/stdlib/datetime/quarter.go create mode 100644 pkg/stdlib/datetime/quarter_test.go diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 2feff7b4..e0c68d9a 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -16,5 +16,6 @@ func NewLib() map[string]core.Function { "DATE_MILLISECOND": DateMillisecond, "DATE_DAYOFYEAR": DateDayOfYear, "DATE_LEAPYEAR": DateLeapYear, + "DATE_QUARTER": DateQuarter, } } diff --git a/pkg/stdlib/datetime/quarter.go b/pkg/stdlib/datetime/quarter.go new file mode 100644 index 00000000..849d8395 --- /dev/null +++ b/pkg/stdlib/datetime/quarter.go @@ -0,0 +1,38 @@ +package datetime + +import ( + "context" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateQuarter Return which quarter date belongs to. +// @params date (DateTime) - source DateTime. +// @return (Int) - a quarter number. +func DateQuarter(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + month := args[0].(values.DateTime).Month() + quarter := values.NewInt(1) + + switch month { + case time.April, time.May, time.June: + quarter = values.NewInt(2) + case time.July, time.August, time.September: + quarter = values.NewInt(3) + case time.October, time.November, time.December: + quarter = values.NewInt(4) + } + + return quarter, nil +} diff --git a/pkg/stdlib/datetime/quarter_test.go b/pkg/stdlib/datetime/quarter_test.go new file mode 100644 index 00000000..3b29c7f2 --- /dev/null +++ b/pkg/stdlib/datetime/quarter_test.go @@ -0,0 +1,44 @@ +package datetime_test + +import ( + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateQuarter(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + } + + for month := time.January; month <= time.December; month++ { + tcs = append(tcs, &testCase{ + Name: "When " + month.String(), + Expected: values.NewInt(((int(month) - 1) / 3) + 1), + Args: []core.Value{ + values.NewDateTime(time.Date(1999, month, 1, 1, 1, 1, 1, time.Local)), + }, + }) + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateQuarter) + } +} From d9292ea6f556ccfa531a2bc5404865f2f9dab4aa Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 00:08:13 +0300 Subject: [PATCH 093/115] added DATE_DAYS_IN_MONTH function --- pkg/stdlib/datetime/daysinmonth.go | 49 +++++++++++++++++++ pkg/stdlib/datetime/daysinmonth_test.go | 62 +++++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 27 +++++------ pkg/stdlib/datetime/quarter.go | 2 +- 4 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 pkg/stdlib/datetime/daysinmonth.go create mode 100644 pkg/stdlib/datetime/daysinmonth_test.go diff --git a/pkg/stdlib/datetime/daysinmonth.go b/pkg/stdlib/datetime/daysinmonth.go new file mode 100644 index 00000000..1a1f7429 --- /dev/null +++ b/pkg/stdlib/datetime/daysinmonth.go @@ -0,0 +1,49 @@ +package datetime + +import ( + "context" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +var daysCount = map[time.Month]int{ + time.January: 31, + time.February: 28, + time.March: 31, + time.April: 30, + time.May: 31, + time.June: 30, + time.July: 30, + time.August: 31, + time.September: 30, + time.October: 31, + time.November: 30, + time.December: 31, +} + +// DateDaysInMonth returns the number of days in the month of date. +// @params date (DateTime) - source DateTime. +// @return (Int) - number of the days. +func DateDaysInMonth(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + dt := args[0].(values.DateTime) + month := dt.Month() + count := daysCount[month] + + if month == time.February && isLeap(dt.Year()) { + count++ + } + + return values.NewInt(count), nil +} diff --git a/pkg/stdlib/datetime/daysinmonth_test.go b/pkg/stdlib/datetime/daysinmonth_test.go new file mode 100644 index 00000000..8b05a3d1 --- /dev/null +++ b/pkg/stdlib/datetime/daysinmonth_test.go @@ -0,0 +1,62 @@ +package datetime_test + +import ( + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateDateDaysInMonth(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When Feb and a leap year", + Expected: values.NewInt(29), + Args: []core.Value{ + values.NewDateTime(time.Date(1972, time.February, 1, 1, 1, 1, 1, time.Local)), + }, + }, + &testCase{ + Name: "When Feb and not a leap year", + Expected: values.NewInt(28), + Args: []core.Value{ + values.NewDateTime(time.Date(1999, time.February, 1, 1, 1, 1, 1, time.Local)), + }, + }, + &testCase{ + Name: "When January", + Expected: values.NewInt(31), + Args: []core.Value{ + values.NewDateTime(time.Date(1999, time.January, 1, 1, 1, 1, 1, time.Local)), + }, + }, + &testCase{ + Name: "When November", + Expected: values.NewInt(30), + Args: []core.Value{ + values.NewDateTime(time.Date(1999, time.November, 1, 1, 1, 1, 1, time.Local)), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDaysInMonth) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index e0c68d9a..00de6563 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -4,18 +4,19 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "NOW": Now, - "DATE": Date, - "DATE_DAYOFWEEK": DateDayOfWeek, - "DATE_YEAR": DateYear, - "DATE_MONTH": DateMonth, - "DATE_DAY": DateDay, - "DATE_HOUR": DateHour, - "DATE_MINUTE": DateMinute, - "DATE_SECOND": DateSecond, - "DATE_MILLISECOND": DateMillisecond, - "DATE_DAYOFYEAR": DateDayOfYear, - "DATE_LEAPYEAR": DateLeapYear, - "DATE_QUARTER": DateQuarter, + "NOW": Now, + "DATE": Date, + "DATE_DAYOFWEEK": DateDayOfWeek, + "DATE_YEAR": DateYear, + "DATE_MONTH": DateMonth, + "DATE_DAY": DateDay, + "DATE_HOUR": DateHour, + "DATE_MINUTE": DateMinute, + "DATE_SECOND": DateSecond, + "DATE_MILLISECOND": DateMillisecond, + "DATE_DAYOFYEAR": DateDayOfYear, + "DATE_LEAPYEAR": DateLeapYear, + "DATE_QUARTER": DateQuarter, + "DATE_DAYS_IN_MONTH": DateDaysInMonth, } } diff --git a/pkg/stdlib/datetime/quarter.go b/pkg/stdlib/datetime/quarter.go index 849d8395..9d3fa520 100644 --- a/pkg/stdlib/datetime/quarter.go +++ b/pkg/stdlib/datetime/quarter.go @@ -8,7 +8,7 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) -// DateQuarter Return which quarter date belongs to. +// DateQuarter returns which quarter date belongs to. // @params date (DateTime) - source DateTime. // @return (Int) - a quarter number. func DateQuarter(_ context.Context, args ...core.Value) (core.Value, error) { From e71399cf07e5d843cbf7002baa1d92f00022ebee Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 01:21:30 +0300 Subject: [PATCH 094/115] added DATE_FORMAT function --- pkg/stdlib/datetime/format.go | 33 +++++++++ pkg/stdlib/datetime/format_test.go | 109 +++++++++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 1 + 3 files changed, 143 insertions(+) create mode 100644 pkg/stdlib/datetime/format.go create mode 100644 pkg/stdlib/datetime/format_test.go diff --git a/pkg/stdlib/datetime/format.go b/pkg/stdlib/datetime/format.go new file mode 100644 index 00000000..a54afe33 --- /dev/null +++ b/pkg/stdlib/datetime/format.go @@ -0,0 +1,33 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateFormat format date according to the given format string. +// @params date (DateTime) - source DateTime object. +// @return (String) - formatted date. +func DateFormat(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, 2) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[1], core.StringType) + if err != nil { + return values.None, err + } + + date := args[0].(values.DateTime) + format := args[1].(values.String).String() + + return values.NewString(date.Format(format)), nil +} diff --git a/pkg/stdlib/datetime/format_test.go b/pkg/stdlib/datetime/format_test.go new file mode 100644 index 00000000..7f4149fd --- /dev/null +++ b/pkg/stdlib/datetime/format_test.go @@ -0,0 +1,109 @@ +package datetime_test + +import ( + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateFormat(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 2 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + values.NewArray(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When less than 2 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When first argument is wrong", + Expected: values.None, + Args: []core.Value{ + values.NewInt(0), + values.NewString(time.RFC822), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When second argument is wrong", + Expected: values.None, + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When DefaultTimeLayout", + Expected: values.NewString("1999-02-07T15:04:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewString(values.DefaultTimeLayout), + }, + }, + &testCase{ + Name: "When RFC3339Nano", + Expected: values.NewString("2018-11-05T00:54:15.000005125+03:00"), + Args: []core.Value{ + values.NewDateTime( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), + ), + values.NewString(time.RFC3339Nano), + }, + }, + &testCase{ + Name: "When custom format", + Expected: values.NewString("2018-11-05"), + Args: []core.Value{ + values.NewDateTime( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), + ), + values.NewString("2006-01-02"), + }, + }, + &testCase{ + Name: "When empty string", + Expected: values.NewString(""), + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewString(""), + }, + }, + &testCase{ + Name: "When random string without numbers", + Expected: values.NewString("qwerty"), + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewString("qwerty"), + }, + }, + &testCase{ + Name: "When random string with numbers", + Expected: values.NewString("qwerty2018uio"), + Args: []core.Value{ + values.NewDateTime( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), + ), + values.NewString("qwerty2006uio"), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateFormat) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 00de6563..558a0c0b 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -18,5 +18,6 @@ func NewLib() map[string]core.Function { "DATE_LEAPYEAR": DateLeapYear, "DATE_QUARTER": DateQuarter, "DATE_DAYS_IN_MONTH": DateDaysInMonth, + "DATE_FORMAT": DateFormat, } } From 6fa267e344c2ef1d2a814b7bee48dc27b0226f31 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 04:35:11 +0300 Subject: [PATCH 095/115] added -v flag into go test --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0d44accb..42376c5e 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ install: dep ensure test: - go test -race ${DIR_PKG}/... + go test -race -v ${DIR_PKG}/... cover: go test -race -coverprofile=coverage.txt -covermode=atomic ${DIR_PKG}/... From 49375484ef9e6932cd2d131179d62750519f73ee Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Sun, 4 Nov 2018 23:21:03 -0500 Subject: [PATCH 096/115] Set codecov support for all branches --- Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile b/Makefile index 42376c5e..31dd5d04 100644 --- a/Makefile +++ b/Makefile @@ -26,13 +26,11 @@ test: cover: go test -race -coverprofile=coverage.txt -covermode=atomic ${DIR_PKG}/... -ifeq ($(TRAVIS_BRANCH)$(TRAVIS_PULL_REQUEST), masterfalse) ifneq ($(CODECOV_TOKEN), ) curl -s https://codecov.io/bash | bash else $(error "CODECOV_TOKEN token is required") endif -endif e2e: go run ${DIR_E2E}/main.go --tests ${DIR_E2E}/tests --pages ${DIR_E2E}/pages From d875c5535942d856691001a1a26f2b76c158ccc5 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 18:28:09 +0300 Subject: [PATCH 097/115] update DATE_FORMAT test cases --- pkg/stdlib/datetime/format_test.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/stdlib/datetime/format_test.go b/pkg/stdlib/datetime/format_test.go index 7f4149fd..2e5dfc5d 100644 --- a/pkg/stdlib/datetime/format_test.go +++ b/pkg/stdlib/datetime/format_test.go @@ -56,8 +56,11 @@ func TestDateFormat(t *testing.T) { }, }, &testCase{ - Name: "When RFC3339Nano", - Expected: values.NewString("2018-11-05T00:54:15.000005125+03:00"), + Name: "When RFC3339Nano", + Expected: values.NewString( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local). + Format(time.RFC3339Nano), + ), Args: []core.Value{ values.NewDateTime( time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), @@ -66,8 +69,11 @@ func TestDateFormat(t *testing.T) { }, }, &testCase{ - Name: "When custom format", - Expected: values.NewString("2018-11-05"), + Name: "When custom format", + Expected: values.NewString( + time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local). + Format("2006-01-02"), + ), Args: []core.Value{ values.NewDateTime( time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local), From 2e54e8c8ee58a36ff845b61d0ffad2ebd2e6ee92 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Mon, 5 Nov 2018 11:36:55 -0500 Subject: [PATCH 098/115] Updated codecov settings --- .codecov.yml | 21 +++++++++++++++++++++ Makefile | 6 +----- 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 .codecov.yml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..bab3d4d9 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,21 @@ +ignore: +# Ignore coverage for generated code. +- "pkg/parser" + +coverage: + range: 70..100 + round: nearest + precision: 1 + + status: + project: + default: + enabled: yes + threshold: 2% + patch: no + changes: no + +comment: + layout: "header, diff" + behavior: once + require_changes: yes \ No newline at end of file diff --git a/Makefile b/Makefile index 31dd5d04..6082f83f 100644 --- a/Makefile +++ b/Makefile @@ -25,12 +25,8 @@ test: go test -race -v ${DIR_PKG}/... cover: - go test -race -coverprofile=coverage.txt -covermode=atomic ${DIR_PKG}/... -ifneq ($(CODECOV_TOKEN), ) + go test -race -coverprofile=coverage.txt -covermode=atomic ${DIR_PKG}/... && \ curl -s https://codecov.io/bash | bash -else - $(error "CODECOV_TOKEN token is required") -endif e2e: go run ${DIR_E2E}/main.go --tests ${DIR_E2E}/tests --pages ${DIR_E2E}/pages From c34b85230c00764c52899015c620f8cc2e7ae0b7 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Mon, 5 Nov 2018 11:45:33 -0500 Subject: [PATCH 099/115] Added panic recovery mechanism (#158) --- pkg/compiler/compiler.go | 6 +++--- pkg/runtime/program.go | 19 ++++++++++++++++++- pkg/runtime/program_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 pkg/runtime/program_test.go diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index df445299..f9907d53 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -59,9 +59,6 @@ func (c *FqlCompiler) Compile(query string) (program *runtime.Program, err error return nil, ErrEmptyQuery } - p := parser.New(query) - p.AddErrorListener(&errorListener{}) - defer func() { if r := recover(); r != nil { // find out exactly what the error was and set err @@ -78,6 +75,9 @@ func (c *FqlCompiler) Compile(query string) (program *runtime.Program, err error } }() + p := parser.New(query) + p.AddErrorListener(&errorListener{}) + l := newVisitor(query, c.funcs) res := p.Visit(l).(*result) diff --git a/pkg/runtime/program.go b/pkg/runtime/program.go index 6e7c8fca..c2f1de25 100644 --- a/pkg/runtime/program.go +++ b/pkg/runtime/program.go @@ -5,6 +5,7 @@ import ( "github.com/MontFerret/ferret/pkg/html" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/pkg/errors" ) type Program struct { @@ -28,7 +29,23 @@ func (p *Program) Source() string { return p.src } -func (p *Program) Run(ctx context.Context, setters ...Option) ([]byte, error) { +func (p *Program) Run(ctx context.Context, setters ...Option) (result []byte, err error) { + defer func() { + if r := recover(); r != nil { + // find out exactly what the error was and set err + switch x := r.(type) { + case string: + err = errors.New(x) + case error: + err = x + default: + err = errors.New("unknown panic") + } + + result = nil + } + }() + scope, closeFn := core.NewRootScope() defer closeFn() diff --git a/pkg/runtime/program_test.go b/pkg/runtime/program_test.go new file mode 100644 index 00000000..00c70324 --- /dev/null +++ b/pkg/runtime/program_test.go @@ -0,0 +1,25 @@ +package runtime_test + +import ( + "context" + "github.com/MontFerret/ferret/pkg/compiler" + "github.com/MontFerret/ferret/pkg/runtime/core" + . "github.com/smartystreets/goconvey/convey" + "testing" +) + +func TestProgram(t *testing.T) { + Convey("Should recover from panic", t, func() { + c := compiler.New() + c.RegisterFunction("panic", func(ctx context.Context, args ...core.Value) (core.Value, error) { + panic("test") + }) + + p := c.MustCompile(`RETURN PANIC()`) + + _, err := p.Run(context.Background()) + + So(err, ShouldBeError) + So(err.Error(), ShouldEqual, "test") + }) +} From a2bd798dd739184550be204be2a56acc80b39d44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 5 Nov 2018 12:46:57 -0500 Subject: [PATCH 100/115] Bump github.com/mafredri/cdp from 0.19.0 to 0.20.0 (#159) Bumps [github.com/mafredri/cdp](https://github.com/mafredri/cdp) from 0.19.0 to 0.20.0. - [Release notes](https://github.com/mafredri/cdp/releases) - [Commits](https://github.com/mafredri/cdp/compare/v0.19.0...v0.20.0) Signed-off-by: dependabot[bot] --- Gopkg.lock | 7 +++---- Gopkg.toml | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 4eaf26b9..69584c69 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -93,13 +93,12 @@ version = "v0.2.7" [[projects]] - digest = "1:7ffdd69928c5153fc351132aa80bbcc18a8f8122de1ba592cf42dccb65732361" + digest = "1:612ebc66cfa7484088f90ab86c7f1eb3d7696cf38ec2451c63f8d6008ff7625f" name = "github.com/mafredri/cdp" packages = [ ".", "devtool", "internal/errors", - "protocol", "protocol/accessibility", "protocol/animation", "protocol/applicationcache", @@ -145,8 +144,8 @@ "session", ] pruneopts = "UT" - revision = "75b0ecc5efcff27ac756a33ec71f0db75dc3d21c" - version = "v0.19.0" + revision = "0b30a3d15c28f2c8f028f99009cfba0b19baf126" + version = "v0.20.0" [[projects]] digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67" diff --git a/Gopkg.toml b/Gopkg.toml index 5ae873d6..45078f5f 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -35,7 +35,7 @@ [[constraint]] name = "github.com/mafredri/cdp" - version = "0.19.0" + version = "0.20.0" [[constraint]] name = "github.com/chzyer/readline" From 51427058620e433df1fc1b20587d18fc266369d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 5 Nov 2018 13:23:47 -0500 Subject: [PATCH 101/115] Bump github.com/gofrs/uuid from 3.1.1 to 3.1.2 (#160) Bumps [github.com/gofrs/uuid](https://github.com/gofrs/uuid) from 3.1.1 to 3.1.2. - [Release notes](https://github.com/gofrs/uuid/releases) - [Commits](https://github.com/gofrs/uuid/compare/v3.1.1...v3.1.2) Signed-off-by: dependabot[bot] --- Gopkg.lock | 6 +++--- Gopkg.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 69584c69..15398912 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -42,12 +42,12 @@ revision = "2b8494104d86337cdd41d0a49cbed8e4583c0ab4" [[projects]] - digest = "1:ce579162ae1341f3e5ab30c0dce767f28b1eb6a81359aad01723f1ba6b4becdf" + digest = "1:c594a691090b434d55c67f6cc8e326ef5ba49452abc059821bd5d4fd4cdef08c" name = "github.com/gofrs/uuid" packages = ["."] pruneopts = "UT" - revision = "370558f003bfe29580cd0f698d8640daccdcc45c" - version = "v3.1.1" + revision = "7077aa61129615a0d7f45c49101cd011ab221c27" + version = "v3.1.2" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index 45078f5f..f4426cdb 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -47,4 +47,4 @@ [[constraint]] name = "github.com/gofrs/uuid" - version = "3.1.1" \ No newline at end of file + version = "3.1.2" \ No newline at end of file From e5d0d4cc53c23bb156465af4850262c09e5fe30e Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 21:24:31 +0300 Subject: [PATCH 102/115] added one more test case --- pkg/stdlib/datetime/date_test.go | 6 ++++++ pkg/stdlib/datetime/day_test.go | 6 ++++++ pkg/stdlib/datetime/dayofweek_test.go | 6 ++++++ pkg/stdlib/datetime/dayofyear_test.go | 6 ++++++ pkg/stdlib/datetime/daysinmonth_test.go | 6 ++++++ pkg/stdlib/datetime/hour_test.go | 6 ++++++ pkg/stdlib/datetime/leapyear_test.go | 6 ++++++ pkg/stdlib/datetime/millisecond_test.go | 6 ++++++ pkg/stdlib/datetime/minute_test.go | 6 ++++++ pkg/stdlib/datetime/month_test.go | 6 ++++++ pkg/stdlib/datetime/quarter_test.go | 6 ++++++ pkg/stdlib/datetime/second_test.go | 6 ++++++ pkg/stdlib/datetime/year_test.go | 6 ++++++ 13 files changed, 78 insertions(+) diff --git a/pkg/stdlib/datetime/date_test.go b/pkg/stdlib/datetime/date_test.go index 221fcbb3..ab4298e3 100644 --- a/pkg/stdlib/datetime/date_test.go +++ b/pkg/stdlib/datetime/date_test.go @@ -26,6 +26,12 @@ func TestDate(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When incorrect timeStrings", Expected: values.None, diff --git a/pkg/stdlib/datetime/day_test.go b/pkg/stdlib/datetime/day_test.go index bb7ded3d..192ad8c0 100644 --- a/pkg/stdlib/datetime/day_test.go +++ b/pkg/stdlib/datetime/day_test.go @@ -25,6 +25,12 @@ func TestDateDay(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 7th day", Expected: values.NewInt(7), diff --git a/pkg/stdlib/datetime/dayofweek_test.go b/pkg/stdlib/datetime/dayofweek_test.go index 617cfef6..dcef7588 100644 --- a/pkg/stdlib/datetime/dayofweek_test.go +++ b/pkg/stdlib/datetime/dayofweek_test.go @@ -25,6 +25,12 @@ func TestDateDayOfWeek(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When Sunday (0th day)", Expected: values.NewInt(0), diff --git a/pkg/stdlib/datetime/dayofyear_test.go b/pkg/stdlib/datetime/dayofyear_test.go index a847befb..50c16f3f 100644 --- a/pkg/stdlib/datetime/dayofyear_test.go +++ b/pkg/stdlib/datetime/dayofyear_test.go @@ -26,6 +26,12 @@ func TestDateDayOfYear(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 38th day of the year", Expected: values.NewInt(38), diff --git a/pkg/stdlib/datetime/daysinmonth_test.go b/pkg/stdlib/datetime/daysinmonth_test.go index 8b05a3d1..ac5ab7d2 100644 --- a/pkg/stdlib/datetime/daysinmonth_test.go +++ b/pkg/stdlib/datetime/daysinmonth_test.go @@ -26,6 +26,12 @@ func TestDateDateDaysInMonth(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When Feb and a leap year", Expected: values.NewInt(29), diff --git a/pkg/stdlib/datetime/hour_test.go b/pkg/stdlib/datetime/hour_test.go index 1118c1ea..f1206415 100644 --- a/pkg/stdlib/datetime/hour_test.go +++ b/pkg/stdlib/datetime/hour_test.go @@ -25,6 +25,12 @@ func TestDateHour(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 7th hour", Expected: values.NewInt(7), diff --git a/pkg/stdlib/datetime/leapyear_test.go b/pkg/stdlib/datetime/leapyear_test.go index d25f2984..5ffaea54 100644 --- a/pkg/stdlib/datetime/leapyear_test.go +++ b/pkg/stdlib/datetime/leapyear_test.go @@ -25,6 +25,12 @@ func TestDateLeapYear(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When not a leap year", Expected: values.NewBoolean(false), diff --git a/pkg/stdlib/datetime/millisecond_test.go b/pkg/stdlib/datetime/millisecond_test.go index b8d42bf4..02ee66c0 100644 --- a/pkg/stdlib/datetime/millisecond_test.go +++ b/pkg/stdlib/datetime/millisecond_test.go @@ -26,6 +26,12 @@ func TestDateMillisecond(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 129 millisecond", Expected: values.NewInt(129), diff --git a/pkg/stdlib/datetime/minute_test.go b/pkg/stdlib/datetime/minute_test.go index f2fbbffd..46a6eff3 100644 --- a/pkg/stdlib/datetime/minute_test.go +++ b/pkg/stdlib/datetime/minute_test.go @@ -25,6 +25,12 @@ func TestDateMinute(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 4th minute", Expected: values.NewInt(4), diff --git a/pkg/stdlib/datetime/month_test.go b/pkg/stdlib/datetime/month_test.go index 56927880..c49e138e 100644 --- a/pkg/stdlib/datetime/month_test.go +++ b/pkg/stdlib/datetime/month_test.go @@ -25,6 +25,12 @@ func TestDateMonth(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 2th month", Expected: values.NewInt(2), diff --git a/pkg/stdlib/datetime/quarter_test.go b/pkg/stdlib/datetime/quarter_test.go index 3b29c7f2..96aca870 100644 --- a/pkg/stdlib/datetime/quarter_test.go +++ b/pkg/stdlib/datetime/quarter_test.go @@ -26,6 +26,12 @@ func TestDateQuarter(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, } for month := time.January; month <= time.December; month++ { diff --git a/pkg/stdlib/datetime/second_test.go b/pkg/stdlib/datetime/second_test.go index 64f194b4..c7e596c8 100644 --- a/pkg/stdlib/datetime/second_test.go +++ b/pkg/stdlib/datetime/second_test.go @@ -25,6 +25,12 @@ func TestDateSecond(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 5th second", Expected: values.NewInt(5), diff --git a/pkg/stdlib/datetime/year_test.go b/pkg/stdlib/datetime/year_test.go index 29b9ab0b..fbddcaa9 100644 --- a/pkg/stdlib/datetime/year_test.go +++ b/pkg/stdlib/datetime/year_test.go @@ -25,6 +25,12 @@ func TestDateYear(t *testing.T) { Args: []core.Value{}, ShouldErr: true, }, + &testCase{ + Name: "When argument isn't DateTime", + Expected: values.None, + Args: []core.Value{values.NewInt(0)}, + ShouldErr: true, + }, &testCase{ Name: "When 1999th year", Expected: values.NewInt(1999), From 479426618ae81852696b62555dc61f883863c8d7 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 21:48:43 +0300 Subject: [PATCH 103/115] sorter instead Compare now --- pkg/runtime/values/array.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/runtime/values/array.go b/pkg/runtime/values/array.go index 813ced8b..276fe7b2 100644 --- a/pkg/runtime/values/array.go +++ b/pkg/runtime/values/array.go @@ -236,7 +236,7 @@ func (t *Array) SortWith(sorter ArraySorter) *Array { copy(c, t.items) sort.SliceStable(c, func(i, j int) bool { - return c[i].Compare(c[j]) == -1 + return sorter(c[i], c[j]) }) res := new(Array) From a94ca6e9778a314f37d0c486b3337492440bbeab Mon Sep 17 00:00:00 2001 From: = Date: Mon, 5 Nov 2018 23:07:52 +0300 Subject: [PATCH 104/115] rename utils.LOG -> utils.PRINT --- pkg/stdlib/utils/lib.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/stdlib/utils/lib.go b/pkg/stdlib/utils/lib.go index cfb54a54..10c676d7 100644 --- a/pkg/stdlib/utils/lib.go +++ b/pkg/stdlib/utils/lib.go @@ -4,7 +4,7 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "WAIT": Wait, - "LOG": Log, + "WAIT": Wait, + "PRINT": Log, } } From b212b06ad154566b438de16e59689f828d8f74c6 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 6 Nov 2018 00:58:54 +0300 Subject: [PATCH 105/115] rename utils.Logs -> utils.Print --- pkg/stdlib/utils/lib.go | 2 +- pkg/stdlib/utils/log.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/stdlib/utils/lib.go b/pkg/stdlib/utils/lib.go index 10c676d7..c31fdd06 100644 --- a/pkg/stdlib/utils/lib.go +++ b/pkg/stdlib/utils/lib.go @@ -5,6 +5,6 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ "WAIT": Wait, - "PRINT": Log, + "PRINT": Print, } } diff --git a/pkg/stdlib/utils/log.go b/pkg/stdlib/utils/log.go index 8b152bc6..96e1d26f 100644 --- a/pkg/stdlib/utils/log.go +++ b/pkg/stdlib/utils/log.go @@ -8,8 +8,8 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/values" ) -// Log writes messages into the system log. -func Log(ctx context.Context, args ...core.Value) (core.Value, error) { +// Print writes messages into the system log. +func Print(ctx context.Context, args ...core.Value) (core.Value, error) { err := core.ValidateArgs(args, 1, core.MaxArgs) if err != nil { From 0d0c4f3f7316050a0a99476b320efcf64355e6f7 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 7 Nov 2018 02:13:24 +0300 Subject: [PATCH 106/115] added DATE_ADD, DATE_SUBTRACT functions --- pkg/runtime/core/value.go | 32 ++++ pkg/stdlib/datetime/add_subtract.go | 121 ++++++++++++ pkg/stdlib/datetime/add_subtract_test.go | 222 +++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 2 + 4 files changed, 377 insertions(+) create mode 100644 pkg/stdlib/datetime/add_subtract.go create mode 100644 pkg/stdlib/datetime/add_subtract_test.go diff --git a/pkg/runtime/core/value.go b/pkg/runtime/core/value.go index 79afba77..7b0812c7 100644 --- a/pkg/runtime/core/value.go +++ b/pkg/runtime/core/value.go @@ -2,6 +2,8 @@ package core import ( "encoding/json" + + "github.com/pkg/errors" ) //revive:disable-next-line redefines-builtin-id @@ -39,6 +41,8 @@ func (t Type) String() string { return typestr[t] } +// Value represents an interface of +// any type that needs to be used during runtime type Value interface { json.Marshaler Type() Type @@ -49,10 +53,15 @@ type Value interface { Copy() Value } +// IsTypeOf return true when value's type +// is equal to check type. +// Returns false, otherwise. func IsTypeOf(value Value, check Type) bool { return value.Type() == check } +// ValidateType checks the match of +// value's type and required types. func ValidateType(value Value, required ...Type) error { var valid bool ct := value.Type() @@ -70,3 +79,26 @@ func ValidateType(value Value, required ...Type) error { return nil } + +// PairValueType is a supporting +// structure that used in validateValueTypePairs. +type PairValueType struct { + Value Value + Types []Type +} + +// ValidateValueTypePairs validate pairs of +// Values and Types. +// Returns error when type didn't match +func ValidateValueTypePairs(pairs ...PairValueType) error { + var err error + + for idx, pair := range pairs { + err = ValidateType(pair.Value, pair.Types...) + if err != nil { + return errors.Errorf("pair %d: %v", idx, err) + } + } + + return nil +} diff --git a/pkg/stdlib/datetime/add_subtract.go b/pkg/stdlib/datetime/add_subtract.go new file mode 100644 index 00000000..b3eeca26 --- /dev/null +++ b/pkg/stdlib/datetime/add_subtract.go @@ -0,0 +1,121 @@ +package datetime + +import ( + "context" + "strings" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/pkg/errors" +) + +var ( + sliceDateTime = []core.Type{core.DateTimeType} + sliceIntType = []core.Type{core.IntType} + sliceStringType = []core.Type{core.StringType} + + emptyDateTime values.DateTime + emptyInt values.Int + emptyString values.String +) + +// DateAdd add amount given in unit to date. +// @params date (DateTime) - source date. +// @params amount (Int) - amount of units +// @params unit (String) - unit. +// @return (DateTime) - calculated date. +// The following units are available: +// * y, year, year +// * m, month, months +// * w, week, weeks +// * d, day, days +// * h, hour, hours +// * i, minute, minutes +// * s, second, seconds +// * f, millisecond, milliseconds +func DateAdd(_ context.Context, args ...core.Value) (core.Value, error) { + date, amount, unit, err := getArgs(args) + if err != nil { + return values.None, err + } + + dt, err := addUnit(date, int(amount), unit.String()) + if err != nil { + return values.None, err + } + + return dt, nil +} + +// DateSubtract subtract amount given in unit to date. +// @params date (DateTime) - source date. +// @params amount (Int) - amount of units +// @params unit (String) - unit. +// @return (DateTime) - calculated date. +// The following units are available: +// * y, year, year +// * m, month, months +// * w, week, weeks +// * d, day, days +// * h, hour, hours +// * i, minute, minutes +// * s, second, seconds +// * f, millisecond, milliseconds +func DateSubtract(_ context.Context, args ...core.Value) (core.Value, error) { + date, amount, unit, err := getArgs(args) + if err != nil { + return values.None, err + } + + dt, err := addUnit(date, -1*int(amount), unit.String()) + if err != nil { + return values.None, err + } + + return dt, nil +} + +func getArgs(args []core.Value) (values.DateTime, values.Int, values.String, error) { + err := core.ValidateArgs(args, 3, 3) + if err != nil { + return emptyDateTime, emptyInt, emptyString, err + } + + err = core.ValidateValueTypePairs( + core.PairValueType{args[0], sliceDateTime}, + core.PairValueType{args[1], sliceIntType}, + core.PairValueType{args[2], sliceStringType}, + ) + if err != nil { + return emptyDateTime, emptyInt, emptyString, err + } + + date := args[0].(values.DateTime) + amount := args[1].(values.Int) + unit := args[2].(values.String) + + return date, amount, unit, nil +} + +func addUnit(dt values.DateTime, amount int, unit string) (values.DateTime, error) { + switch strings.ToLower(unit) { + case "y", "year", "years": + return values.NewDateTime(dt.AddDate(amount*1, 0, 0)), nil + case "m", "month", "months": + return values.NewDateTime(dt.AddDate(0, amount*1, 0)), nil + case "w", "week", "weeks": + return values.NewDateTime(dt.AddDate(0, 0, amount*7)), nil + case "d", "day", "days": + return values.NewDateTime(dt.AddDate(0, 0, amount*1)), nil + case "h", "hour", "hours": + return values.NewDateTime(dt.Add(time.Hour)), nil + case "i", "minute", "minutes": + return values.NewDateTime(dt.Add(time.Minute)), nil + case "s", "second", "seconds": + return values.NewDateTime(dt.Add(time.Second)), nil + case "f", "millisecond", "milliseconds": + return values.NewDateTime(dt.Add(time.Millisecond)), nil + } + return values.DateTime{}, errors.Errorf("no such unit '%s'", unit) +} diff --git a/pkg/stdlib/datetime/add_subtract_test.go b/pkg/stdlib/datetime/add_subtract_test.go new file mode 100644 index 00000000..8a6e1015 --- /dev/null +++ b/pkg/stdlib/datetime/add_subtract_test.go @@ -0,0 +1,222 @@ +package datetime_test + +import ( + "context" + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +var ( + utcLoc, _ = time.LoadLocation("UTC") +) + +func TestDateAdd(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 3 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewInt(0), + values.NewInt(0), + values.NewInt(0), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When less than 3 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When incorrect arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("bla-bla"), + values.NewInt(0), + values.NewString("be-be"), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When wrong unit given", + Expected: values.None, + Args: []core.Value{ + mustLayoutDt("2006-01-02", "1999-02-07"), + values.NewInt(5), + values.NewString("not_exist"), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When argument have correct types", + Expected: func() core.Value { + expected, _ := datetime.DateAdd( + context.Background(), + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(1), + values.NewString("day"), + ) + return expected + }(), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(1), + values.NewString("day"), + }, + }, + &testCase{ + Name: "-1 day", + Expected: mustDefaultLayoutDt("1999-02-06T15:04:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(-1), + values.NewString("day"), + }, + }, + &testCase{ + Name: "+3 months", + Expected: mustDefaultLayoutDt("1999-05-07T15:04:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(3), + values.NewString("months"), + }, + }, + &testCase{ + Name: "+5 years", + Expected: mustLayoutDt("2006-01-02", "2004-02-07"), + Args: []core.Value{ + mustLayoutDt("2006-01-02", "1999-02-07"), + values.NewInt(5), + values.NewString("y"), + }, + }, + &testCase{ + Name: "1999 minus 2000 years", + Expected: values.NewDateTime( + time.Date(-1, 2, 7, 0, 0, 0, 0, utcLoc), + ), + Args: []core.Value{ + mustLayoutDt("2006-01-02", "1999-02-07"), + values.NewInt(-2000), + values.NewString("year"), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateAdd) + } +} + +func TestDateSubtract(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 3 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewInt(0), + values.NewInt(0), + values.NewInt(0), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When less than 3 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When incorrect arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("bla-bla"), + values.NewInt(0), + values.NewString("be-be"), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When wrong unit given", + Expected: values.None, + Args: []core.Value{ + mustLayoutDt("2006-01-02", "1999-02-07"), + values.NewInt(5), + values.NewString("not_exist"), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When argument have correct types", + Expected: func() core.Value { + expected, _ := datetime.DateSubtract( + context.Background(), + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(1), + values.NewString("day"), + ) + return expected + }(), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(1), + values.NewString("day"), + }, + }, + &testCase{ + Name: "-1 day", + Expected: mustDefaultLayoutDt("1999-02-08T15:04:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(-1), + values.NewString("day"), + }, + }, + &testCase{ + Name: "+3 months", + Expected: mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-05-07T15:04:05Z"), + values.NewInt(3), + values.NewString("months"), + }, + }, + &testCase{ + Name: "+5 years", + Expected: mustLayoutDt("2006-01-02", "1994-02-07"), + Args: []core.Value{ + mustLayoutDt("2006-01-02", "1999-02-07"), + values.NewInt(5), + values.NewString("y"), + }, + }, + &testCase{ + Name: "1999 minus 2000 years", + Expected: values.NewDateTime( + time.Date(-1, 2, 7, 0, 0, 0, 0, utcLoc), + ), + Args: []core.Value{ + mustLayoutDt("2006-01-02", "1999-02-07"), + values.NewInt(2000), + values.NewString("year"), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateSubtract) + } +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index 558a0c0b..a365b016 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -19,5 +19,7 @@ func NewLib() map[string]core.Function { "DATE_QUARTER": DateQuarter, "DATE_DAYS_IN_MONTH": DateDaysInMonth, "DATE_FORMAT": DateFormat, + "DATE_ADD": DateAdd, + "DATE_SUBTRACT": DateSubtract, } } From 89033df827fa3e97d854b05005c3fbf7b4f80047 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 7 Nov 2018 21:14:19 +0300 Subject: [PATCH 107/115] use keyed fields now --- pkg/stdlib/datetime/add_subtract.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/stdlib/datetime/add_subtract.go b/pkg/stdlib/datetime/add_subtract.go index b3eeca26..777641ba 100644 --- a/pkg/stdlib/datetime/add_subtract.go +++ b/pkg/stdlib/datetime/add_subtract.go @@ -83,9 +83,9 @@ func getArgs(args []core.Value) (values.DateTime, values.Int, values.String, err } err = core.ValidateValueTypePairs( - core.PairValueType{args[0], sliceDateTime}, - core.PairValueType{args[1], sliceIntType}, - core.PairValueType{args[2], sliceStringType}, + core.PairValueType{Value: args[0], Types: sliceDateTime}, + core.PairValueType{Value: args[1], Types: sliceIntType}, + core.PairValueType{Value: args[2], Types: sliceStringType}, ) if err != nil { return emptyDateTime, emptyInt, emptyString, err From 72d721f2335ffaf9e07e6adef658e962255626b8 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 14 Nov 2018 00:17:00 +0300 Subject: [PATCH 108/115] added DATE_DIFF function --- pkg/stdlib/datetime/diff.go | 101 ++++++++++++++++ pkg/stdlib/datetime/diff_test.go | 178 ++++++++++++++++++++++++++++ pkg/stdlib/datetime/helpers_test.go | 2 +- pkg/stdlib/datetime/lib.go | 1 + 4 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 pkg/stdlib/datetime/diff.go create mode 100644 pkg/stdlib/datetime/diff_test.go diff --git a/pkg/stdlib/datetime/diff.go b/pkg/stdlib/datetime/diff.go new file mode 100644 index 00000000..4e5230a7 --- /dev/null +++ b/pkg/stdlib/datetime/diff.go @@ -0,0 +1,101 @@ +package datetime + +import ( + "context" + "strings" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/pkg/errors" +) + +var ( + unitConverter = map[string]func(int64) float64{ + "year": func(nsec int64) float64 { + return float64(nsec / 31536e12) + }, + } +) + +// DateDiff returns the difference between two dates in given time unit. +// @params date1 (DateTime) - first DateTime. +// @params date2 (DateTime) - second DateTime. +// @params unit (String) - time unit to return the difference in. +// @params asFloat (Boolean, optional) - if true amount of unit will be as float. +// @return (Int, Float) - difference between date1 and date2. +func DateDiff(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 3, 4) + if err != nil { + return values.None, err + } + + err = core.ValidateValueTypePairs( + core.PairValueType{Value: args[0], Types: sliceDateTime}, + core.PairValueType{Value: args[1], Types: sliceDateTime}, + core.PairValueType{Value: args[2], Types: sliceStringType}, + ) + if err != nil { + return values.None, err + } + + date1 := args[0].(values.DateTime) + date2 := args[1].(values.DateTime) + unit := args[2].(values.String) + isFloat := values.NewBoolean(false) + + if len(args) == 4 { + err = core.ValidateType(args[3], core.BooleanType) + if err != nil { + return values.None, err + } + isFloat = args[3].(values.Boolean) + } + + if date1.Equal(date2.Time) { + if bool(isFloat) { + return values.NewFloat(0), nil + } + return values.NewInt(0), nil + } + + var nsecDiff int64 + + if date1.After(date2.Time) { + nsecDiff = date1.Time.Sub(date2.Time).Nanoseconds() + } else { + nsecDiff = date2.Time.Sub(date1.Time).Nanoseconds() + } + + unitDiff, err := nsecToUnit(float64(nsecDiff), unit.String()) + if err != nil { + return values.None, err + } + + if !isFloat { + return values.NewInt(int(unitDiff)), nil + } + + return values.NewFloat(unitDiff), nil +} + +func nsecToUnit(nsec float64, unit string) (float64, error) { + switch strings.ToLower(unit) { + case "y", "year", "years": + return nsec / 31536e12, nil + case "m", "month", "months": + return nsec / 26784e11, nil + case "w", "week", "weeks": + return nsec / 6048e11, nil + case "d", "day", "days": + return nsec / 864e11, nil + case "h", "hour", "hours": + return nsec / 36e11, nil + case "i", "minute", "minutes": + return nsec / 6e10, nil + case "s", "second", "seconds": + return nsec / 1e9, nil + case "f", "millisecond", "milliseconds": + return nsec / 1e6, nil + } + return -1, errors.Errorf("no such unit '%s'", unit) +} diff --git a/pkg/stdlib/datetime/diff_test.go b/pkg/stdlib/datetime/diff_test.go new file mode 100644 index 00000000..9d445efe --- /dev/null +++ b/pkg/stdlib/datetime/diff_test.go @@ -0,0 +1,178 @@ +package datetime_test + +import ( + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +var ( + isFloat = values.NewBoolean(true) + beginingEpoch = values.NewDateTime(time.Time{}) +) + +func TestDiff(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "when less then 3 arguments", + Expected: values.NewInt(1), + Args: []core.Value{beginingEpoch}, + ShouldErr: true, + }, + &testCase{ + Name: "when more then 4 arguments", + Expected: values.NewInt(1), + Args: []core.Value{beginingEpoch, beginingEpoch, beginingEpoch, beginingEpoch, beginingEpoch}, + ShouldErr: true, + }, + &testCase{ + Name: "when wrong type argument", + Expected: values.NewInt(1), + Args: []core.Value{beginingEpoch, beginingEpoch, beginingEpoch}, + ShouldErr: true, + }, + &testCase{ + Name: "when the difference is 1 year and 1 month (int)", + Expected: values.NewInt(1), + Args: []core.Value{ + beginingEpoch, + values.NewDateTime( + beginingEpoch.AddDate(1, 1, 0), + ), + values.NewString("y"), + }, + }, + &testCase{ + Name: "when the difference is 1 year and 1 month (float)", + Expected: values.NewFloat(1.084931506849315), + Args: []core.Value{ + beginingEpoch, + values.NewDateTime( + beginingEpoch.AddDate(1, 1, 0), + ), + values.NewString("year"), + isFloat, + }, + }, + &testCase{ + Name: "when date1 after date2 (int)", + Expected: values.NewInt(2), + Args: []core.Value{ + beginingEpoch, + values.NewDateTime( + beginingEpoch.Add(-time.Hour * 48), + ), + values.NewString("d"), + }, + }, + &testCase{ + Name: "when date1 after date2 (float)", + Expected: values.NewFloat(2), + Args: []core.Value{ + beginingEpoch, + values.NewDateTime( + beginingEpoch.Add(-time.Hour * 48), + ), + values.NewString("d"), + isFloat, + }, + }, + &testCase{ + Name: "when dates are equal (int)", + Expected: values.NewInt(0), + Args: []core.Value{ + beginingEpoch, + beginingEpoch, + values.NewString("i"), + }, + }, + &testCase{ + Name: "when dates are equal (float)", + Expected: values.NewFloat(0), + Args: []core.Value{ + beginingEpoch, + beginingEpoch, + values.NewString("y"), + isFloat, + }, + }, + } + + bigUnits := map[string][3]int{ + "y": [3]int{1, 0, 0}, "year": [3]int{1, 0, 0}, "years": [3]int{1, 0, 0}, + "m": [3]int{0, 1, 0}, "month": [3]int{0, 1, 0}, "months": [3]int{0, 1, 0}, + "w": [3]int{0, 0, 7}, "week": [3]int{0, 0, 7}, "weeks": [3]int{0, 0, 7}, + "d": [3]int{0, 0, 1}, "day": [3]int{0, 0, 1}, "days": [3]int{0, 0, 1}, + } + + for unit, dates := range bigUnits { + tcs = append(tcs, + &testCase{ + Name: "When difference is 1 " + unit + " (int)", + Expected: values.NewInt(1), + Args: []core.Value{ + beginingEpoch, + values.NewDateTime( + beginingEpoch.AddDate(dates[0], dates[1], dates[2]), + ), + values.NewString(unit), + }, + }, + &testCase{ + Name: "When difference is 1 " + unit + " (float)", + Expected: values.NewFloat(1), + Args: []core.Value{ + beginingEpoch, + values.NewDateTime( + beginingEpoch.AddDate(dates[0], dates[1], dates[2]), + ), + values.NewString(unit), + isFloat, + }, + }, + ) + } + + units := map[string]time.Duration{ + "h": time.Hour, "hour": time.Hour, "hours": time.Hour, + "i": time.Minute, "minute": time.Minute, "minutes": time.Minute, + "s": time.Second, "second": time.Second, "seconds": time.Second, + "f": time.Millisecond, "millisecond": time.Millisecond, "milliseconds": time.Millisecond, + } + + for unit, durn := range units { + tcs = append(tcs, + &testCase{ + Name: "When difference is 1 " + unit + " (int)", + Expected: values.NewInt(1), + Args: []core.Value{ + beginingEpoch, + values.NewDateTime( + beginingEpoch.Add(durn), + ), + values.NewString(unit), + }, + }, + &testCase{ + Name: "When difference is 1 " + unit + " (int)", + Expected: values.NewFloat(1), + Args: []core.Value{ + beginingEpoch, + values.NewDateTime( + beginingEpoch.Add(durn), + ), + values.NewString(unit), + isFloat, + }, + }, + ) + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDiff) + } +} diff --git a/pkg/stdlib/datetime/helpers_test.go b/pkg/stdlib/datetime/helpers_test.go index 1fe4b0b5..e69fd124 100644 --- a/pkg/stdlib/datetime/helpers_test.go +++ b/pkg/stdlib/datetime/helpers_test.go @@ -14,7 +14,6 @@ import ( type testCase struct { Name string Expected core.Value - TimeArg time.Time Args []core.Value ShouldErr bool } @@ -32,6 +31,7 @@ func (tc *testCase) Do(t *testing.T, fn core.Function) { So(err, ShouldBeNil) } + So(actual.Type(), ShouldEqual, expected.Type()) So(actual.Compare(expected), ShouldEqual, 0) }) } diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index a365b016..3daa77b2 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -21,5 +21,6 @@ func NewLib() map[string]core.Function { "DATE_FORMAT": DateFormat, "DATE_ADD": DateAdd, "DATE_SUBTRACT": DateSubtract, + "DATE_DIFF": DateDiff, } } From 64528499950769a05768dcf392a53cf0356d5ae4 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 14 Nov 2018 00:19:07 +0300 Subject: [PATCH 109/115] delete unused var --- pkg/stdlib/datetime/diff.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pkg/stdlib/datetime/diff.go b/pkg/stdlib/datetime/diff.go index 4e5230a7..b206573b 100644 --- a/pkg/stdlib/datetime/diff.go +++ b/pkg/stdlib/datetime/diff.go @@ -9,14 +9,6 @@ import ( "github.com/pkg/errors" ) -var ( - unitConverter = map[string]func(int64) float64{ - "year": func(nsec int64) float64 { - return float64(nsec / 31536e12) - }, - } -) - // DateDiff returns the difference between two dates in given time unit. // @params date1 (DateTime) - first DateTime. // @params date2 (DateTime) - second DateTime. From 5f34deb94bcaa272b1c7e77e0c0925a621eb7a30 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 14 Nov 2018 00:21:58 +0300 Subject: [PATCH 110/115] delete useless type cast --- pkg/stdlib/datetime/diff.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/stdlib/datetime/diff.go b/pkg/stdlib/datetime/diff.go index b206573b..743d9ebd 100644 --- a/pkg/stdlib/datetime/diff.go +++ b/pkg/stdlib/datetime/diff.go @@ -44,7 +44,7 @@ func DateDiff(_ context.Context, args ...core.Value) (core.Value, error) { } if date1.Equal(date2.Time) { - if bool(isFloat) { + if isFloat { return values.NewFloat(0), nil } return values.NewInt(0), nil From 25b535f053f2841144f4140cd16b9bdf0f1c7dc9 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 18 Nov 2018 17:55:34 +0300 Subject: [PATCH 111/115] fixed a bug when adding/subtrating did not take an amount of units --- pkg/stdlib/datetime/add_subtract.go | 8 +-- pkg/stdlib/datetime/add_subtract_test.go | 72 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/pkg/stdlib/datetime/add_subtract.go b/pkg/stdlib/datetime/add_subtract.go index 777641ba..fc80b5aa 100644 --- a/pkg/stdlib/datetime/add_subtract.go +++ b/pkg/stdlib/datetime/add_subtract.go @@ -109,13 +109,13 @@ func addUnit(dt values.DateTime, amount int, unit string) (values.DateTime, erro case "d", "day", "days": return values.NewDateTime(dt.AddDate(0, 0, amount*1)), nil case "h", "hour", "hours": - return values.NewDateTime(dt.Add(time.Hour)), nil + return values.NewDateTime(dt.Add(time.Duration(amount) * time.Hour)), nil case "i", "minute", "minutes": - return values.NewDateTime(dt.Add(time.Minute)), nil + return values.NewDateTime(dt.Add(time.Duration(amount) * time.Minute)), nil case "s", "second", "seconds": - return values.NewDateTime(dt.Add(time.Second)), nil + return values.NewDateTime(dt.Add(time.Duration(amount) * time.Second)), nil case "f", "millisecond", "milliseconds": - return values.NewDateTime(dt.Add(time.Millisecond)), nil + return values.NewDateTime(dt.Add(time.Duration(amount) * time.Millisecond)), nil } return values.DateTime{}, errors.Errorf("no such unit '%s'", unit) } diff --git a/pkg/stdlib/datetime/add_subtract_test.go b/pkg/stdlib/datetime/add_subtract_test.go index 8a6e1015..83077d3c 100644 --- a/pkg/stdlib/datetime/add_subtract_test.go +++ b/pkg/stdlib/datetime/add_subtract_test.go @@ -111,6 +111,42 @@ func TestDateAdd(t *testing.T) { values.NewString("year"), }, }, + &testCase{ + Name: "+2 hours", + Expected: mustDefaultLayoutDt("1999-02-07T17:04:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(2), + values.NewString("h"), + }, + }, + &testCase{ + Name: "+20 minutes", + Expected: mustDefaultLayoutDt("1999-02-07T15:24:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(20), + values.NewString("i"), + }, + }, + &testCase{ + Name: "+30 seconds", + Expected: mustDefaultLayoutDt("1999-02-07T15:04:35Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(30), + values.NewString("s"), + }, + }, + &testCase{ + Name: "+1000 milliseconds", + Expected: mustDefaultLayoutDt("1999-02-07T15:04:06Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(1000), + values.NewString("f"), + }, + }, } for _, tc := range tcs { @@ -214,6 +250,42 @@ func TestDateSubtract(t *testing.T) { values.NewString("year"), }, }, + &testCase{ + Name: "-2 hours", + Expected: mustDefaultLayoutDt("1999-02-07T13:04:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(2), + values.NewString("h"), + }, + }, + &testCase{ + Name: "-20 minutes", + Expected: mustDefaultLayoutDt("1999-02-07T14:44:05Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(20), + values.NewString("i"), + }, + }, + &testCase{ + Name: "-30 seconds", + Expected: mustDefaultLayoutDt("1999-02-07T15:03:35Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(30), + values.NewString("s"), + }, + }, + &testCase{ + Name: "-1000 milliseconds", + Expected: mustDefaultLayoutDt("1999-02-07T15:04:04Z"), + Args: []core.Value{ + mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + values.NewInt(1000), + values.NewString("f"), + }, + }, } for _, tc := range tcs { From 5f5795843682a6e2b138ca696e799ba0eca5c714 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 20 Nov 2018 00:57:23 +0300 Subject: [PATCH 112/115] added DateCompare function --- pkg/stdlib/datetime/add_subtract.go | 37 ++------- pkg/stdlib/datetime/compare.go | 66 ++++++++++++++++ pkg/stdlib/datetime/compare_test.go | 107 ++++++++++++++++++++++++++ pkg/stdlib/datetime/diff.go | 24 +----- pkg/stdlib/datetime/unit.go | 113 ++++++++++++++++++++++++++++ 5 files changed, 298 insertions(+), 49 deletions(-) create mode 100644 pkg/stdlib/datetime/compare.go create mode 100644 pkg/stdlib/datetime/compare_test.go create mode 100644 pkg/stdlib/datetime/unit.go diff --git a/pkg/stdlib/datetime/add_subtract.go b/pkg/stdlib/datetime/add_subtract.go index 777641ba..7131455e 100644 --- a/pkg/stdlib/datetime/add_subtract.go +++ b/pkg/stdlib/datetime/add_subtract.go @@ -2,12 +2,9 @@ package datetime import ( "context" - "strings" - "time" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "github.com/pkg/errors" ) var ( @@ -40,12 +37,14 @@ func DateAdd(_ context.Context, args ...core.Value) (core.Value, error) { return values.None, err } - dt, err := addUnit(date, int(amount), unit.String()) + u, err := UnitFromString(unit.String()) if err != nil { return values.None, err } - return dt, nil + tm := AddUnit(date.Time, int(amount), u) + + return values.NewDateTime(tm), nil } // DateSubtract subtract amount given in unit to date. @@ -68,12 +67,14 @@ func DateSubtract(_ context.Context, args ...core.Value) (core.Value, error) { return values.None, err } - dt, err := addUnit(date, -1*int(amount), unit.String()) + u, err := UnitFromString(unit.String()) if err != nil { return values.None, err } - return dt, nil + tm := AddUnit(date.Time, -1*int(amount), u) + + return values.NewDateTime(tm), nil } func getArgs(args []core.Value) (values.DateTime, values.Int, values.String, error) { @@ -97,25 +98,3 @@ func getArgs(args []core.Value) (values.DateTime, values.Int, values.String, err return date, amount, unit, nil } - -func addUnit(dt values.DateTime, amount int, unit string) (values.DateTime, error) { - switch strings.ToLower(unit) { - case "y", "year", "years": - return values.NewDateTime(dt.AddDate(amount*1, 0, 0)), nil - case "m", "month", "months": - return values.NewDateTime(dt.AddDate(0, amount*1, 0)), nil - case "w", "week", "weeks": - return values.NewDateTime(dt.AddDate(0, 0, amount*7)), nil - case "d", "day", "days": - return values.NewDateTime(dt.AddDate(0, 0, amount*1)), nil - case "h", "hour", "hours": - return values.NewDateTime(dt.Add(time.Hour)), nil - case "i", "minute", "minutes": - return values.NewDateTime(dt.Add(time.Minute)), nil - case "s", "second", "seconds": - return values.NewDateTime(dt.Add(time.Second)), nil - case "f", "millisecond", "milliseconds": - return values.NewDateTime(dt.Add(time.Millisecond)), nil - } - return values.DateTime{}, errors.Errorf("no such unit '%s'", unit) -} diff --git a/pkg/stdlib/datetime/compare.go b/pkg/stdlib/datetime/compare.go new file mode 100644 index 00000000..7519b1e5 --- /dev/null +++ b/pkg/stdlib/datetime/compare.go @@ -0,0 +1,66 @@ +package datetime + +import ( + "github.com/pkg/errors" + + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateCompare check if two partial dates match. +// @params date1, date2 (DateTime) - comparable dates. +// @params unitRangeStart (String) - unit to start from. +// @params unitRangeEnd (String, Optional) - unit to end with. +// Error will be returned if unitRangeStart unit less that unitRangeEnd. +// @return (Boolean) - true if the dates match, else false. +func DateCompare(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 3, 4) + if err != nil { + return values.None, err + } + + err = core.ValidateValueTypePairs( + core.PairValueType{Value: args[0], Types: sliceDateTime}, + core.PairValueType{Value: args[1], Types: sliceDateTime}, + core.PairValueType{Value: args[2], Types: sliceStringType}, + ) + if err != nil { + return values.None, err + } + + date1 := args[0].(values.DateTime) + date2 := args[1].(values.DateTime) + rangeStart := args[2].(values.String) + rangeEnd := values.NewString("millisecond") + + if len(args) == 4 { + if err = core.ValidateType(args[3], core.StringType); err != nil { + return values.None, err + } + rangeEnd = args[3].(values.String) + } + + unitStart, err := UnitFromString(rangeStart.String()) + if err != nil { + return values.None, err + } + + unitEnd, err := UnitFromString(rangeEnd.String()) + if err != nil { + return values.None, err + } + + if unitStart < unitEnd { + return values.None, errors.Errorf("start unit less that end unit") + } + + for u := unitEnd; u <= unitStart; u++ { + if IsDatesEqual(date1.Time, date2.Time, u) { + return values.NewBoolean(true), nil + } + } + + return values.NewBoolean(false), nil +} diff --git a/pkg/stdlib/datetime/compare_test.go b/pkg/stdlib/datetime/compare_test.go new file mode 100644 index 00000000..534d29c5 --- /dev/null +++ b/pkg/stdlib/datetime/compare_test.go @@ -0,0 +1,107 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateCompare(t *testing.T) { + expectedTrue := values.NewBoolean(true) + expectedFalse := values.NewBoolean(false) + + tcs := []*testCase{ + &testCase{ + Name: "When less than 3 arguments", + Expected: values.None, + Args: []core.Value{values.NewInt(0), values.NewInt(0)}, + ShouldErr: true, + }, + &testCase{ + Name: "When more than 4 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewInt(0), values.NewInt(0), values.NewInt(0), + values.NewInt(0), values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "when wrong type of arguments", + Expected: values.None, + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewCurrentDateTime(), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "when wrong type of optional argument", + Expected: values.None, + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewCurrentDateTime(), + values.NewString("year"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "when start unit less that end unit", + Expected: values.None, + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewCurrentDateTime(), + values.NewString("day"), + values.NewString("year"), + }, + ShouldErr: true, + }, + &testCase{ + Name: "when years are equal", + Expected: expectedTrue, + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewCurrentDateTime(), + values.NewString("year"), + }, + }, + &testCase{ + Name: "when years are not equal", + Expected: expectedFalse, + Args: []core.Value{ + mustLayoutDt("2006-01-02", "1999-02-07"), + mustLayoutDt("2006-01-02", "2000-02-07"), + values.NewString("year"), + values.NewString("year"), + }, + }, + &testCase{ + Name: "when months are equal", + Expected: expectedTrue, + Args: []core.Value{ + mustLayoutDt("2006-01-02", "1999-02-07"), + mustLayoutDt("2006-01-02", "2000-02-09"), + values.NewString("year"), + values.NewString("days"), + }, + }, + &testCase{ + Name: "when days are equal", + Expected: expectedTrue, + Args: []core.Value{ + values.NewCurrentDateTime(), + values.NewCurrentDateTime(), + values.NewString("days"), + values.NewString("days"), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateCompare) + } +} diff --git a/pkg/stdlib/datetime/diff.go b/pkg/stdlib/datetime/diff.go index 743d9ebd..d40bd8e4 100644 --- a/pkg/stdlib/datetime/diff.go +++ b/pkg/stdlib/datetime/diff.go @@ -2,11 +2,9 @@ package datetime import ( "context" - "strings" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "github.com/pkg/errors" ) // DateDiff returns the difference between two dates in given time unit. @@ -71,23 +69,9 @@ func DateDiff(_ context.Context, args ...core.Value) (core.Value, error) { } func nsecToUnit(nsec float64, unit string) (float64, error) { - switch strings.ToLower(unit) { - case "y", "year", "years": - return nsec / 31536e12, nil - case "m", "month", "months": - return nsec / 26784e11, nil - case "w", "week", "weeks": - return nsec / 6048e11, nil - case "d", "day", "days": - return nsec / 864e11, nil - case "h", "hour", "hours": - return nsec / 36e11, nil - case "i", "minute", "minutes": - return nsec / 6e10, nil - case "s", "second", "seconds": - return nsec / 1e9, nil - case "f", "millisecond", "milliseconds": - return nsec / 1e6, nil + u, err := UnitFromString(unit) + if err != nil { + return -1, err } - return -1, errors.Errorf("no such unit '%s'", unit) + return nsec / u.Nanosecond(), nil } diff --git a/pkg/stdlib/datetime/unit.go b/pkg/stdlib/datetime/unit.go new file mode 100644 index 00000000..e0cfbfae --- /dev/null +++ b/pkg/stdlib/datetime/unit.go @@ -0,0 +1,113 @@ +package datetime + +import ( + "strings" + "time" + + "github.com/pkg/errors" +) + +// Unit specifies an unit of time (Millsecond, Second...). +type Unit int + +const ( + Millisecond Unit = iota + Second + Minute + Hour + Day + Week + Month + Year +) + +var nanoseconds = []float64{ + 1e6, + 1e9, + 6e10, + 36e11, + 864e11, + 6048e11, + 26784e11, + 31536e12, +} + +// Nanosecond returns representation of an Unit +// in nanosconds +func (u Unit) Nanosecond() float64 { + return nanoseconds[u] +} + +// IsDatesEqual check if two partial dates match. +// This case the day means not the amount of days in Time, +// but the day of the month. +// The same rules applied to each unit. +func IsDatesEqual(tm1, tm2 time.Time, u Unit) bool { + switch u { + case Millisecond: + date1Msec := tm1.Nanosecond() / 1e6 + date2Msec := tm2.Nanosecond() / 1e6 + return date1Msec == date2Msec + case Second: + return tm1.Second() == tm2.Second() + case Minute: + return tm1.Minute() == tm2.Minute() + case Hour: + return tm1.Hour() == tm2.Hour() + case Day: + return tm1.Day() == tm2.Day() + case Week: + date1Wk := tm1.Day() / 7 + date2Wk := tm2.Day() / 7 + return date1Wk == date2Wk + case Month: + return tm1.Month() == tm2.Month() + case Year: + return tm1.Year() == tm2.Year() + } + return false +} + +// AddUnit add amount given in u to tm +func AddUnit(tm time.Time, amount int, u Unit) (res time.Time) { + if u < Day { + return tm.Add(time.Duration(int64(u.Nanosecond()))) + } + + switch u { + case Day: + res = tm.AddDate(0, 0, amount*1) + case Week: + res = tm.AddDate(0, 0, amount*7) + case Month: + res = tm.AddDate(0, amount*1, 0) + case Year: + res = tm.AddDate(amount*1, 0, 0) + } + + return +} + +// UnitFromString returns true and an Unit object if +// Unit with that name exists. Returns false, otherwise. +func UnitFromString(s string) (Unit, error) { + switch strings.ToLower(s) { + case "y", "year", "years": + return Year, nil + case "m", "month", "months": + return Month, nil + case "w", "week", "weeks": + return Week, nil + case "d", "day", "days": + return Day, nil + case "h", "hour", "hours": + return Hour, nil + case "i", "minute", "minutes": + return Minute, nil + case "s", "second", "seconds": + return Second, nil + case "f", "millisecond", "milliseconds": + return Millisecond, nil + } + return -1, errors.Errorf("no such unit '%s'", s) +} From f5da93496bd96d85040889f76964b28383fc986b Mon Sep 17 00:00:00 2001 From: = Date: Tue, 20 Nov 2018 01:04:08 +0300 Subject: [PATCH 113/115] renames --- pkg/stdlib/datetime/unit.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/stdlib/datetime/unit.go b/pkg/stdlib/datetime/unit.go index e0cfbfae..cdd7c7db 100644 --- a/pkg/stdlib/datetime/unit.go +++ b/pkg/stdlib/datetime/unit.go @@ -45,9 +45,9 @@ func (u Unit) Nanosecond() float64 { func IsDatesEqual(tm1, tm2 time.Time, u Unit) bool { switch u { case Millisecond: - date1Msec := tm1.Nanosecond() / 1e6 - date2Msec := tm2.Nanosecond() / 1e6 - return date1Msec == date2Msec + tm1Msec := tm1.Nanosecond() / 1e6 + tm2Msec := tm2.Nanosecond() / 1e6 + return tm1Msec == tm2Msec case Second: return tm1.Second() == tm2.Second() case Minute: @@ -57,9 +57,9 @@ func IsDatesEqual(tm1, tm2 time.Time, u Unit) bool { case Day: return tm1.Day() == tm2.Day() case Week: - date1Wk := tm1.Day() / 7 - date2Wk := tm2.Day() / 7 - return date1Wk == date2Wk + tm1Wk := tm1.Day() / 7 + tm2Wk := tm2.Day() / 7 + return tm1Wk == tm2Wk case Month: return tm1.Month() == tm2.Month() case Year: From 6dc041bc94fda0f3d1b0492febf39a39362c1dde Mon Sep 17 00:00:00 2001 From: = Date: Tue, 20 Nov 2018 01:23:01 +0300 Subject: [PATCH 114/115] fix small bug --- pkg/stdlib/datetime/unit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/stdlib/datetime/unit.go b/pkg/stdlib/datetime/unit.go index cdd7c7db..580f0c71 100644 --- a/pkg/stdlib/datetime/unit.go +++ b/pkg/stdlib/datetime/unit.go @@ -71,7 +71,7 @@ func IsDatesEqual(tm1, tm2 time.Time, u Unit) bool { // AddUnit add amount given in u to tm func AddUnit(tm time.Time, amount int, u Unit) (res time.Time) { if u < Day { - return tm.Add(time.Duration(int64(u.Nanosecond()))) + return tm.Add(time.Duration(amount) * time.Duration(int64(u.Nanosecond()))) } switch u { From 8eec650026cad154356b4119fff2d41ef5a9d2d1 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 20 Nov 2018 01:50:19 +0300 Subject: [PATCH 115/115] fix --- pkg/runtime/values/array.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/runtime/values/array.go b/pkg/runtime/values/array.go index 813ced8b..276fe7b2 100644 --- a/pkg/runtime/values/array.go +++ b/pkg/runtime/values/array.go @@ -236,7 +236,7 @@ func (t *Array) SortWith(sorter ArraySorter) *Array { copy(c, t.items) sort.SliceStable(c, func(i, j int) bool { - return c[i].Compare(c[j]) == -1 + return sorter(c[i], c[j]) }) res := new(Array)