From 7afe14ab1a8054321dcb6a04d179d8b2a5d4a155 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Fri, 20 Apr 2018 21:34:54 +0100 Subject: [PATCH] =?UTF-8?q?Add=20=E2=80=98purge=E2=80=99=20SQL=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/funcs.go | 6 ++++ util/fncs/args.go | 16 +++++---- util/fncs/fnc.go | 8 +++++ util/fncs/purge.go | 73 +++++++++++++++++++++++++++++++++++++++++ util/fncs/purge_test.go | 69 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 util/fncs/purge.go create mode 100644 util/fncs/purge_test.go diff --git a/sql/funcs.go b/sql/funcs.go index 39c21abe..b24afb14 100644 --- a/sql/funcs.go +++ b/sql/funcs.go @@ -88,6 +88,12 @@ var funcs = map[string]map[int]interface{}{ "count.if": {2: nil}, "count.not": {2: nil}, + // Purge implementation + + "purge": {1: nil}, + "purge.if": {2: nil}, + "purge.not": {2: nil}, + // Json implementation "json.decode": {1: nil}, "json.encode": {1: nil}, diff --git a/util/fncs/args.go b/util/fncs/args.go index 0bb1423d..ac7c8241 100644 --- a/util/fncs/args.go +++ b/util/fncs/args.go @@ -25,6 +25,12 @@ import ( var defaultTime = time.Unix(0, 0) +func copySlice(arr []interface{}) (out []interface{}) { + out = make([]interface{}, len(arr)) + copy(out, arr) + return +} + func outputFloat(val float64) (interface{}, error) { switch { case math.IsNaN(val): @@ -176,20 +182,18 @@ func ensureDuration(val interface{}) (out time.Duration, ok bool) { func ensureSlice(args interface{}) (out []interface{}, ok bool) { if i, ok := args.([]interface{}); ok { - out = i + return i, true } else { - out = []interface{}{args} + return []interface{}{args}, false } - return out, true } func ensureObject(args interface{}) (out map[string]interface{}, ok bool) { if i, ok := args.(map[string]interface{}); ok { - out = i + return i, true } else { - out = map[string]interface{}{} + return map[string]interface{}{}, false } - return out, true } func ensureInts(args interface{}) (out []int64) { diff --git a/util/fncs/fnc.go b/util/fncs/fnc.go index 7ac2500e..cdaffef5 100644 --- a/util/fncs/fnc.go +++ b/util/fncs/fnc.go @@ -55,6 +55,14 @@ func Run(ctx context.Context, name string, args ...interface{}) (interface{}, er case "count.not": return countNot(ctx, args...) + // Purge implementation + case "purge": + return purge(ctx, args...) + case "purge.if": + return purgeIf(ctx, args...) + case "purge.not": + return purgeNot(ctx, args...) + // Json implementation case "json.decode": return jsonDecode(ctx, args...) diff --git a/util/fncs/purge.go b/util/fncs/purge.go new file mode 100644 index 00000000..7383fe6c --- /dev/null +++ b/util/fncs/purge.go @@ -0,0 +1,73 @@ +// Copyright © 2016 Abcum Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fncs + +import ( + "context" +) + +func purge(ctx context.Context, args ...interface{}) (interface{}, error) { + if arr, ok := ensureSlice(args[0]); ok { + arr = copySlice(arr) + for i := len(arr) - 1; i >= 0; i-- { + if arr[i] == nil { + copy(arr[i:], arr[i+1:]) + arr[len(arr)-1] = nil + arr = arr[:len(arr)-1] + } + } + return arr, nil + } + if args[0] == nil { + return nil, nil + } + return args[0], nil +} + +func purgeIf(ctx context.Context, args ...interface{}) (interface{}, error) { + if arr, ok := ensureSlice(args[0]); ok { + arr = copySlice(arr) + for i := len(arr) - 1; i >= 0; i-- { + if arr[i] == args[1] { + copy(arr[i:], arr[i+1:]) + arr[len(arr)-1] = nil + arr = arr[:len(arr)-1] + } + } + return arr, nil + } + if args[0] == args[1] { + return nil, nil + } + return args[0], nil +} + +func purgeNot(ctx context.Context, args ...interface{}) (interface{}, error) { + if arr, ok := ensureSlice(args[0]); ok { + arr = copySlice(arr) + for i := len(arr) - 1; i >= 0; i-- { + if arr[i] != args[1] { + copy(arr[i:], arr[i+1:]) + arr[len(arr)-1] = nil + arr = arr[:len(arr)-1] + } + } + return arr, nil + } + if args[0] != args[1] { + return nil, nil + } + return args[0], nil +} diff --git a/util/fncs/purge_test.go b/util/fncs/purge_test.go new file mode 100644 index 00000000..de8e45f1 --- /dev/null +++ b/util/fncs/purge_test.go @@ -0,0 +1,69 @@ +// Copyright © 2016 Abcum Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fncs + +import ( + "context" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestPurge(t *testing.T) { + + var res interface{} + + var test = []interface{}{int(1), int(2), nil, float64(3.5), "testing string"} + + Convey("purge() works properly", t, func() { + res, _ = Run(context.Background(), "purge", nil) + So(res, ShouldResemble, nil) + res, _ = Run(context.Background(), "purge", "test") + So(res, ShouldResemble, "test") + res, _ = Run(context.Background(), "purge", test) + So(res, ShouldResemble, []interface{}{int(1), int(2), float64(3.5), "testing string"}) + }) + + Convey("purge.if() works properly", t, func() { + res, _ = Run(context.Background(), "purge.if", "test", nil) + So(res, ShouldResemble, "test") + res, _ = Run(context.Background(), "purge.if", "test", "none") + So(res, ShouldResemble, "test") + res, _ = Run(context.Background(), "purge.if", "test", "test") + So(res, ShouldResemble, nil) + res, _ = Run(context.Background(), "purge.if", test, "testing string") + So(res, ShouldResemble, []interface{}{int(1), int(2), nil, float64(3.5)}) + res, _ = Run(context.Background(), "purge.if", test, 3.5) + So(res, ShouldResemble, []interface{}{int(1), int(2), nil, "testing string"}) + res, _ = Run(context.Background(), "purge.if", test, nil) + So(res, ShouldResemble, []interface{}{int(1), int(2), float64(3.5), "testing string"}) + }) + + Convey("purge.not() works properly", t, func() { + res, _ = Run(context.Background(), "purge.not", "test", nil) + So(res, ShouldResemble, nil) + res, _ = Run(context.Background(), "purge.not", "test", "none") + So(res, ShouldResemble, nil) + res, _ = Run(context.Background(), "purge.not", "test", "test") + So(res, ShouldResemble, "test") + res, _ = Run(context.Background(), "purge.not", test, "testing string") + So(res, ShouldResemble, []interface{}{"testing string"}) + res, _ = Run(context.Background(), "purge.not", test, 3.5) + So(res, ShouldResemble, []interface{}{float64(3.5)}) + res, _ = Run(context.Background(), "purge.not", test, nil) + So(res, ShouldResemble, []interface{}{nil}) + }) + +}