Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to support slice of struct? #9

Closed
ludanfeng opened this issue Sep 28, 2018 · 4 comments
Closed

How to support slice of struct? #9

ludanfeng opened this issue Sep 28, 2018 · 4 comments

Comments

@ludanfeng
Copy link

ludanfeng commented Sep 28, 2018

For example:

type confModule struct {
	Name   string   `yaml:"name" default:"abc"`
	Params []string `yaml:"params" default:"[\"-c\", \"conf.yml\"]"`
}

type confStruct struct {
	ID     string       `yaml:"id"`
	Modules []confModule `yaml:"modules"`
}

defaults will skip the slice of struct (confStruct.Modules), how to support this case?

@creasty
Copy link
Owner

creasty commented Sep 28, 2018

Could you try this?

type confStruct struct {
	ID     string       `yaml:"id"`
	Modules []confModule `yaml:"modules" default:"[]"`
}

@ludanfeng
Copy link
Author

ludanfeng commented Sep 29, 2018

Could you try this?

type confStruct struct {
	ID     string       `yaml:"id"`
	Modules []confModule `yaml:"modules" default:"[]"`
}

Does not work. The following is the test result and case:
Diff:
--- Expected
+++ Actual
@@ -5,6 +5,3 @@
Name: (string) (len=2) "m1",
- Params: ([]string) (len=2) {
- (string) (len=2) "-c",
- (string) (len=8) "conf.yml"
- }
+ Params: ([]string)
},

package utils

import (
	"testing"

	"github.com/creasty/defaults"

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

type confModule struct {
	Name   string   `yaml:"name"`
	Params []string `yaml:"params" default:"[\"-c\", \"conf.yml\"]"`
}

type confStruct struct {
	Others  string       `yaml:"others"`
	Modules []confModule `yaml:"modules" default:"[]"`
}

func TestSetDefaults(t *testing.T) {
	tests := []struct {
		name    string
		args    *confStruct
		want    *confStruct
		wantErr bool
	}{
		{
			name: "defaults-struct-slice",
			args: &confStruct{
				Others: "others",
				Modules: []confModule{
					confModule{
						Name: "m1",
					},
					confModule{
						Name:   "m2",
						Params: []string{"arg1", "arg2"},
					},
				},
			},
			want: &confStruct{
				Others: "others",
				Modules: []confModule{
					confModule{
						Name:   "m1",
						Params: []string{"-c", "conf.yml"},
					},
					confModule{
						Name:   "m2",
						Params: []string{"arg1", "arg2"},
					},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := defaults.Set(tt.args); (err != nil) != tt.wantErr {
				t.Errorf("SetDefaults() error = %v, wantErr %v", err, tt.wantErr)
			}
			assert.Equal(t, tt.want, tt.args)
		})
	}
}

@ludanfeng
Copy link
Author

ludanfeng commented Sep 29, 2018

And my current workaround is:

package utils

import (
	"reflect"

	"github.com/creasty/defaults"
	"github.com/juju/errors"
)

// SetDefaults set default values
func SetDefaults(ptr interface{}) error {
	err := defaults.Set(ptr)
	if err != nil {
		return errors.Errorf("%v %s", ptr, err.Error())
	}

	v := reflect.ValueOf(ptr).Elem()
	t := v.Type()

	for i := 0; i < t.NumField(); i++ {
		if f := t.Field(i); f.Type.Kind() == reflect.Slice {
			slice := v.Field(i)
			for j := 0; j < slice.Len(); j++ {
				sliceItem := slice.Index(j)
				if sliceItem.Kind() != reflect.Struct {
					continue
				}
				sliceItemTemp := reflect.New(sliceItem.Type())
				sliceItemTemp.Elem().Set(sliceItem)
				err = SetDefaults(sliceItemTemp.Interface())
				if err != nil {
					return errors.Trace(err)
				}
				sliceItem.Set(sliceItemTemp.Elem())
			}
		}
	}
	return nil
}

@creasty
Copy link
Owner

creasty commented Jan 30, 2019

@creasty creasty closed this as completed Jan 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants