Skip to content

Commit

Permalink
CLOUDP-226171: handle safely pointers of slices in the output templat…
Browse files Browse the repository at this point in the history
…es (#2569)
  • Loading branch information
tibulca authored Jan 26, 2024
1 parent b628ab9 commit cbea179
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 1 deletion.
20 changes: 19 additions & 1 deletion internal/templatewriter/templatewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package templatewriter

import (
"io"
"reflect"
"text/tabwriter"
"text/template"
)
Expand All @@ -27,6 +28,23 @@ const (
tabwriterPadChar = ' '
)

var funcMap = template.FuncMap{
"valueOrEmptySlice": valueOrEmptySlice,
}

func valueOrEmptySlice(slice interface{}) (result interface{}) {
if slice == nil {
return result
}

k := reflect.TypeOf(slice).Kind()
if (k == reflect.Slice || k == reflect.Ptr) && reflect.ValueOf(slice).IsNil() {
return result
}

return slice
}

// newTabWriter returns a tabwriter that handles tabs(`\t) to space columns evenly.
func newTabWriter(output io.Writer) *tabwriter.Writer {
return tabwriter.NewWriter(output, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, 0)
Expand All @@ -37,7 +55,7 @@ func newTabWriter(output io.Writer) *tabwriter.Writer {
// this template will be handled with a tabwriter so you can use tabs (\t)
// and new lines (\n) to space your content evenly.
func Print(writer io.Writer, t string, v interface{}) error {
tmpl, err := template.New("output").Parse(t)
tmpl, err := template.New("output").Funcs(funcMap).Parse(t)
if err != nil {
return err
}
Expand Down
95 changes: 95 additions & 0 deletions internal/templatewriter/templatewriter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2024 MongoDB Inc
//
// 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 templatewriter

import (
"bytes"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type printTest struct {
name string
template string
data interface{}
expected string
wantErr require.ErrorAssertionFunc
}

func Test_Print(t *testing.T) {
var buf bytes.Buffer

tests := []printTest{
{
name: "primitive data",
template: "name: {{.}}",
data: "Jane",
expected: "name: Jane",
wantErr: require.NoError,
},
{
name: "nil data",
template: "name: {{.}}",
data: nil,
expected: "name: <no value>",
wantErr: require.NoError,
},
{
name: "pointer of non empty slice",
template: "items: {{range .Items}}{{.}} {{end}}",
data: struct{ Items *[]string }{Items: &[]string{"AWS", "GCP", "Azure"}},
expected: "items: AWS GCP Azure ",
wantErr: require.NoError,
},
{
name: "nil pointer of slice",
template: "items: {{range .Items}}{{.}} {{end}}",
data: struct{ Items *[]string }{Items: nil},
expected: "",
wantErr: require.Error, // expected to fail, as Items is nil
},
{
name: "nil pointer of slice",
template: "items: {{range valueOrEmptySlice .Items}}{{.}} {{end}}",
data: struct{ Items *[]string }{Items: nil},
expected: "items: ",
wantErr: require.NoError,
},
{
name: "non empty slice",
template: "items: {{range valueOrEmptySlice .Items}}{{.}} {{end}}",
data: struct{ Items []string }{Items: []string{"AWS", "GCP", "Azure"}},
expected: "items: AWS GCP Azure ",
wantErr: require.NoError,
},
{
name: "empty slice",
template: "items: {{range valueOrEmptySlice .Items}}{{.}} {{end}}",
data: struct{ Items []string }{Items: []string{}},
expected: "items: ",
wantErr: require.NoError,
},
}

for _, conf := range tests {
t.Run(conf.name, func(t *testing.T) {
conf.wantErr(t, Print(&buf, conf.template, conf.data))
assert.Equal(t, conf.expected, buf.String())
})
buf.Reset()
}
}

0 comments on commit cbea179

Please sign in to comment.