From 56b30cd912fd70f76baa0c20e4cf2373e4ae1745 Mon Sep 17 00:00:00 2001 From: Jan Semmelink <jan@uafrica.com> Date: Mon, 27 Sep 2021 10:20:35 +0200 Subject: [PATCH] Added struct_utils --- struct_utils/map_params.go | 55 +++++++++++++++++++++++++++++ struct_utils/map_params_test.go | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 struct_utils/map_params.go create mode 100644 struct_utils/map_params_test.go diff --git a/struct_utils/map_params.go b/struct_utils/map_params.go new file mode 100644 index 0000000..a7830fd --- /dev/null +++ b/struct_utils/map_params.go @@ -0,0 +1,55 @@ +package struct_utils + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" +) + +//convert fields in a struct to a map of parameters, as if defined in a URL +//we use this mainly for legacy functions that expect params to be defined in a map[string]string +//to convert the new params struct into such a map +func MapParams(data interface{}) map[string]string { + params := map[string]string{} + addStructParams(params, reflect.ValueOf(data)) + return params +} + +//recursive function +func addStructParams(params map[string]string, structValue reflect.Value) { + t := structValue.Type() + if t.Kind() != reflect.Struct { + return + } + + for i := 0; i < t.NumField(); i++ { + tf := t.Field(i) + //recurse for embedded structs + if tf.Anonymous { + addStructParams(params, structValue.Field(i)) + } else { + jsonTags := strings.Split(t.Field(i).Tag.Get("json"), ",") + skip := false + if jsonTags[0] == "-" || jsonTags[0] == "" { + skip = true + } else { + for _, option := range jsonTags[1:] { + if option == "omitempty" && structValue.Field(i).IsZero() { // ignore the field if omitempty is applicable + skip = true + } + } + } + if !skip { + //lists must be written as JSON lists so they can be unmarshalled + //jsut because that is how the legacy code did it + if t.Field(i).Type.Kind() == reflect.Slice { + jsonValue, _ := json.Marshal(structValue.Field(i).Interface()) + params[jsonTags[0]] = string(jsonValue) + } else { + params[jsonTags[0]] = fmt.Sprintf("%v", structValue.Field(i).Interface()) + } + } + } + } +} diff --git a/struct_utils/map_params_test.go b/struct_utils/map_params_test.go new file mode 100644 index 0000000..369264e --- /dev/null +++ b/struct_utils/map_params_test.go @@ -0,0 +1,61 @@ +package struct_utils_test + +import ( + "testing" + + "gitlab.com/uafrica/go-utils/struct_utils" +) + +func TestParams(t *testing.T) { + type s struct { + NameWithoutTag string //will not be encoded into params! + NameWithDashTag string `json:"-"` //will not be encoded into params! + Name string `json:"name"` //encoded always + NameOmitempty string `json:"name2,omitempty"` //encoded when not empty + } + ps := s{"a", "b", "c", "d"} + pm := struct_utils.MapParams(ps) + if len(pm) != 2 || pm["name"] != "c" || pm["name2"] != "d" { //name2 is encoded when not empty + t.Fatalf("wrong params: %+v != %+v", ps, pm) + } + t.Logf("ps=%+v -> pm=%+v", ps, pm) + + ps = s{} + pm = struct_utils.MapParams(ps) + if len(pm) != 1 || pm["name"] != "" { //name is always encoded because it has json tag and does not specify omitempty + t.Fatalf("wrong params: %+v != %+v", ps, pm) + } + t.Logf("ps=%+v -> pm=%+v", ps, pm) +} + +func TestAnonymous(t *testing.T) { + type page struct { + Limit int64 `json:"limit,omitempty"` + Offset int64 `json:"offset,omitempty"` + } + type get struct { + ID int64 `json:"id,omitempty"` + page + } + + ps := get{ID: 123} + pm := struct_utils.MapParams(ps) + if len(pm) != 1 || pm["id"] != "123" { + t.Fatalf("wrong params: %+v != %+v", ps, pm) + } + t.Logf("ps=%+v -> pm=%+v", ps, pm) + + ps = get{page: page{Limit: 444, Offset: 555}} + pm = struct_utils.MapParams(ps) + if len(pm) != 2 || pm["limit"] != "444" || pm["offset"] != "555" { + t.Fatalf("wrong params: %+v != %+v", ps, pm) + } + t.Logf("ps=%+v -> pm=%+v", ps, pm) + + ps = get{page: page{Limit: 444, Offset: 555}, ID: 111} + pm = struct_utils.MapParams(ps) + if len(pm) != 3 || pm["limit"] != "444" || pm["offset"] != "555" || pm["id"] != "111" { + t.Fatalf("wrong params: %+v != %+v", ps, pm) + } + t.Logf("ps=%+v -> pm=%+v", ps, pm) +} -- GitLab