package gofakeit import ( "errors" "math/rand/v2" "reflect" "sync" "github.com/brianvoe/gofakeit/v7/source" ) // Create global variable to deal with global function call var GlobalFaker *Faker = New(0) // Faker struct is the primary struct for using localized type Faker struct { Rand rand.Source // Lock to make thread safe Locked bool mu sync.Mutex } // New creates and returns a new Faker struct seeded with a given seed // using the PCG algorithm in lock mode for thread safety func New(seed uint64) *Faker { // If seed is 0, use a random crypto seed if seed == 0 { faker := NewFaker(source.NewCrypto(), false) return NewFaker(rand.NewPCG(faker.Uint64(), faker.Uint64()), true) } return NewFaker(rand.NewPCG(seed, seed), true) } // NewFaker takes in a rand.Source and thread lock state and returns a new Faker struct func NewFaker(src rand.Source, lock bool) *Faker { return &Faker{ Rand: src, Locked: lock, } } // Seed attempts to seed the Faker with the given seed func (f *Faker) Seed(args ...any) error { // Lock if locked if f.Locked { f.mu.Lock() defer f.mu.Unlock() } // Ensure GlobalFaker is not nil and Rand is initialized if GlobalFaker == nil || GlobalFaker.Rand == nil { return errors.New("GlobalFaker or GlobalFaker.Rand is nil") } // If args is empty or 0, seed with a random crypto seed if len(args) == 0 { faker := NewFaker(source.NewCrypto(), false) args = append(args, faker.Uint64()) } if args[0] == 0 { faker := NewFaker(source.NewCrypto(), false) args[0] = faker.Uint64() } // Retrieve the Seed method method := reflect.ValueOf(GlobalFaker.Rand).MethodByName("Seed") if !method.IsValid() { return errors.New("Seed method not found") } // Adjust args if method requires exactly 2 args but only 1 was provided if method.Type().NumIn() == 2 && len(args) == 1 { args = append(args, args[0]) // Duplicate the first value if only one is provided } // Get array of function argument types and prepare converted arguments argTypes := make([]reflect.Type, method.Type().NumIn()) convertedArgs := make([]reflect.Value, len(args)) for i := 0; i < method.Type().NumIn(); i++ { argTypes[i] = method.Type().In(i) } // Convert args to the expected type by the Seed method for i, arg := range args { if i < len(argTypes) { // Ensure arg index is within argTypes bounds argValue := reflect.ValueOf(arg) // Check if conversion is necessary if argValue.Type().ConvertibleTo(argTypes[i]) { convertedArgs[i] = argValue.Convert(argTypes[i]) } else { // If not convertible, use the argument as is (reflectively) convertedArgs[i] = argValue } } } // Dynamically call the Seed method with converted arguments method.Call(convertedArgs) return nil } // Seed attempts to seed the GlobalFaker with the given seed func Seed(args ...any) error { return GlobalFaker.Seed(args...) }