package lazy import ( "database/sql/driver" "errors" "fmt" "math/rand/v2" "reflect" "regexp" "github.com/jmoiron/sqlx" ) const ( DB_TAG = "db" LAZY_TAG = "lazy" KEY_VALUE = "key" ) type MockResults struct { Query string Columns []string Rows [][]driver.Value } type Config struct { Query string Example any Keys []any RowCount int } func GenerateRandomResults(m Config) (*MockResults, error) { if m.Example == nil { return nil, errors.New("example value cannot be nil") } if reflect.ValueOf(m.Example).Kind() != reflect.Struct { return nil, errors.New("example value must be a struct") } if m.RowCount == 0 { m.RowCount = 1 } if len(m.Keys) != 0 { if len(m.Keys) != m.RowCount { return nil, errors.New("you must provide a key for each row") } } retType := reflect.TypeOf(m.Example) maxFieldCount := retType.NumField() columns := make([]string, 0, maxFieldCount) rows := make([][]driver.Value, 0) for y := 0; y < m.RowCount; y++ { rows = append(rows, make([]driver.Value, 0)) for x := 0; x < maxFieldCount; x++ { field := retType.Field(x) dbTag := field.Tag.Get(DB_TAG) if dbTag == "" { continue } if y == 0 { columns = append(columns, dbTag) } if field.Tag.Get(LAZY_TAG) == KEY_VALUE { rows[y] = append(rows[y], m.Keys[y]) continue } nv := kindToRandom(field) if nv == nil { return nil, fmt.Errorf("could not match type: %s", retType.Name()) } rows[y] = append(rows[y], nv) } } return &MockResults{ Query: sqlx.Rebind(sqlx.AT, regexp.QuoteMeta(m.Query)), Columns: columns, Rows: rows, }, nil } func kindToRandom(field reflect.StructField) any { kind := field.Type.Kind() switch kind { case reflect.Int: return rand.Int() case reflect.Int32: return rand.Int32() case reflect.Int64: return rand.Int64() case reflect.Float32: return rand.Float32() case reflect.Float64: return rand.Float64() case reflect.String: return fmt.Sprintf("%d", rand.Int()) case reflect.Bool: return rand.Int()%2 == 0 case reflect.Slice: underlying := field.Type.Elem().Kind() switch underlying { case reflect.Int: return []int{rand.Int()} case reflect.Int32: return []int32{rand.Int32()} case reflect.Int64: return []int64{rand.Int64()} case reflect.Float32: return []float32{rand.Float32()} case reflect.Float64: return []float64{rand.Float64()} case reflect.String: return []string{fmt.Sprintf("%d", rand.Int())} case reflect.Bool: return []bool{rand.Int()%2 == 0} } } return nil }