From 2c152ba5e44d92cce2b1b934f6fbac08c45b0066 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Tue, 19 Dec 2023 11:24:36 +0100 Subject: [PATCH] feat: sortObjectsByKeys with types other than string --- internal/template/sort.go | 44 ++++++++++++++++++++++++++++------ internal/template/sort_test.go | 40 +++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/internal/template/sort.go b/internal/template/sort.go index 76689fc4..ef71258f 100644 --- a/internal/template/sort.go +++ b/internal/template/sort.go @@ -3,6 +3,8 @@ package template import ( "reflect" "sort" + "strconv" + "time" ) // sortStrings returns a sorted array of strings in increasing order @@ -52,15 +54,43 @@ func (s *sortableByKey) set(funcName string, entries interface{}) (err error) { return } -// method required to implement sort.Interface -func (s sortableByKey) Less(i, j int) bool { - values := map[int]string{i: "", j: ""} - for k := range values { - if v := reflect.ValueOf(deepGet(s.data[k], s.key)); v.Kind() != reflect.Invalid { - values[k] = v.Interface().(string) +func getFieldAsString(item interface{}, path string) string { + // Mostly inspired by https://stackoverflow.com/a/47739620 + e := deepGet(item, path) + r := reflect.ValueOf(e) + + if r.Kind() == reflect.Invalid { + return "" + } + + fieldValue := r.Interface() + + switch v := fieldValue.(type) { + case int: + return strconv.FormatInt(int64(v), 10) + case int32: + return strconv.FormatInt(int64(v), 10) + case int64: + return strconv.FormatInt(v, 10) + case string: + return v + case bool: + if v { + return "true" } + return "false" + case time.Time: + return v.String() + default: + return "" } - return values[i] < values[j] +} + +// method required to implement sort.Interface +func (s sortableByKey) Less(i, j int) bool { + dataI := getFieldAsString(s.data[i], s.key) + dataJ := getFieldAsString(s.data[j], s.key) + return dataI < dataJ } // Generalized SortBy function diff --git a/internal/template/sort_test.go b/internal/template/sort_test.go index b04c440f..e5c5ff44 100644 --- a/internal/template/sort_test.go +++ b/internal/template/sort_test.go @@ -2,6 +2,7 @@ package template import ( "testing" + "time" "github.com/nginx-proxy/docker-gen/internal/context" "github.com/stretchr/testify/assert" @@ -19,28 +20,61 @@ func TestSortStringsDesc(t *testing.T) { assert.Equal(t, expected, sortStringsDesc(strings)) } +func TestGetFieldAsString(t *testing.T) { + testStruct := struct { + String string + BoolT bool + BoolF bool + Int int + Int32 int32 + Int64 int64 + Time time.Time + }{ + String: "foo", + BoolT: true, + BoolF: false, + Int: 42, + Int32: 43, + Int64: 44, + Time: time.Date(2023, 12, 19, 0, 0, 0, 0, time.UTC), + } + + assert.Equal(t, "foo", getFieldAsString(testStruct, "String")) + assert.Equal(t, "true", getFieldAsString(testStruct, "BoolT")) + assert.Equal(t, "false", getFieldAsString(testStruct, "BoolF")) + assert.Equal(t, "42", getFieldAsString(testStruct, "Int")) + assert.Equal(t, "43", getFieldAsString(testStruct, "Int32")) + assert.Equal(t, "44", getFieldAsString(testStruct, "Int64")) + assert.Equal(t, "2023-12-19 00:00:00 +0000 UTC", getFieldAsString(testStruct, "Time")) + assert.Equal(t, "", getFieldAsString(testStruct, "InvalidField")) +} + func TestSortObjectsByKeys(t *testing.T) { o0 := &context.RuntimeContainer{ + Created: time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC), Env: map[string]string{ "VIRTUAL_HOST": "bar.localhost", }, ID: "9", } o1 := &context.RuntimeContainer{ + Created: time.Date(2021, 1, 2, 0, 0, 10, 0, time.UTC), Env: map[string]string{ "VIRTUAL_HOST": "foo.localhost", }, ID: "1", } o2 := &context.RuntimeContainer{ + Created: time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC), Env: map[string]string{ "VIRTUAL_HOST": "baz.localhost", }, ID: "3", } o3 := &context.RuntimeContainer{ - Env: map[string]string{}, - ID: "8", + Created: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + Env: map[string]string{}, + ID: "8", } containers := []*context.RuntimeContainer{o0, o1, o2, o3} @@ -54,6 +88,8 @@ func TestSortObjectsByKeys(t *testing.T) { {"Asc complex", sortObjectsByKeysAsc, "Env.VIRTUAL_HOST", []interface{}{o3, o0, o2, o1}}, {"Desc simple", sortObjectsByKeysDesc, "ID", []interface{}{o0, o3, o2, o1}}, {"Desc complex", sortObjectsByKeysDesc, "Env.VIRTUAL_HOST", []interface{}{o1, o2, o0, o3}}, + {"Asc time", sortObjectsByKeysAsc, "Created", []interface{}{o3, o0, o2, o1}}, + {"Desc time", sortObjectsByKeysDesc, "Created", []interface{}{o1, o2, o0, o3}}, } { t.Run(tc.desc, func(t *testing.T) { got, err := tc.fn(containers, tc.key)