Browse Source

fix: Better type conversion of nested structures and a new type function to inspect objects

Matthias Ladkau 2 months ago
parent
commit
585ba9018e
4 changed files with 89 additions and 1 deletions
  1. 34 0
      interpreter/func_provider.go
  2. 20 0
      interpreter/func_provider_test.go
  3. 18 1
      scope/helper.go
  4. 17 0
      scope/helper_test.go

+ 34 - 0
interpreter/func_provider.go

@@ -31,6 +31,7 @@ InbuildFuncMap contains the mapping of inbuild functions.
 var InbuildFuncMap = map[string]util.ECALFunction{
 	"range":           &rangeFunc{&inbuildBaseFunc{}},
 	"new":             &newFunc{&inbuildBaseFunc{}},
+	"type":            &typeFunc{&inbuildBaseFunc{}},
 	"len":             &lenFunc{&inbuildBaseFunc{}},
 	"del":             &delFunc{&inbuildBaseFunc{}},
 	"add":             &addFunc{&inbuildBaseFunc{}},
@@ -281,6 +282,39 @@ func (rf *newFunc) DocString() (string, error) {
 	return "New creates a new object instance.", nil
 }
 
+// Type
+// =====
+
+/*
+typeFunc returns the underlying types and values of an object.
+*/
+type typeFunc struct {
+	*inbuildBaseFunc
+}
+
+/*
+Run executes this function.
+*/
+func (rf *typeFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
+	var res interface{}
+
+	err := fmt.Errorf("Need a value as first parameter")
+
+	if len(args) > 0 {
+		res = fmt.Sprintf("%#v", args[0])
+		err = nil
+	}
+
+	return res, err
+}
+
+/*
+DocString returns a descriptive string.
+*/
+func (rf *typeFunc) DocString() (string, error) {
+	return "Type returns the underlying types and values of an object.", nil
+}
+
 // Len
 // ===
 

+ 20 - 0
interpreter/func_provider_test.go

@@ -99,6 +99,26 @@ identifier: len
 		return
 	}
 
+	res, err = UnitTestEvalAndAST(
+		`type({a: [1,2,3]})`, nil,
+		`
+identifier: type
+  funccall
+    map
+      kvp
+        identifier: a
+        list
+          number: 1
+          number: 2
+          number: 3
+`[1:])
+	errorutil.AssertOk(err)
+
+	if res != "map[interface {}]interface {}{interface {}(nil):[]interface {}{1, 2, 3}}" {
+		t.Error("Unexpected result: ", res, err)
+		return
+	}
+
 	res, err = UnitTestEvalAndAST(
 		`len({"a":1, 2:"b"})`, nil,
 		`

+ 18 - 1
scope/helper.go

@@ -71,7 +71,16 @@ can be used by ECAL.
 func ConvertJSONToECALObject(v interface{}) interface{} {
 	res := v
 
-	if mapContainer, ok := v.(map[string]interface{}); ok {
+	if mapContainer, ok := v.(map[interface{}]interface{}); ok {
+		newRes := make(map[interface{}]interface{})
+
+		for mk, mv := range mapContainer {
+			newRes[mk] = ConvertJSONToECALObject(mv)
+		}
+
+		res = newRes
+
+	} else if mapContainer, ok := v.(map[string]interface{}); ok {
 		newRes := make(map[interface{}]interface{})
 
 		for mk, mv := range mapContainer {
@@ -87,6 +96,14 @@ func ConvertJSONToECALObject(v interface{}) interface{} {
 			newRes[i] = ConvertJSONToECALObject(lv)
 		}
 
+		res = newRes
+	} else if mapList, ok := v.([]map[string]interface{}); ok {
+		newRes := make([]interface{}, len(mapList))
+
+		for i, lv := range mapList {
+			newRes[i] = ConvertJSONToECALObject(lv)
+		}
+
 		res = newRes
 	}
 

+ 17 - 0
scope/helper_test.go

@@ -67,4 +67,21 @@ func TestConvertJSONToECALObject(t *testing.T) {
 		return
 	}
 
+	testJSONStructure2 := map[interface{}]interface{}{"data": map[interface{}]interface{}{"obj": []map[string]interface{}{{"key": "LovelyRabbit"}}}}
+
+	res = ConvertJSONToECALObject(testJSONStructure2)
+
+	if typeString := fmt.Sprintf("%#v", res); typeString !=
+		`map[interface {}]interface {}{"data":map[interface {}]interface {}{"obj":[]interface {}{map[interface {}]interface {}{"key":"LovelyRabbit"}}}}` {
+		t.Error("Unexpected result:", typeString)
+		return
+	}
+
+	res = ConvertECALToJSONObject(res)
+
+	if typeString := fmt.Sprintf("%#v", res); typeString !=
+		`map[string]interface {}{"data":map[string]interface {}{"obj":[]interface {}{map[string]interface {}{"key":"LovelyRabbit"}}}}` {
+		t.Error("Unexpected result:", typeString)
+		return
+	}
 }