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

feat: change/assign faked values by implementing transformable interface #359

Closed
wants to merge 4 commits into from
Closed
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
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,58 @@ fmt.Printf("%s", f.Name) // billy
fmt.Printf("%s", f.Age) // 1990-12-07 04:14:25.685339029 +0000 UTC
```

## Recipient types

Assignment of generated values can be controlled by implementing the
`Recipient` interface. This is useful when working with wrapper types,
and you want to control the generation of values for the wrapped type (the "receptor").

Types implementing `Recipient` are initially populated with faked values
just like any other type.
The value of their receptor is then overwritten, which allows combining
`Recipient` with `Fakeable`, or controlling the generation of faked
values for other non-receptor fields.
```go
type Optional[T any] struct {
IsSet bool
Value T
}

func (o *Optional[T]) Fake(faker *Faker) (any, error) {
return Optional[T]{
IsSet: true,
}, nil
}

func (o *Optional[T]) Receptor() reflect.Value {
return reflect.ValueOf(&o.Value)
}

type Foo struct {
OptEmail Optional[string] `fake:"{email}" fakesize:"3"` // {true [email protected]}
OptSentences Optional[[]string] `fake:"{sentence:3}" fakesize:"3"` // {true [Rather daughter including. As whereas late. My as yet.]
OptMap Optional[map[string]int] `fakesize:"2"` // {true map[MuPIU:1906369721605615517 mOItdWIG:5095924209814061526]}
Skip Optional[int] `fake:"-"` // {false 0}
}
```

## Transformable types
Types can implement the `Transformable` interface, allowing to modify their value
in arbitrary ways after their generation.

```go
type Prefixer string

func (s *Prefixer) FakeTransform(f *Faker) error {
*s = Prefixer(fmt.Sprintf("PREFIX.%s", *s))
return nil
}

type Foo struct {
Bar Prefixer `fake:"{word}"` // "PREFIX.Hello"
}
```

## Custom Functions

In a lot of situations you may need to use your own random function usage for your specific needs.
Expand Down
44 changes: 44 additions & 0 deletions faker_recipient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package gofakeit

import (
"errors"
"fmt"
"reflect"
)

// Recipient interface can be implemented by wrapper types to define which field
// will be set with faked values.
type Recipient interface {
// Receptor method implementation MUST have pointer receiver,
// e.g. func (r *R) Receptor().
//
// Returned value MUST have kind reflect.Pointer, or it will not be settable
Receptor() reflect.Value
}

func isRecipient(t reflect.Type) bool {
receiverType := reflect.TypeOf((*Recipient)(nil)).Elem()
return t.Implements(receiverType) || reflect.PointerTo(t).Implements(receiverType)
}

func callReceptor(t reflect.Type, v reflect.Value) (reflect.Value, error) {
var receptor reflect.Value
if t.Kind() == reflect.Pointer {
// Pointer elements need to be initialized first
ptrValue := reflect.New(t.Elem())
v.Set(ptrValue)
receptor = ptrValue.Interface().(Recipient).Receptor()
} else {
receptor = v.Addr().Interface().(Recipient).Receptor()
}
if receptor.Kind() != reflect.Pointer {
return reflect.Value{}, errors.New("method Receiver MUST return a pointer when implementing Receiver interface")
}
if !receptor.Elem().CanSet() {
return reflect.Value{}, fmt.Errorf("Receptor for type %s is not settable", t)
}
// Set zero to overwrite default generation.
// This is needed to preserve faked values generation for other potential non-receptor fields.
receptor.Elem().SetZero()
return receptor, nil
}
Loading
Loading