Skip to content

Commit

Permalink
feat: sortObjectsByKeys with types other than string
Browse files Browse the repository at this point in the history
  • Loading branch information
buchdag committed Dec 19, 2023
1 parent 0aff309 commit 2c152ba
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 9 deletions.
44 changes: 37 additions & 7 deletions internal/template/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package template
import (
"reflect"
"sort"
"strconv"
"time"
)

// sortStrings returns a sorted array of strings in increasing order
Expand Down Expand Up @@ -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
Expand Down
40 changes: 38 additions & 2 deletions internal/template/sort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package template

import (
"testing"
"time"

"github.com/nginx-proxy/docker-gen/internal/context"
"github.com/stretchr/testify/assert"
Expand All @@ -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}

Expand All @@ -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)
Expand Down

0 comments on commit 2c152ba

Please sign in to comment.