1package options
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"math/rand"
  7	"reflect"
  8	"sync/atomic"
  9	"time"
 10	"unsafe"
 11
 12	fakerErrors "github.com/go-faker/faker/v4/pkg/errors"
 13	"github.com/go-faker/faker/v4/pkg/interfaces"
 14)
 15
 16var (
 17	// global settings, read/write must be concurrent safe
 18	generateUniqueValues atomic.Value
 19	ignoreInterface      atomic.Value
 20	randomStringLen      int32 = 25
 21	lang                 unsafe.Pointer
 22	randomMaxSize        int32 = 100
 23	randomMinSize        int32
 24	iBoundary            unsafe.Pointer
 25)
 26
 27func init() {
 28	generateUniqueValues.Store(false)
 29	ignoreInterface.Store(false)
 30	lang = unsafe.Pointer(&interfaces.LangENG)
 31	iBoundary = unsafe.Pointer(&interfaces.DefaultIntBoundary)
 32}
 33
 34// Options represent all available option for faker.
 35type Options struct {
 36	// IgnoreFields used for ignoring a field when generating the fake data
 37	IgnoreFields map[string]struct{}
 38	// FieldProviders used for storing the custom provider function
 39	FieldProviders map[string]interfaces.CustomProviderFunction
 40	// StructTypeProviders used for storing the struct type of custom provider function
 41	StructTypeProviders map[reflect.Type]interfaces.CustomProviderFunction
 42	// MaxDepthOption used for configuring the max depth of nested identical structs for faker
 43	MaxDepthOption *MaxDepthOption
 44	// MaxFieldDepthOption used for configuring the max depth of fields that are filled for a struct
 45	MaxFieldDepthOption int
 46	// IgnoreInterface used for ignoring any interface field
 47	IgnoreInterface bool
 48	// StringLanguage used for setting the language for any string in faker
 49	StringLanguage *interfaces.LangRuneBoundary
 50	// GenerateUniqueValues to ensure the generated data is unique
 51	GenerateUniqueValues bool
 52	// RandomStringLength to ensure the generated string is expected as we want
 53	RandomStringLength int
 54	// RandomMaxSliceSize used for setting the maximum of slice size, or map size that will be generated
 55	RandomMaxSliceSize int
 56	// RandomMinSliceSize used for setting the minimum of slize, array, map size that will be generated
 57	RandomMinSliceSize int
 58	// MaxGenerateStringRetries set how much tries for generating random string
 59	MaxGenerateStringRetries int
 60	// SetSliceMapNilIfLenZero allows to set nil for the slice and maps, if size is 0.
 61	SetSliceMapNilIfLenZero bool
 62	// SetSliceMapRandomToZero sets random integer generation to zero for slice and maps
 63	SetSliceMapRandomToZero bool
 64	// RandomIntegerBoundary sets boundary random integer value generation. Boundaries can not exceed integer(4 byte...)
 65	RandomIntegerBoundary *interfaces.RandomIntegerBoundary
 66	// RandomFloatBoundary sets the boundary for random float value generation. Boundaries should comply with float values constraints (IEEE 754)
 67	RandomFloatBoundary *interfaces.RandomFloatBoundary
 68	// SetTagName sets the tag name that should be used
 69	TagName string
 70	// CustomDomain is used for specifying a custom domain when generating email
 71	CustomDomain *string
 72}
 73
 74// MaxDepthOption used for configuring the max depth of nested struct for faker
 75type MaxDepthOption struct {
 76	typeSeen          map[reflect.Type]int
 77	recursionMaxDepth int
 78}
 79
 80func (o *MaxDepthOption) RememberType(t reflect.Type) {
 81	o.typeSeen[t]++
 82}
 83
 84func (o *MaxDepthOption) ForgetType(t reflect.Type) {
 85	o.typeSeen[t]--
 86}
 87
 88func (o *MaxDepthOption) RecursionOutOfLimit(t reflect.Type) bool {
 89	return o.typeSeen[t] > o.recursionMaxDepth
 90}
 91
 92// BuildOptions build all option functions into one option
 93func BuildOptions(optFuncs []OptionFunc) *Options {
 94	ops := DefaultOption()
 95
 96	for _, optFunc := range optFuncs {
 97		optFunc(ops)
 98	}
 99
100	return ops
101}
102
103// DefaultOption build the default option
104func DefaultOption() *Options {
105	ops := &Options{}
106	ops.StructTypeProviders = make(map[reflect.Type]interfaces.CustomProviderFunction)
107	ops.StructTypeProviders[reflect.TypeOf(time.Time{})] = func() (interface{}, error) {
108		return time.Now().Add(time.Duration(rand.Int63())), nil
109	}
110	ops.MaxDepthOption = &MaxDepthOption{
111		typeSeen:          make(map[reflect.Type]int, 1),
112		recursionMaxDepth: 1,
113	}
114	ops.MaxFieldDepthOption = -1
115	ops.GenerateUniqueValues = generateUniqueValues.Load().(bool)
116	ops.IgnoreInterface = ignoreInterface.Load().(bool)
117	ops.StringLanguage = (*interfaces.LangRuneBoundary)(atomic.LoadPointer(&lang))
118	ops.RandomStringLength = int(atomic.LoadInt32(&randomStringLen))
119	ops.RandomMaxSliceSize = int(atomic.LoadInt32(&randomMaxSize))
120	ops.RandomMinSliceSize = int(atomic.LoadInt32(&randomMinSize))
121	ops.MaxGenerateStringRetries = 1000000 //default
122	ops.RandomIntegerBoundary = (*interfaces.RandomIntegerBoundary)(atomic.LoadPointer(&iBoundary))
123	ops.RandomFloatBoundary = &interfaces.DefaultFloatBoundary
124	ops.TagName = "faker"
125	return ops
126}
127
128// OptionFunc define the options contract
129type OptionFunc func(oo *Options)
130
131// WithFieldsToIgnore used for ignoring a field when generating the fake data
132func WithFieldsToIgnore(fieldNames ...string) OptionFunc {
133	return func(oo *Options) {
134		if oo.IgnoreFields == nil {
135			oo.IgnoreFields = make(map[string]struct{}, len(fieldNames))
136		}
137		for _, f := range fieldNames {
138			oo.IgnoreFields[f] = struct{}{}
139		}
140	}
141}
142
143// WithCustomDomain is used to set a custom domain for generating fake email
144func WithCustomDomain(domain string) OptionFunc {
145	return func(oo *Options) {
146		oo.CustomDomain = &domain
147	}
148}
149
150// WithCustomFieldProvider used for storing the custom provider function
151func WithCustomFieldProvider(fieldName string, provider interfaces.CustomProviderFunction) OptionFunc {
152	return func(oo *Options) {
153		if oo.FieldProviders == nil {
154			oo.FieldProviders = make(map[string]interfaces.CustomProviderFunction, 1)
155		}
156		oo.FieldProviders[fieldName] = provider
157	}
158}
159
160// WithRecursionMaxDepth used for configuring the max depth of recursion struct for faker
161func WithRecursionMaxDepth(depth uint) OptionFunc {
162	return func(oo *Options) {
163		if oo.MaxDepthOption == nil {
164			oo.MaxDepthOption = &MaxDepthOption{
165				recursionMaxDepth: 1, // default
166				typeSeen:          make(map[reflect.Type]int, 1),
167			}
168		}
169		oo.MaxDepthOption.recursionMaxDepth = int(depth)
170	}
171}
172
173// WithMaxFieldDepthOption used for configuring the max depth of fields that are filled for a struct
174func WithMaxFieldDepthOption(depth int) OptionFunc {
175	return func(oo *Options) {
176		oo.MaxFieldDepthOption = depth
177	}
178}
179
180// WithStructTypeProviders used for configuring the custom provider of struct type
181func WithStructTypeProviders(t interface{}, provider interfaces.CustomProviderFunction) OptionFunc {
182	if reflect.TypeOf(t).Kind() != reflect.Struct {
183		panic(fakerErrors.ErrOnlyStructTypeSupported)
184	}
185	return func(oo *Options) {
186		oo.StructTypeProviders[reflect.TypeOf(t)] = provider
187	}
188}
189
190// WithIgnoreInterface allows to set a flag to ignore found interface{}s.
191func WithIgnoreInterface(value bool) OptionFunc {
192	return func(oo *Options) {
193		oo.IgnoreInterface = value
194	}
195}
196
197// WithStringLanguage sets language of random string generation (LangENG, LangCHI, LangRUS, LangJPN, LangKOR, EmotEMJ)
198func WithStringLanguage(l interfaces.LangRuneBoundary) OptionFunc {
199	return func(oo *Options) {
200		oo.StringLanguage = &l
201	}
202}
203
204// WithGenerateUniqueValues allows to set the single fake data generator functions to generate unique data.
205func WithGenerateUniqueValues(unique bool) OptionFunc {
206	return func(oo *Options) {
207		oo.GenerateUniqueValues = unique
208	}
209}
210
211// WithRandomStringLength sets a length for random string generation
212func WithRandomStringLength(size uint) OptionFunc {
213	return func(oo *Options) {
214		oo.RandomStringLength = int(size)
215	}
216}
217
218// WithRandomMapAndSliceMaxSize sets the max size for maps and slices for random generation.
219func WithRandomMapAndSliceMaxSize(size uint) OptionFunc {
220	if size < 1 {
221		err := fmt.Errorf(fakerErrors.ErrSmallerThanOne, size)
222		panic(err)
223	}
224	return func(oo *Options) {
225		oo.RandomMaxSliceSize = int(size)
226	}
227}
228
229// WithRandomMapAndSliceMinSize sets the min size for maps and slices for random generation.
230func WithRandomMapAndSliceMinSize(size uint) OptionFunc {
231	return func(oo *Options) {
232		oo.RandomMinSliceSize = int(size)
233	}
234}
235
236// WithMaxGenerateStringRetries set how much tries for generating random string
237func WithMaxGenerateStringRetries(retries uint) OptionFunc {
238	return func(oo *Options) {
239		oo.MaxGenerateStringRetries = int(retries)
240	}
241}
242
243// WithNilIfLenIsZero allows to set nil for the slice and maps, if size is 0.
244func WithNilIfLenIsZero(setNil bool) OptionFunc {
245	return func(oo *Options) {
246		oo.SetSliceMapNilIfLenZero = setNil
247	}
248}
249
250// WithSliceMapRandomToZero Sets random integer generation to zero for slice and maps
251func WithSliceMapRandomToZero(setNumberToZero bool) OptionFunc {
252	return func(oo *Options) {
253		oo.SetSliceMapRandomToZero = setNumberToZero
254	}
255}
256
257// WithRandomIntegerBoundaries sets boundary random integer value generation. Boundaries can not exceed integer(4 byte...)
258func WithRandomIntegerBoundaries(boundary interfaces.RandomIntegerBoundary) OptionFunc {
259	if boundary.Start > boundary.End {
260		err := errors.New(fakerErrors.ErrStartValueBiggerThanEnd)
261		panic(err)
262	}
263	return func(oo *Options) {
264		oo.RandomIntegerBoundary = &boundary
265	}
266}
267
268// WithRandomFloatBoundaries sets the boundary for random float value generation. Boundaries should comply with float values constraints (IEEE 754)
269func WithRandomFloatBoundaries(boundary interfaces.RandomFloatBoundary) OptionFunc {
270	if boundary.Start > boundary.End {
271		err := errors.New(fakerErrors.ErrStartValueBiggerThanEnd)
272		panic(err)
273	}
274	return func(oo *Options) {
275		oo.RandomFloatBoundary = &boundary
276	}
277}
278
279// WithTagName sets the tag name to use. Default tag name is 'faker'.
280func WithTagName(tagName string) OptionFunc {
281	if tagName == "" {
282		err := errors.New(fakerErrors.ErrFieldTagIdentifierInvalid)
283		panic(err)
284	}
285	return func(oo *Options) {
286		oo.TagName = tagName
287	}
288}
289
290// SetGenerateUniqueValues allows to set the single fake data generator functions to generate unique data.
291func SetGenerateUniqueValues(unique bool) {
292	generateUniqueValues.Store(unique)
293}
294
295// SetIgnoreInterface allows to set a flag to ignore found interface{}s.
296func SetIgnoreInterface(ignore bool) {
297	ignoreInterface.Store(ignore)
298}
299
300// SetRandomStringLength sets a length for random string generation
301func SetRandomStringLength(size int) error {
302	if size < 0 {
303		return fmt.Errorf(fakerErrors.ErrSmallerThanZero, size)
304	}
305	atomic.StoreInt32(&randomStringLen, int32(size))
306	return nil
307}
308
309// SetStringLang sets language of random string generation (LangENG, LangCHI, LangRUS, LangJPN, LangKOR, EmotEMJ)
310func SetStringLang(l interfaces.LangRuneBoundary) {
311	atomic.StorePointer(&lang, unsafe.Pointer(&l))
312}
313
314// SetRandomMapAndSliceSize sets the size for maps and slices for random generation.
315// deprecates, currently left for old version usage
316func SetRandomMapAndSliceSize(size int) error {
317	return SetRandomMapAndSliceMaxSize(size)
318}
319
320// SetRandomMapAndSliceMaxSize sets the max size for maps and slices for random generation.
321func SetRandomMapAndSliceMaxSize(size int) error {
322	if size < 1 {
323		return fmt.Errorf(fakerErrors.ErrSmallerThanOne, size)
324	}
325	atomic.StoreInt32(&randomMaxSize, int32(size))
326	return nil
327}
328
329// SetRandomMapAndSliceMinSize sets the min size for maps and slices for random generation.
330func SetRandomMapAndSliceMinSize(size int) error {
331	if size < 0 {
332		return fmt.Errorf(fakerErrors.ErrSmallerThanZero, size)
333	}
334	atomic.StoreInt32(&randomMinSize, int32(size))
335	return nil
336}
337
338// SetRandomNumberBoundaries sets boundary for random number generation
339func SetRandomNumberBoundaries(start, end int) error {
340	if start > end {
341		return errors.New(fakerErrors.ErrStartValueBiggerThanEnd)
342	}
343	ptr := &interfaces.RandomIntegerBoundary{Start: start, End: end}
344	atomic.StorePointer(&iBoundary, unsafe.Pointer(ptr))
345	return nil
346}