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

fix: unexpected logic issue with the ignore interface option #36

Merged
merged 5 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.17
go-version: 1.18

- name: Linter
run: make lint-prepare && make lint
Expand All @@ -31,7 +31,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.17
go-version: 1.18

- name: Test
run: go test -v ./... -gcflags=all=-l -coverprofile=coverage.txt -covermode=atomic -race
Expand Down
10 changes: 6 additions & 4 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ linters-settings:
golint:
min-confidence: 0.8
gocyclo:
min-complexity: 60
min-complexity: 70
maligned:
suggest-new: true
dupl:
Expand All @@ -27,7 +27,7 @@ linters:
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- deadcode
# - deadcode
- errcheck
- goconst
- gocyclo
Expand All @@ -37,12 +37,12 @@ linters:
- ineffassign
- misspell
- staticcheck
- structcheck
# - structcheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
# - varcheck

issues:
exclude-rules:
Expand All @@ -52,6 +52,8 @@ issues:
- path: datetime.go
linters:
- misspell
- path: .
text: "parameter 'v' seems to be unused"
run:
timeout: 5m
go: "1.17"
Expand Down
167 changes: 101 additions & 66 deletions faker.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,55 +311,60 @@ func FakeData(a interface{}, opt ...options.OptionFunc) error {
if err != nil {
return err
}
if finalValue.Kind() == reflect.Invalid {
return nil

if rval.Elem().CanSet() && rval.Elem().
CanConvert(reflectType.Elem()) {
rval.Elem().Set(finalValue.Elem().
Convert(reflectType.Elem()))
}
rval.Elem().Set(finalValue.Elem().Convert(reflectType.Elem()))
return nil
}

// AddProvider extend faker with tag to generate fake data with specified custom algorithm
// Example:
// type Gondoruwo struct {
// Name string
// Locatadata int
// }
//
// type Sample struct {
// ID int64 `faker:"customIdFaker"`
// Gondoruwo Gondoruwo `faker:"gondoruwo"`
// Danger string `faker:"danger"`
// }
// type Gondoruwo struct {
// Name string
// Locatadata int
// }
//
// func CustomGenerator() {
// // explicit
// faker.AddProvider("customIdFaker", func(v reflect.Value) (interface{}, error) {
// return int64(43), nil
// })
// // functional
// faker.AddProvider("danger", func() faker.TaggedFunction {
// return func(v reflect.Value) (interface{}, error) {
// return "danger-ranger", nil
// }
// }())
// faker.AddProvider("gondoruwo", func(v reflect.Value) (interface{}, error) {
// obj := Gondoruwo{
// Name: "Power",
// Locatadata: 324,
// }
// return obj, nil
// })
// }
// type Sample struct {
// ID int64 `faker:"customIdFaker"`
// Gondoruwo Gondoruwo `faker:"gondoruwo"`
// Danger string `faker:"danger"`
// }
//
// func main() {
// CustomGenerator()
// var sample Sample
// faker.FakeData(&sample)
// fmt.Printf("%+v", sample)
// }
// func CustomGenerator() {
// // explicit
// faker.AddProvider("customIdFaker", func(v reflect.Value) (interface{}, error) {
// return int64(43), nil
// })
// // functional
// faker.AddProvider("danger", func() faker.TaggedFunction {
// return func(v reflect.Value) (interface{}, error) {
// return "danger-ranger", nil
// }
// }())
// faker.AddProvider("gondoruwo", func(v reflect.Value) (interface{}, error) {
// obj := Gondoruwo{
// Name: "Power",
// Locatadata: 324,
// }
// return obj, nil
// })
// }
//
// func main() {
// CustomGenerator()
// var sample Sample
// faker.FakeData(&sample)
// fmt.Printf("%+v", sample)
// }
//
// Will print
// {ID:43 Gondoruwo:{Name:Power Locatadata:324} Danger:danger-ranger}
//
// {ID:43 Gondoruwo:{Name:Power Locatadata:324} Danger:danger-ranger}
//
// Notes: when using a custom provider make sure to return the same type as the field
func AddProvider(tag string, provider interfaces.TaggedFunction) error {
if _, ok := mapperTag.Load(tag); ok {
Expand All @@ -380,8 +385,8 @@ func RemoveProvider(tag string) error {
return nil
}

func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error) {
t := reflect.TypeOf(a)
func getFakedValue(item interface{}, opts *options.Options) (reflect.Value, error) {
t := reflect.TypeOf(item)
if t == nil {
if opts.IgnoreInterface {
return reflect.ValueOf(nil), nil
Expand All @@ -396,32 +401,36 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error)
opts.MaxDepthOption.ForgetType(t)
}()
k := t.Kind()

switch k {
case reflect.Ptr:
v := reflect.New(t.Elem())
var val reflect.Value
var err error
if a != reflect.Zero(reflect.TypeOf(a)).Interface() {
val, err = getFakedValue(reflect.ValueOf(a).Elem().Interface(), opts)
if item != reflect.Zero(reflect.TypeOf(item)).Interface() {
val, err = getFakedValue(reflect.ValueOf(item).Elem().Interface(), opts)

} else {
val, err = getFakedValue(v.Elem().Interface(), opts)
}
if err != nil {
return reflect.Value{}, err
}
if val.Kind() == reflect.Invalid {
return val, nil

if reflect.ValueOf(val).IsZero() {
return v, nil
}

if v.Elem().CanSet() && v.Elem().CanConvert(t.Elem()) {
v.Elem().Set(val.Convert(t.Elem()))
}
v.Elem().Set(val.Convert(t.Elem()))
return v, nil
case reflect.Struct:
switch t.String() {
case "time.Time":
ft := time.Now().Add(time.Duration(rand.Int63()))
return reflect.ValueOf(ft), nil
default:
originalDataVal := reflect.ValueOf(a)
originalDataVal := reflect.ValueOf(item)
v := reflect.New(t).Elem()
retry := 0 // error if cannot generate unique value after maxRetry tries
for i := 0; i < v.NumField(); i++ {
Expand All @@ -445,7 +454,7 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error)
tags := decodeTags(t, i, opts.TagName)
switch {
case tags.keepOriginal:
zero, err := isZero(reflect.ValueOf(a).Field(i))
zero, err := isZero(reflect.ValueOf(item).Field(i))
if err != nil {
return reflect.Value{}, err
}
Expand All @@ -456,20 +465,24 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error)
}
continue
}
v.Field(i).Set(reflect.ValueOf(a).Field(i))
v.Field(i).Set(reflect.ValueOf(item).Field(i))
case tags.fieldType == "":
val, err := getFakedValue(v.Field(i).Interface(), opts)
if err != nil {
return reflect.Value{}, err
}
if val.Kind() != reflect.Invalid {
val = val.Convert(v.Field(i).Type())
v.Field(i).Set(val)

if v.Field(i).CanSet() {
if !reflect.ValueOf(val).IsZero() && val.CanConvert(v.Field(i).Type()) {
val = val.Convert(v.Field(i).Type())
v.Field(i).Set(val)
}

}
case tags.fieldType == SKIP:
item := originalDataVal.Field(i).Interface()
if v.CanSet() && item != nil {
v.Field(i).Set(reflect.ValueOf(item))
data := originalDataVal.Field(i).Interface()
if v.CanSet() && data != nil {
v.Field(i).Set(reflect.ValueOf(data))
}
default:
err := setDataWithTag(v.Field(i).Addr(), tags.fieldType, *opts)
Expand All @@ -479,9 +492,8 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error)
}

if tags.unique {

if retry >= maxRetry {
return reflect.Value{}, fmt.Errorf(fakerErrors.ErrUniqueFailure, reflect.TypeOf(a).Field(i).Name)
return reflect.Value{}, fmt.Errorf(fakerErrors.ErrUniqueFailure, reflect.TypeOf(item).Field(i).Name)
}

value := v.Field(i).Interface()
Expand All @@ -504,18 +516,26 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error)
res, err := randomString(opts.RandomStringLength, *opts)
return reflect.ValueOf(res), err
case reflect.Slice:

length := randomSliceAndMapSize(*opts)
if opts.SetSliceMapNilIfLenZero && length == 0 {
return reflect.Zero(t), nil
}
v := reflect.MakeSlice(t, length, length)
for i := 0; i < v.Len(); i++ {
for i := 0; i < length; i++ {
val, err := getFakedValue(v.Index(i).Interface(), opts)
if err != nil {
return reflect.Value{}, err
}
val = val.Convert(v.Index(i).Type())
v.Index(i).Set(val)
// if the value generated is NIL/Zero
// it will kept it as nil
if reflect.ValueOf(val).IsZero() {
continue
}
if val.CanConvert(v.Index(i).Type()) {
val = val.Convert(v.Index(i).Type())
v.Index(i).Set(val)
}
}
return v, nil
case reflect.Array:
Expand All @@ -525,7 +545,12 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error)
if err != nil {
return reflect.Value{}, err
}
val = val.Convert(v.Index(i).Type())
if reflect.ValueOf(val).IsZero() {
continue
}
if val.CanConvert(v.Index(i).Type()) {
val = val.Convert(v.Index(i).Type())
}
v.Index(i).Set(val)
}
return v, nil
Expand Down Expand Up @@ -580,6 +605,13 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error)
if err != nil {
return reflect.Value{}, err
}

keyIsZero := reflect.ValueOf(key).IsZero()
valIsZero := reflect.ValueOf(val).IsZero()

if keyIsZero || valIsZero {
continue
}
key = key.Convert(t.Key())
val = val.Convert(v.Type().Elem())
v.SetMapIndex(key, val)
Expand Down Expand Up @@ -909,11 +941,13 @@ func userDefinedNumber(v reflect.Value, tag string) error {
return fmt.Errorf(fakerErrors.ErrTagNotSupported, tag)
}

v.Set(reflect.ValueOf(res).Convert(v.Type()))
if v.CanSet() && v.CanConvert(v.Type()) {
v.Set(reflect.ValueOf(res).Convert(v.Type()))
}
return nil
}

//extractSliceLengthFromTag checks if the sliceLength tag 'slice_len' is set, if so, returns its value, else return a random length
// extractSliceLengthFromTag checks if the sliceLength tag 'slice_len' is set, if so, returns its value, else return a random length
func extractSliceLengthFromTag(tag string, opt options.Options) (int, error) {
if strings.Contains(tag, SliceLength) {
lenParts := strings.SplitN(findSliceLenReg.FindString(tag), Equals, -1)
Expand Down Expand Up @@ -1289,9 +1323,10 @@ func randomStringNumber(n int) string {

// RandomInt Get three parameters , only first mandatory and the rest are optional
// (minimum_int, maximum_int, count)
// If only set one parameter : An integer greater than minimum_int will be returned
// If only set two parameters : All integers between minimum_int and maximum_int will be returned, in a random order.
// If three parameters: `count` integers between minimum_int and maximum_int will be returned.
//
// If only set one parameter : An integer greater than minimum_int will be returned
// If only set two parameters : All integers between minimum_int and maximum_int will be returned, in a random order.
// If three parameters: `count` integers between minimum_int and maximum_int will be returned.
func RandomInt(parameters ...int) (p []int, err error) {
switch len(parameters) {
case 1:
Expand Down
Loading