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: adds the ability to change the default tag name (#27) #28

Merged
merged 2 commits into from
Jul 24, 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
145 changes: 145 additions & 0 deletions example_with_custom_tags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package faker_test

import (
"fmt"

"github.com/go-faker/faker/v4"
"github.com/go-faker/faker/v4/pkg/options"
)

// SomeStructWithTags ...
type SomeStructWithACustomTagName struct {
Latitude float32 `custom:"lat"`
Longitude float32 `custom:"long"`
RealAddress faker.RealAddress `custom:"real_address"`
CreditCardNumber string `custom:"cc_number"`
CreditCardType string `custom:"cc_type"`
Email string `custom:"email"`
DomainName string `custom:"domain_name"`
IPV4 string `custom:"ipv4"`
IPV6 string `custom:"ipv6"`
Password string `custom:"password"`
Jwt string `custom:"jwt"`
PhoneNumber string `custom:"phone_number"`
MacAddress string `custom:"mac_address"`
URL string `custom:"url"`
UserName string `custom:"username"`
TollFreeNumber string `custom:"toll_free_number"`
E164PhoneNumber string `custom:"e_164_phone_number"`
TitleMale string `custom:"title_male"`
TitleFemale string `custom:"title_female"`
FirstName string `custom:"first_name"`
FirstNameMale string `custom:"first_name_male"`
FirstNameFemale string `custom:"first_name_female"`
LastName string `custom:"last_name"`
Name string `custom:"name"`
UnixTime int64 `custom:"unix_time"`
Date string `custom:"date"`
Time string `custom:"time"`
MonthName string `custom:"month_name"`
Year string `custom:"year"`
DayOfWeek string `custom:"day_of_week"`
DayOfMonth string `custom:"day_of_month"`
Timestamp string `custom:"timestamp"`
Century string `custom:"century"`
TimeZone string `custom:"timezone"`
TimePeriod string `custom:"time_period"`
Word string `custom:"word"`
Sentence string `custom:"sentence"`
Paragraph string `custom:"paragraph"`
Currency string `custom:"currency"`
Amount float64 `custom:"amount"`
AmountWithCurrency string `custom:"amount_with_currency"`
UUIDHypenated string `custom:"uuid_hyphenated"`
UUID string `custom:"uuid_digit"`
Skip string `custom:"-"`
PaymentMethod string `custom:"oneof: cc, paypal, check, money order"` // oneof will randomly pick one of the comma-separated values supplied in the tag
AccountID int `custom:"oneof: 15, 27, 61"` // use commas to separate the values for now. Future support for other separator characters may be added
Price32 float32 `custom:"oneof: 4.95, 9.99, 31997.97"`
Price64 float64 `custom:"oneof: 47463.9463525, 993747.95662529, 11131997.978767990"`
NumS64 int64 `custom:"oneof: 1, 2"`
NumS32 int32 `custom:"oneof: -3, 4"`
NumS16 int16 `custom:"oneof: -5, 6"`
NumS8 int8 `custom:"oneof: 7, -8"`
NumU64 uint64 `custom:"oneof: 9, 10"`
NumU32 uint32 `custom:"oneof: 11, 12"`
NumU16 uint16 `custom:"oneof: 13, 14"`
NumU8 uint8 `custom:"oneof: 15, 16"`
NumU uint `custom:"oneof: 17, 18"`
PtrNumU *uint `custom:"oneof: 19, 20"`
}

func Example_withTagsAndCustomTagName() {

a := SomeStructWithTags{}
// just set the custom tag name option
err := faker.FakeData(&a, options.WithTagName("custom"))
if err != nil {
fmt.Println(err)
}
fmt.Printf("%+v", a)
/*
Result:
{
Latitude: 81.12195
Longitude: -84.38158
RealAddress: {Address:107 Guaymas Place City:Davis State:CA PostalCode:95616 Coordinates:{Latitude:38.567048 Longitude:-121.746046}}
CreditCardType: American Express
CreditCardNumber: 373641309057568
Email: [email protected]
DomainName: FWZcaRE.ru,
IPV4: 99.23.42.63
IPV6: 975c:fb2c:2133:fbdd:beda:282e:1e0a:ec7d
Password: dfJdyHGuVkHBgnHLQQgpINApynzexnRpgIKBpiIjpTPOmNyMFb
Jwt: HDMNSOKhEIYkPIuHcVjfCtHlKkaqLGrUEqjKVkgR.HDMNSOKhEIYkPIuHcVjfCtHlKkaqLGrUEqjKVkgR.HDMNSOKhEIYkPIuHcVjfCtHlKkaqLGrUEqjKVkgR
PhoneNumber: 792-153-4861
MacAddress: cd:65:e1:d4:76:c6
URL: https://www.oEuqqAY.org/QgqfOhd
UserName: lVxELHS
TollFreeNumber: (777) 831-964572
E164PhoneNumber: +724891571063
TitleMale: Mr.
TitleFemale: Queen
FirstName: Whitney
FirstNameMale: Kenny
FirstNameFemale: Jana
LastName: Rohan
Name: Miss Casandra Kiehn
UnixTime: 1197930901
Date: 1982-02-27
Time: 03:10:25
MonthName: February
Year: 1996
DayOfWeek: Sunday
DayOfMonth: 20
Timestamp: 1973-06-21 14:50:46
Century: IV
TimeZone: Canada/Eastern
TimePeriod: AM
Word: nesciunt
Sentence: Consequatur perferendis aut sit voluptatem accusantium.
Paragraph: Aut consequatur sit perferendis accusantium voluptatem. Accusantium perferendis consequatur voluptatem sit aut. Aut sit accusantium consequatur voluptatem perferendis. Perferendis voluptatem aut accusantium consequatur sit.
Currency: IRR,
Amount: 88.990000,
AmountWithCurrency: XBB 49257.100000,
UUIDHypenated: 8f8e4463-9560-4a38-9b0c-ef24481e4e27,
UUID: 90ea6479fd0e4940af741f0a87596b73,
PaymentMethod: paypal,
AccountID: 61,
Price32: 4.95,
Price64: 993747.95662529
NumS64: 1
NumS32: -3
NumS16: 5
NumS8: -8
NumU64: 9
NumU32: 11
NumU16: 13
NumU8: 15
NumU: 17
PtrNumU: 19
Skip:
}
*/

}
5 changes: 2 additions & 3 deletions faker.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const (
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
maxRetry = 10000 // max number of retry for unique values
tagName = "faker"
keep = "keep"
unique = "unique"
ID = "uuid_digit"
Expand Down Expand Up @@ -443,7 +442,7 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error)
continue
}

tags := decodeTags(t, i)
tags := decodeTags(t, i, opts.TagName)
switch {
case tags.keepOriginal:
zero, err := isZero(reflect.ValueOf(a).Field(i))
Expand Down Expand Up @@ -606,7 +605,7 @@ func isZero(field reflect.Value) (bool, error) {
return reflect.Zero(field.Type()).Interface() == field.Interface(), nil
}

func decodeTags(typ reflect.Type, i int) structTag {
func decodeTags(typ reflect.Type, i int, tagName string) structTag {
tagField := typ.Field(i).Tag.Get(tagName)
tags := strings.Split(tagField, ",")

Expand Down
125 changes: 80 additions & 45 deletions faker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,51 +251,51 @@ type CStruct struct {
}

type TaggedStruct struct {
Latitude float32 `faker:"lat"`
Longitude float32 `faker:"long"`
CreditCardNumber string `faker:"cc_number"`
CreditCardType string `faker:"cc_type"`
Email string `faker:"email"`
DomainName string `faker:"domain_name"`
IPV4 string `faker:"ipv4"`
IPV6 string `faker:"ipv6"`
Password string `faker:"password"`
Jwt string `faker:"jwt"`
PhoneNumber string `faker:"phone_number"`
MacAddress string `faker:"mac_address"`
URL string `faker:"url"`
UserName string `faker:"username"`
TollFreeNumber string `faker:"toll_free_number"`
E164PhoneNumber string `faker:"e_164_phone_number"`
TitleMale string `faker:"title_male"`
TitleFemale string `faker:"title_female"`
FirstName string `faker:"first_name"`
FirstNameMale string `faker:"first_name_male"`
FirstNameFemale string `faker:"first_name_female"`
LastName string `faker:"last_name"`
Name string `faker:"name"`
ChineseFirstName string `faker:"chinese_first_name"`
ChineseLastName string `faker:"chinese_last_name"`
ChineseName string `faker:"chinese_name"`
UnixTime int64 `faker:"unix_time"`
Date string `faker:"date"`
Time string `faker:"time"`
MonthName string `faker:"month_name"`
Year string `faker:"year"`
DayOfWeek string `faker:"day_of_week"`
DayOfMonth string `faker:"day_of_month"`
Timestamp string `faker:"timestamp"`
Century string `faker:"century"`
TimeZone string `faker:"timezone"`
TimePeriod string `faker:"time_period"`
Word string `faker:"word"`
Sentence string `faker:"sentence"`
Paragraph string `faker:"paragraph"`
Currency string `faker:"currency"`
Amount float32 `faker:"amount"`
AmountWithCurrency string `faker:"amount_with_currency"`
ID string `faker:"uuid_digit"`
HyphenatedID string `faker:"uuid_hyphenated"`
Latitude float32 `faker:"lat" custom_tag_name:"lat" `
Longitude float32 `faker:"long" custom_tag_name:"long"`
CreditCardNumber string `faker:"cc_number" custom_tag_name:"cc_number"`
CreditCardType string `faker:"cc_type" custom_tag_name:"cc_type"`
Email string `faker:"email" custom_tag_name:"email"`
DomainName string `faker:"domain_name" custom_tag_name:"domain_name"`
IPV4 string `faker:"ipv4" custom_tag_name:"ipv4"`
IPV6 string `faker:"ipv6" custom_tag_name:"ipv6"`
Password string `faker:"password" custom_tag_name:"password"`
Jwt string `faker:"jwt" custom_tag_name:"jwt"`
PhoneNumber string `faker:"phone_number" custom_tag_name:"phone_number"`
MacAddress string `faker:"mac_address" custom_tag_name:"mac_address"`
URL string `faker:"url" custom_tag_name:"url"`
UserName string `faker:"username" custom_tag_name:"username"`
TollFreeNumber string `faker:"toll_free_number" custom_tag_name:"toll_free_number"`
E164PhoneNumber string `faker:"e_164_phone_number" custom_tag_name:"e_164_phone_number"`
TitleMale string `faker:"title_male" custom_tag_name:"title_male"`
TitleFemale string `faker:"title_female" custom_tag_name:"title_female"`
FirstName string `faker:"first_name" custom_tag_name:"first_name"`
FirstNameMale string `faker:"first_name_male" custom_tag_name:"first_name_male"`
FirstNameFemale string `faker:"first_name_female" custom_tag_name:"first_name_female"`
LastName string `faker:"last_name" custom_tag_name:"last_name"`
Name string `faker:"name" custom_tag_name:"name"`
ChineseFirstName string `faker:"chinese_first_name" custom_tag_name:"chinese_first_name"`
ChineseLastName string `faker:"chinese_last_name" custom_tag_name:"chinese_last_name"`
ChineseName string `faker:"chinese_name" custom_tag_name:"chinese_name"`
UnixTime int64 `faker:"unix_time" custom_tag_name:"unix_time"`
Date string `faker:"date" custom_tag_name:"date"`
Time string `faker:"time" custom_tag_name:"time"`
MonthName string `faker:"month_name" custom_tag_name:"month_name"`
Year string `faker:"year" custom_tag_name:"year"`
DayOfWeek string `faker:"day_of_week" custom_tag_name:"day_of_week"`
DayOfMonth string `faker:"day_of_month" custom_tag_name:"day_of_month"`
Timestamp string `faker:"timestamp" custom_tag_name:"timestamp"`
Century string `faker:"century" custom_tag_name:"century"`
TimeZone string `faker:"timezone" custom_tag_name:"timezone"`
TimePeriod string `faker:"time_period" custom_tag_name:"time_period"`
Word string `faker:"word" custom_tag_name:"word"`
Sentence string `faker:"sentence" custom_tag_name:"sentence"`
Paragraph string `faker:"paragraph" custom_tag_name:"paragraph"`
Currency string `faker:"currency" custom_tag_name:"currency"`
Amount float32 `faker:"amount" custom_tag_name:"amount"`
AmountWithCurrency string `faker:"amount_with_currency" custom_tag_name:"amount_with_currency"`
ID string `faker:"uuid_digit" custom_tag_name:"uuid_digit"`
HyphenatedID string `faker:"uuid_hyphenated" custom_tag_name:"uuid_hyphenated"`
}

func (t TaggedStruct) String() string {
Expand Down Expand Up @@ -2324,6 +2324,41 @@ func TestRandomMapSliceSize(t *testing.T) {
}
}

func TestWithTagName(t *testing.T) {
a := TaggedStruct{}
if err := FakeData(&a, options.WithTagName("custom_tag_name")); err != nil {
t.Error(err)
}

if err := assertAllStructFieldsNonZero(a); err != nil {
t.Error(err)
}
}

// assertAllStructFieldsNonZero asserts that the given struct s has all fields set to a non-zero value.
// s must be a struct or a pointer to a struct, the function panics otherwise.
func assertAllStructFieldsNonZero(s interface{}) error {
v := reflect.ValueOf(s)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}

if v.Kind() != reflect.Struct {
panic("passed interface is not a struct or a pointer to a struct")
}

for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
zero := reflect.Zero(field.Type())

if reflect.DeepEqual(field.Interface(), zero.Interface()) {
return fmt.Errorf("field '%s' has a zero value", v.Type().Field(i).Name)
}
}

return nil
}

func TestWithFieldsToIgnore(t *testing.T) {
a := AStruct{}
if err := FakeData(&a, options.WithFieldsToIgnore("Height", "Name")); err != nil {
Expand Down
23 changes: 12 additions & 11 deletions pkg/errors/generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ package errors
// ErrMoreArguments: Error on passing more arguments
// ErrNotSupportedPointer: Error when passing unsupported pointer
var (
ErrUnsupportedKindPtr = "Unsupported kind: %s Change Without using * (pointer) in Field of %s"
ErrUnsupportedKind = "Unsupported kind: %s"
ErrValueNotPtr = "Not a pointer value"
ErrTagNotSupported = "Tag unsupported: %s"
ErrTagAlreadyExists = "Tag exists"
ErrTagDoesNotExist = "Tag does not exist"
ErrMoreArguments = "Passed more arguments than is possible : (%d)"
ErrNotSupportedPointer = "Use sample:=new(%s)\n faker.FakeData(sample) instead"
ErrSmallerThanZero = "Size:%d is smaller than zero."
ErrSmallerThanOne = "Size:%d is smaller than one."
ErrUniqueFailure = "Failed to generate a unique value for field \"%s\""
ErrUnsupportedKindPtr = "Unsupported kind: %s Change Without using * (pointer) in Field of %s"
ErrUnsupportedKind = "Unsupported kind: %s"
ErrValueNotPtr = "Not a pointer value"
ErrTagNotSupported = "Tag unsupported: %s"
ErrTagAlreadyExists = "Tag exists"
ErrTagDoesNotExist = "Tag does not exist"
ErrFieldTagIdentifierInvalid = "Field tag identifier invalid"
ErrMoreArguments = "Passed more arguments than is possible : (%d)"
ErrNotSupportedPointer = "Use sample:=new(%s)\n faker.FakeData(sample) instead"
ErrSmallerThanZero = "Size:%d is smaller than zero."
ErrSmallerThanOne = "Size:%d is smaller than one."
ErrUniqueFailure = "Failed to generate a unique value for field \"%s\""

ErrStartValueBiggerThanEnd = "Start value can not be bigger than end value."
ErrWrongFormattedTag = "Tag \"%s\" is not written properly"
Expand Down
14 changes: 14 additions & 0 deletions pkg/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ type Options struct {
RandomIntegerBoundary *interfaces.RandomIntegerBoundary
// RandomFloatBoundary sets the boundary for random float value generation. Boundaries should comply with float values constraints (IEEE 754)
RandomFloatBoundary *interfaces.RandomFloatBoundary
// SetTagName sets the tag name that should be used
TagName string
}

// MaxDepthOption used for configuring the max depth of nested struct for faker
Expand Down Expand Up @@ -106,6 +108,7 @@ func DefaultOption() *Options {
ops.MaxGenerateStringRetries = 1000000 //default
ops.RandomIntegerBoundary = (*interfaces.RandomIntegerBoundary)(atomic.LoadPointer(&iBoundary))
ops.RandomFloatBoundary = &interfaces.DefaultFloatBoundary
ops.TagName = "faker"
return ops
}

Expand Down Expand Up @@ -236,6 +239,17 @@ func WithRandomFloatBoundaries(boundary interfaces.RandomFloatBoundary) OptionFu
}
}

// WithTagName sets the tag name to use. Default tag name is 'faker'.
func WithTagName(tagName string) OptionFunc {
if tagName == "" {
err := errors.New(fakerErrors.ErrFieldTagIdentifierInvalid)
panic(err)
}
return func(oo *Options) {
oo.TagName = tagName
}
}

// SetGenerateUniqueValues allows to set the single fake data generator functions to generate unique data.
func SetGenerateUniqueValues(unique bool) {
generateUniqueValues.Store(unique)
Expand Down