diff --git a/ai/conversation.go b/ai/conversation.go index c2e181c7f..1930cd9a7 100644 --- a/ai/conversation.go +++ b/ai/conversation.go @@ -411,8 +411,7 @@ func (r *conversation) runMiddlewarePipeline(ctx context.Context, prompt contrac next := destination activeMiddlewares := slices.Clone(r.middlewares) - for i := len(activeMiddlewares) - 1; i >= 0; i-- { - middleware := activeMiddlewares[i] + for _, middleware := range slices.Backward(activeMiddlewares) { nextHandler := next next = contractsai.Next(func(ctx context.Context, prompt contractsai.AgentPrompt) (contractsai.AgentResponse, error) { return middleware.Handle(ctx, prompt, nextHandler) diff --git a/ai/failover.go b/ai/failover.go index 414747f7b..713e06861 100644 --- a/ai/failover.go +++ b/ai/failover.go @@ -3,7 +3,7 @@ package ai import ( "context" "regexp" - "sort" + "slices" "strings" contractsai "github.com/goravel/framework/contracts/ai" @@ -53,7 +53,7 @@ func newFailoverProvider(providers []resolvedProvider) contractsai.Provider { return providers[0].provider } - return &failoverProvider{providers: append([]resolvedProvider(nil), providers...)} + return &failoverProvider{providers: slices.Clone(providers)} } func (e *failoverError) Error() string { @@ -215,7 +215,7 @@ func NewFailoverRules(provider string, patterns map[contractsai.FailoverReason][ reasons = append(reasons, string(reason)) } } - sort.Strings(reasons) + slices.Sort(reasons) var rules []failoverRule for _, reasonValue := range reasons { diff --git a/ai/media_storage.go b/ai/media_storage.go index ab4bddeee..983fe63b5 100644 --- a/ai/media_storage.go +++ b/ai/media_storage.go @@ -3,6 +3,7 @@ package ai import ( pathpkg "path" "regexp" + "slices" "strings" contractsfilesystem "github.com/goravel/framework/contracts/filesystem" @@ -69,13 +70,7 @@ func storeContent(content []byte, name, path, disk string, nameRequired error) ( } func hasParentPathSegment(path string) bool { - for _, segment := range strings.Split(path, "/") { - if segment == ".." { - return true - } - } - - return false + return slices.Contains(strings.Split(path, "/"), "..") } func isAbsoluteStoragePath(path string) bool { diff --git a/ai/response.go b/ai/response.go index 503869942..51a9c57ac 100644 --- a/ai/response.go +++ b/ai/response.go @@ -3,6 +3,7 @@ package ai import ( "bytes" "context" + "slices" "strings" contractsai "github.com/goravel/framework/contracts/ai" @@ -251,5 +252,5 @@ func cloneTranscriptionSegments(segments []contractsai.TranscriptionSegment) []c return nil } - return append([]contractsai.TranscriptionSegment(nil), segments...) + return slices.Clone(segments) } diff --git a/cache/memory_test.go b/cache/memory_test.go index ab5e36b43..23872e06a 100644 --- a/cache/memory_test.go +++ b/cache/memory_test.go @@ -78,14 +78,12 @@ func (s *MemoryTestSuite) TestDecrementWithConcurrent() { failed atomic.Int64 ) for range 1000 { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { // Assertions belong on the test goroutine, and err is shared. if _, err := s.memory.Decrement("decrement_concurrent", 1); err != nil { failed.Add(1) } - }() + }) } wg.Wait() @@ -126,22 +124,18 @@ func (s *MemoryTestSuite) TestFlushWhileReading() { var wg sync.WaitGroup for range 50 { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { for range 500 { s.memory.Get("test-flush-while-reading") } - }() + }) } for range 10 { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { for range 200 { s.memory.Flush() } - }() + }) } wg.Wait() @@ -229,9 +223,7 @@ func (s *MemoryTestSuite) TestAddWithConcurrent() { start = make(chan struct{}) ) for range 8 { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { // Contend on the same expired item: a goroutine released after // a winner has replaced it leaves through the "not expired" // branch, which proves nothing about the swap. @@ -239,7 +231,7 @@ func (s *MemoryTestSuite) TestAddWithConcurrent() { if s.memory.Add(key, "goravel", NoExpiration) { added.Add(1) } - }() + }) } close(start) wg.Wait() @@ -383,14 +375,12 @@ func (s *MemoryTestSuite) TestIncrementWithConcurrent() { failed atomic.Int64 ) for range 1000 { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { // Assertions belong on the test goroutine, and err is shared. if _, err := s.memory.Increment("increment_concurrent", 1); err != nil { failed.Add(1) } - }() + }) } wg.Wait() diff --git a/console/application_test.go b/console/application_test.go index 58684fb48..cb0d545ef 100644 --- a/console/application_test.go +++ b/console/application_test.go @@ -44,12 +44,10 @@ func TestRun_Concurrent(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 100; i++ { - wg.Add(1) - go func() { + wg.Go(func() { _ = cliApp.Call("test1") _ = cliApp.Call("test2") - wg.Done() - }() + }) } wg.Wait() @@ -698,7 +696,7 @@ type filterTestCommand struct { signature string } -func (f *filterTestCommand) Signature() string { return f.signature } +func (f *filterTestCommand) Signature() string { return f.signature } func (f *filterTestCommand) Description() string { return f.signature } func (f *filterTestCommand) Extend() command.Extend { return command.Extend{} diff --git a/console/cli_context.go b/console/cli_context.go index 90ebac12b..c06095b60 100644 --- a/console/cli_context.go +++ b/console/cli_context.go @@ -204,7 +204,7 @@ func (r *CliContext) NewLine(times ...int) { if len(times) > 0 && times[0] > 0 { numLines = times[0] } - for i := 0; i < numLines; i++ { + for range numLines { color.Default().Println() } } diff --git a/console/cli_context_test.go b/console/cli_context_test.go index f40bc97c1..e5c977908 100644 --- a/console/cli_context_test.go +++ b/console/cli_context_test.go @@ -16,7 +16,6 @@ import ( "github.com/goravel/framework/contracts/console" "github.com/goravel/framework/contracts/console/command" "github.com/goravel/framework/support/color" - "github.com/goravel/framework/support/convert" ) func TestArgumentString(t *testing.T) { @@ -2094,10 +2093,10 @@ func TestTable(t *testing.T) { { name: "compact style using boolean flags", option: console.TableOption{ - BorderTop: convert.Pointer(false), - BorderBottom: convert.Pointer(false), - BorderLeft: convert.Pointer(false), - BorderRight: convert.Pointer(false), + BorderTop: new(false), + BorderBottom: new(false), + BorderLeft: new(false), + BorderRight: new(false), }, contains: []string{"ID", "Name", "│"}, }, diff --git a/console/cli_helper.go b/console/cli_helper.go index 4fc2e5a85..9d89b186f 100644 --- a/console/cli_helper.go +++ b/console/cli_helper.go @@ -1,10 +1,12 @@ package console import ( + "cmp" "context" "fmt" "io" "os" + "slices" "sort" "strings" "text/tabwriter" @@ -162,11 +164,8 @@ func findAlternatives(name string, collection []string) (result []string) { sortedAlternatives = append(sortedAlternatives, scoredItem{item, score}) } } - sort.Slice(sortedAlternatives, func(i, j int) bool { - if sortedAlternatives[i].score == sortedAlternatives[j].score { - return sortedAlternatives[i].name < sortedAlternatives[j].name - } - return sortedAlternatives[i].score < sortedAlternatives[j].score + slices.SortFunc(sortedAlternatives, func(a, b scoredItem) int { + return cmp.Or(cmp.Compare(a.score, b.score), strings.Compare(a.name, b.name)) }) for _, item := range sortedAlternatives { result = append(result, item.name) @@ -186,8 +185,8 @@ func getFlagDefaultText(flag cli.DocGenerationFlag) string { func getFlagName(flag cli.Flag) string { names := flag.Names() - sort.Slice(names, func(i, j int) bool { - return len(names[i]) < len(names[j]) + slices.SortFunc(names, func(a, b string) int { + return cmp.Compare(len(a), len(b)) }) prefixed := cli.FlagNamePrefixer(names, "") // If there is no short name, add some padding to align flag name. @@ -384,10 +383,17 @@ func sortVisibleFlags(cmd *cli.Command) []cli.Flag { for i := range globalFlags { globalFlagNames[getFlagName(globalFlags[i])] = struct{}{} } - sort.Slice(flags, func(i, j int) bool { - _, isGlobalI := globalFlagNames[getFlagName(flags[i])] - _, isGlobalJ := globalFlagNames[getFlagName(flags[j])] - return !isGlobalI && isGlobalJ + // Stable sort keeps the by-name order from above inside each group. + slices.SortStableFunc(flags, func(a, b cli.Flag) int { + _, isGlobalA := globalFlagNames[getFlagName(a)] + _, isGlobalB := globalFlagNames[getFlagName(b)] + switch { + case !isGlobalA && isGlobalB: + return -1 + case isGlobalA && !isGlobalB: + return 1 + } + return 0 }) return flags diff --git a/console/service_provider_test.go b/console/service_provider_test.go index 32f90f4bd..7ca260e8d 100644 --- a/console/service_provider_test.go +++ b/console/service_provider_test.go @@ -1,7 +1,7 @@ package console import ( - "sort" + "slices" "testing" "github.com/stretchr/testify/assert" @@ -50,7 +50,7 @@ func signaturesOf(commands []contractsconsole.Command) []string { for _, c := range commands { sigs = append(sigs, c.Signature()) } - sort.Strings(sigs) + slices.Sort(sigs) return sigs } @@ -107,7 +107,7 @@ func TestServiceProviderBootAllowlistApplies(t *testing.T) { artisan.EXPECT().Register(mock.MatchedBy(func(commands []contractsconsole.Command) bool { got := signaturesOf(commands) want := []string{"list", "make:command"} - sort.Strings(want) + slices.Sort(want) if len(got) != len(want) { return false } diff --git a/console/style.go b/console/style.go index a5e9a77d0..9a7fe7ef3 100644 --- a/console/style.go +++ b/console/style.go @@ -7,7 +7,6 @@ import ( "github.com/pterm/pterm" "github.com/goravel/framework/contracts/console" - "github.com/goravel/framework/support/convert" ) var ( @@ -40,13 +39,13 @@ var ( StyleFunc: DefaultTableStyleFunc, - BorderTop: convert.Pointer(true), - BorderBottom: convert.Pointer(true), - BorderLeft: convert.Pointer(true), - BorderRight: convert.Pointer(true), - BorderHeader: convert.Pointer(true), - BorderColumn: convert.Pointer(true), - BorderRow: convert.Pointer(false), + BorderTop: new(true), + BorderBottom: new(true), + BorderLeft: new(true), + BorderRight: new(true), + BorderHeader: new(true), + BorderColumn: new(true), + BorderRow: new(false), } GlobalHuhTheme = func() *huh.Theme { diff --git a/contracts/translation/translator.go b/contracts/translation/translator.go index 11bec3475..a4770eff3 100644 --- a/contracts/translation/translator.go +++ b/contracts/translation/translator.go @@ -27,6 +27,9 @@ type Option struct { Locale string } +// Bool returns a pointer to the value. +// +// Deprecated: Use new(value) instead, Bool will be removed in a future version. func Bool(value bool) *bool { return &value } diff --git a/crypt/aes_test.go b/crypt/aes_test.go index ae0678826..e42215da1 100644 --- a/crypt/aes_test.go +++ b/crypt/aes_test.go @@ -79,9 +79,7 @@ func Benchmark_EncryptString(b *testing.B) { b.Fatal(err) } - b.ResetTimer() - - for i := 0; i < b.N; i++ { + for b.Loop() { _, err := aes.EncryptString("Goravel") if err != nil { b.Error(err) @@ -101,9 +99,8 @@ func Benchmark_DecryptString(b *testing.B) { if err != nil { b.Error(err) } - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { _, err := aes.DecryptString(payload) if err != nil { b.Error(err) diff --git a/database/db/query.go b/database/db/query.go index d623e513d..43518ce87 100644 --- a/database/db/query.go +++ b/database/db/query.go @@ -6,7 +6,7 @@ import ( "fmt" "maps" "reflect" - "sort" + "slices" "strings" sq "github.com/Masterminds/squirrel" @@ -19,7 +19,6 @@ import ( "github.com/goravel/framework/errors" "github.com/goravel/framework/support/carbon" "github.com/goravel/framework/support/collect" - "github.com/goravel/framework/support/convert" "github.com/goravel/framework/support/deep" "github.com/goravel/framework/support/str" ) @@ -215,7 +214,7 @@ func (r *Query) Distinct(columns ...string) db.Query { query = r.clone() } - query.conditions.Distinct = convert.Pointer(true) + query.conditions.Distinct = new(true) return query } @@ -417,7 +416,7 @@ func (r *Query) Increment(column string, value ...uint64) error { func (r *Query) InRandomOrder() db.Query { q := r.clone() - q.conditions.InRandomOrder = convert.Pointer(true) + q.conditions.InRandomOrder = new(true) return q } @@ -516,7 +515,7 @@ func (r *Query) Limit(limit uint64) db.Query { func (r *Query) LockForUpdate() db.Query { q := r.clone() - q.conditions.LockForUpdate = convert.Pointer(true) + q.conditions.LockForUpdate = new(true) return q } @@ -728,7 +727,7 @@ func (r *Query) Select(columns ...string) db.Query { func (r *Query) SharedLock() db.Query { q := r.clone() - q.conditions.SharedLock = convert.Pointer(true) + q.conditions.SharedLock = new(true) return q } @@ -1115,11 +1114,7 @@ func (r *Query) buildInsert(data []map[string]any) (sql string, args []any, err } } - cols := make([]string, 0, len(colSet)) - for col := range colSet { - cols = append(cols, col) - } - sort.Strings(cols) + cols := slices.Sorted(maps.Keys(colSet)) builder = builder.Columns(cols...) for _, row := range data { diff --git a/database/db/row.go b/database/db/row.go index 61af9195a..e4b90ac73 100644 --- a/database/db/row.go +++ b/database/db/row.go @@ -54,7 +54,7 @@ func (r *Row) Scan(value any) error { // Mysql returns []uint8 for String type when scanning the rows. func ToStringHookFunc() mapstructure.DecodeHookFunc { return func(f reflect.Type, t reflect.Type, data any) (any, error) { - if t != reflect.TypeOf("") { + if t != reflect.TypeFor[string]() { return data, nil } @@ -69,7 +69,7 @@ func ToStringHookFunc() mapstructure.DecodeHookFunc { func ToTimeHookFunc() mapstructure.DecodeHookFunc { return func(f reflect.Type, t reflect.Type, data any) (any, error) { - if t != reflect.TypeOf(time.Time{}) { + if t != reflect.TypeFor[time.Time]() { return data, nil } @@ -88,11 +88,11 @@ func ToTimeHookFunc() mapstructure.DecodeHookFunc { func ToDeletedAtHookFunc() mapstructure.DecodeHookFunc { return func(f reflect.Type, t reflect.Type, data any) (any, error) { - if t != reflect.TypeOf(gorm.DeletedAt{}) { + if t != reflect.TypeFor[gorm.DeletedAt]() { return data, nil } - if f == reflect.TypeOf(time.Time{}) { + if f == reflect.TypeFor[time.Time]() { return gorm.DeletedAt{Time: data.(time.Time), Valid: true}, nil } @@ -109,7 +109,7 @@ func ToDeletedAtHookFunc() mapstructure.DecodeHookFunc { func ToScannerHookFunc() mapstructure.DecodeHookFunc { return func(f reflect.Type, t reflect.Type, data any) (any, error) { // Skip types that are handled by other specific hooks - if t == reflect.TypeOf(time.Time{}) || t == reflect.TypeOf(gorm.DeletedAt{}) { + if t == reflect.TypeFor[time.Time]() || t == reflect.TypeFor[gorm.DeletedAt]() { return data, nil } @@ -119,7 +119,7 @@ func ToScannerHookFunc() mapstructure.DecodeHookFunc { } // Only process database types (string, []byte, []uint8, time.Time) - if f.Kind() != reflect.String && f != reflect.TypeOf([]byte(nil)) && f != reflect.TypeOf([]uint8(nil)) && f != reflect.TypeOf(time.Time{}) { + if f.Kind() != reflect.String && f != reflect.TypeFor[[]byte]() && f != reflect.TypeFor[[]uint8]() && f != reflect.TypeFor[time.Time]() { return data, nil } diff --git a/database/db/utils.go b/database/db/utils.go index dd05d7ba9..fbbc16c29 100644 --- a/database/db/utils.go +++ b/database/db/utils.go @@ -98,7 +98,7 @@ func convertToMap(data any) (map[string]any, error) { // Handle struct result := make(map[string]any) - for i := 0; i < typ.NumField(); i++ { + for i := range typ.NumField() { field := typ.Field(i) if !field.IsExported() { continue @@ -127,8 +127,8 @@ func convertToMap(data any) (map[string]any, error) { } var fieldName string if tag != "" { - if comma := strings.Index(tag, ","); comma != -1 { - fieldName = tag[:comma] + if before, _, ok := strings.Cut(tag, ","); ok { + fieldName = before } else { fieldName = tag } diff --git a/database/db/utils_test.go b/database/db/utils_test.go index 5cdd93feb..8f20acc91 100644 --- a/database/db/utils_test.go +++ b/database/db/utils_test.go @@ -8,7 +8,6 @@ import ( "gorm.io/gorm" "github.com/goravel/framework/support/carbon" - "github.com/goravel/framework/support/convert" ) type Body struct { @@ -184,20 +183,20 @@ func TestConvertToSliceMap(t *testing.T) { return User{ ID: 1, Email: "john@example.com", - Avatar: convert.Pointer("avatar.jpg"), - Alias: convert.Pointer(42), + Avatar: new("avatar.jpg"), + Alias: new(42), Body: Body{Weight: "100kg", Head: &head, DateTime: *dateTime}, - House: &House{Address: "123 Main St", Size: convert.Pointer(100)}, - Job: Job{Title: "Engineer", Salary: convert.Pointer(50000.0)}, + House: &House{Address: "123 Main St", Size: new(100)}, + Job: Job{Title: "Engineer", Salary: new(50000.0)}, TestTimestamps: TestTimestamps{CreatedAt: dateTime, UpdatedAt: dateTime}, } }(), want: []map[string]any{ { - "id": 1, "email": "john@example.com", "avatar": convert.Pointer("avatar.jpg"), - "alias": convert.Pointer(42), "weight": "100kg", "head": &head, "date_time": *dateTime, - "house": &House{Address: "123 Main St", Size: convert.Pointer(100)}, - "job": Job{Title: "Engineer", Salary: convert.Pointer(50000.0)}, + "id": 1, "email": "john@example.com", "avatar": new("avatar.jpg"), + "alias": new(42), "weight": "100kg", "head": &head, "date_time": *dateTime, + "house": &House{Address: "123 Main St", Size: new(100)}, + "job": Job{Title: "Engineer", Salary: new(50000.0)}, "created_at": dateTime, "updated_at": dateTime, }, }, diff --git a/database/factory/factory.go b/database/factory/factory.go index 9d019a0e6..20a9bbeb4 100644 --- a/database/factory/factory.go +++ b/database/factory/factory.go @@ -54,7 +54,7 @@ func (f *FactoryImpl) Make(value any, attributes ...map[string]any) error { if f.count != nil { count = *f.count } - for i := 0; i < count; i++ { + for range count { elemValue := reflect.New(reflectValue.Type().Elem()).Interface() attributes, err := getRawAttributes(elemValue, attributes...) if err != nil { diff --git a/database/gorm/event.go b/database/gorm/event.go index 2ac141222..49c595e1d 100644 --- a/database/gorm/event.go +++ b/database/gorm/event.go @@ -127,7 +127,7 @@ func (e *Event) SetAttribute(key string, value any) { destValue = destValueCanAddr.Elem() } - for i := 0; i < destType.NumField(); i++ { + for i := range destType.NumField() { if !destType.Field(i).IsExported() { continue } @@ -301,7 +301,7 @@ func fetchColumnNames(model any) map[string]string { return res } - for i := 0; i < modelType.NumField(); i++ { + for i := range modelType.NumField() { if !modelType.Field(i).IsExported() { continue } @@ -335,7 +335,7 @@ func structToMap(data any) map[string]any { return res } - for i := 0; i < modelType.NumField(); i++ { + for i := range modelType.NumField() { fieldType := modelType.Field(i) fieldValue := modelValue.Field(i) @@ -364,8 +364,7 @@ func structToMap(data any) map[string]any { func structNameToDbColumnName(structName, tag string) string { if strings.Contains(tag, "column:") { - tags := strings.Split(tag, ";") - for _, item := range tags { + for item := range strings.SplitSeq(tag, ";") { if strings.Contains(item, "column:") { return strings.Trim(strings.ReplaceAll(item, "column:", ""), " ") } diff --git a/database/gorm/event_test.go b/database/gorm/event_test.go index 9e2ed58ae..0db51ec2e 100644 --- a/database/gorm/event_test.go +++ b/database/gorm/event_test.go @@ -9,7 +9,6 @@ import ( "gorm.io/gorm" "github.com/goravel/framework/support/carbon" - "github.com/goravel/framework/support/convert" ) type Model struct { @@ -369,7 +368,7 @@ func TestStructToMap(t *testing.T) { }, }, Name: "name", - Avatar: convert.Pointer("avatar"), + Avatar: new("avatar"), IsAdmin: true, IsManage: 2, AdminAt: testNow, diff --git a/database/gorm/query.go b/database/gorm/query.go index 705747ad2..3f87c4370 100644 --- a/database/gorm/query.go +++ b/database/gorm/query.go @@ -492,7 +492,7 @@ func (r *Query) Load(model any, relation string, args ...any) error { t := destType.Elem() v := reflect.ValueOf(model).Elem() - for i := 0; i < t.NumField(); i++ { + for i := range t.NumField() { if !t.Field(i).IsExported() { continue } @@ -512,7 +512,7 @@ func (r *Query) LoadMissing(model any, relation string, args ...any) error { t := reflect.TypeOf(model).Elem() v := reflect.ValueOf(model).Elem() - for i := 0; i < t.NumField(); i++ { + for i := range t.NumField() { if !t.Field(i).IsExported() { continue } @@ -2036,23 +2036,23 @@ func getDeletedAtColumn(model any) string { t = t.Elem() } - for i := 0; i < t.NumField(); i++ { - if !t.Field(i).IsExported() { + for field := range t.Fields() { + if !field.IsExported() { continue } - if t.Field(i).Type.Kind() == reflect.Struct { - if t.Field(i).Type == reflect.TypeOf(gormio.DeletedAt{}) { - return t.Field(i).Name + if field.Type.Kind() == reflect.Struct { + if field.Type == reflect.TypeFor[gormio.DeletedAt]() { + return field.Name } - structField := t.Field(i).Type - for j := 0; j < structField.NumField(); j++ { - if !structField.Field(j).IsExported() { + structField := field.Type + for nested := range structField.Fields() { + if !nested.IsExported() { continue } - if structField.Field(j).Type == reflect.TypeOf(gormio.DeletedAt{}) { - return structField.Field(j).Name + if nested.Type == reflect.TypeFor[gormio.DeletedAt]() { + return nested.Name } } } diff --git a/database/gorm/query_test.go b/database/gorm/query_test.go index cf4b8221f..82e3e39fa 100644 --- a/database/gorm/query_test.go +++ b/database/gorm/query_test.go @@ -12,7 +12,6 @@ import ( contractsdriver "github.com/goravel/framework/contracts/database/driver" contractsorm "github.com/goravel/framework/contracts/database/orm" "github.com/goravel/framework/errors" - "github.com/goravel/framework/support/convert" ) func TestAddGlobalScopes(t *testing.T) { @@ -682,25 +681,25 @@ func TestModelToStruct(t *testing.T) { }, { name: "pointer to string", - input: convert.Pointer("test"), + input: new("test"), expectError: true, expectedErr: errors.OrmQueryInvalidModel.Args(""), }, { name: "pointer to int", - input: convert.Pointer(123), + input: new(123), expectError: true, expectedErr: errors.OrmQueryInvalidModel.Args(""), }, { name: "pointer to float", - input: convert.Pointer(123.45), + input: new(123.45), expectError: true, expectedErr: errors.OrmQueryInvalidModel.Args(""), }, { name: "pointer to bool", - input: convert.Pointer(true), + input: new(true), expectError: true, expectedErr: errors.OrmQueryInvalidModel.Args(""), }, @@ -896,7 +895,7 @@ func TestModelToStruct(t *testing.T) { assert.NotNil(t, result) // The result should be a different instance assert.NotSame(t, &TestModel{ID: 1, Name: "test"}, result) - assert.Equal(t, reflect.TypeOf(&TestModel{}), reflect.TypeOf(result)) + assert.Equal(t, reflect.TypeFor[*TestModel](), reflect.TypeOf(result)) }, }, } diff --git a/database/gorm/utils.go b/database/gorm/utils.go index fb3cf9448..316f94d90 100644 --- a/database/gorm/utils.go +++ b/database/gorm/utils.go @@ -11,8 +11,8 @@ func copyStruct(dest any) reflect.Value { } destFields := make([]reflect.StructField, 0) - for i := 0; i < t.NumField(); i++ { - destFields = append(destFields, t.Field(i)) + for field := range t.Fields() { + destFields = append(destFields, field) } copyDestStruct := reflect.StructOf(destFields) diff --git a/database/migration/migrator.go b/database/migration/migrator.go index 3b6da7f9a..28a5d3115 100644 --- a/database/migration/migrator.go +++ b/database/migration/migrator.go @@ -254,7 +254,7 @@ func (r *Migrator) prepareDatabase() error { func (r *Migrator) printTitle(maxNameLength int) { color.Default().Print(fmt.Sprintf("%-*s", maxNameLength, "Migration name")) color.Default().Println(" | Batch / Status") - for i := 0; i < maxNameLength+17; i++ { + for range maxNameLength + 17 { color.Default().Print("-") } color.Default().Println() diff --git a/database/migration/model_mapper.go b/database/migration/model_mapper.go index 6f57054b8..45fd143eb 100644 --- a/database/migration/model_mapper.go +++ b/database/migration/model_mapper.go @@ -3,7 +3,7 @@ package migration import ( "fmt" "reflect" - "sort" + "slices" "strconv" "strings" "sync" @@ -271,7 +271,9 @@ func renderIndexes(sch *schema.Schema, fields []*schema.Field) []string { } indexes := sch.ParseIndexes() - sort.Slice(indexes, func(i, j int) bool { return indexes[i].Name < indexes[j].Name }) + slices.SortFunc(indexes, func(a, b *schema.Index) int { + return strings.Compare(a.Name, b.Name) + }) for _, idx := range indexes { if len(idx.Fields) == 0 { diff --git a/database/schema/blueprint.go b/database/schema/blueprint.go index 28dbae635..56f7f61fc 100644 --- a/database/schema/blueprint.go +++ b/database/schema/blueprint.go @@ -7,7 +7,6 @@ import ( "github.com/goravel/framework/contracts/database/driver" "github.com/goravel/framework/contracts/database/orm" "github.com/goravel/framework/contracts/database/schema" - "github.com/goravel/framework/support/convert" ) const ( @@ -244,7 +243,7 @@ func (r *Blueprint) Enum(column string, allowed []any) driver.ColumnDefinition { func (r *Blueprint) Float(column string, precision ...int) driver.ColumnDefinition { columnImpl := r.createAndAddColumn("float", column) - columnImpl.precision = convert.Pointer(53) + columnImpl.precision = new(53) if len(precision) > 0 { columnImpl.precision = &precision[0] @@ -696,7 +695,7 @@ func (r *Blueprint) addImpliedCommands(grammar driver.Grammar) { func (r *Blueprint) createAndAddColumn(ttype, name string) *ColumnDefinition { columnImpl := &ColumnDefinition{ name: &name, - ttype: convert.Pointer(ttype), + ttype: new(ttype), } r.columns = append(r.columns, columnImpl) diff --git a/database/schema/blueprint_test.go b/database/schema/blueprint_test.go index ae6f6c295..28e448fcb 100644 --- a/database/schema/blueprint_test.go +++ b/database/schema/blueprint_test.go @@ -11,7 +11,6 @@ import ( mocksdriver "github.com/goravel/framework/mocks/database/driver" mocksorm "github.com/goravel/framework/mocks/database/orm" mocksschema "github.com/goravel/framework/mocks/database/schema" - "github.com/goravel/framework/support/convert" ) type BlueprintTestSuite struct { @@ -34,7 +33,7 @@ func (s *BlueprintTestSuite) TestAddAttributeCommands() { var ( mockGrammar *mocksdriver.Grammar columnDefinition = &ColumnDefinition{ - comment: convert.Pointer("comment"), + comment: new("comment"), } ) @@ -88,10 +87,10 @@ func (s *BlueprintTestSuite) TestBigIncrements() { name := "name" s.blueprint.BigIncrements(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ - autoIncrement: convert.Pointer(true), + autoIncrement: new(true), name: &name, - unsigned: convert.Pointer(true), - ttype: convert.Pointer("bigInteger"), + unsigned: new(true), + ttype: new("bigInteger"), }) } @@ -100,15 +99,15 @@ func (s *BlueprintTestSuite) TestBigInteger() { s.blueprint.BigInteger(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("bigInteger"), + ttype: new("bigInteger"), }) s.blueprint.BigInteger(name).AutoIncrement().Unsigned() s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ - autoIncrement: convert.Pointer(true), + autoIncrement: new(true), name: &name, - unsigned: convert.Pointer(true), - ttype: convert.Pointer("bigInteger"), + unsigned: new(true), + ttype: new("bigInteger"), }) } @@ -117,7 +116,7 @@ func (s *BlueprintTestSuite) TestBoolean() { s.blueprint.Boolean(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("boolean"), + ttype: new("boolean"), }) } @@ -239,7 +238,7 @@ func (s *BlueprintTestSuite) TestDecimal() { s.blueprint.Decimal(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("decimal"), + ttype: new("decimal"), }) } @@ -248,7 +247,7 @@ func (s *BlueprintTestSuite) TestDouble() { s.blueprint.Double(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("double"), + ttype: new("double"), }) } @@ -257,15 +256,15 @@ func (s *BlueprintTestSuite) TestFloat() { s.blueprint.Float(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - precision: convert.Pointer(53), - ttype: convert.Pointer("float"), + precision: new(53), + ttype: new("float"), }) s.blueprint.Float(name, 10) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - precision: convert.Pointer(10), - ttype: convert.Pointer("float"), + precision: new(10), + ttype: new("float"), }) } @@ -274,8 +273,8 @@ func (s *BlueprintTestSuite) TestForeignID() { s.blueprint.ForeignID(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("bigInteger"), - unsigned: convert.Pointer(true), + ttype: new("bigInteger"), + unsigned: new(true), }) } @@ -284,8 +283,8 @@ func (s *BlueprintTestSuite) TestForeignIDConstrained() { s.blueprint.ForeignID(name).Constrained("users", "id", "fk_user_id") s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("bigInteger"), - unsigned: convert.Pointer(true), + ttype: new("bigInteger"), + unsigned: new(true), }) var fkCmd *driver.Command @@ -306,8 +305,8 @@ func (s *BlueprintTestSuite) TestForeignIDReferences() { s.blueprint.ForeignID(name).References("id", "fk_user_id") s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("bigInteger"), - unsigned: convert.Pointer(true), + ttype: new("bigInteger"), + unsigned: new(true), }) var fkCmd *driver.Command @@ -328,8 +327,8 @@ func (s *BlueprintTestSuite) TestForeignUlid() { s.blueprint.ForeignUlid(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("char"), - length: convert.Pointer(26), + ttype: new("char"), + length: new(26), }) } @@ -338,8 +337,8 @@ func (s *BlueprintTestSuite) TestForeignUlidConstrained() { s.blueprint.ForeignUlid(name).Constrained("users", "id", "fk_user_id") s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("char"), - length: convert.Pointer(26), + ttype: new("char"), + length: new(26), }) var fkCmd *driver.Command @@ -360,8 +359,8 @@ func (s *BlueprintTestSuite) TestForeignUlidReferences() { s.blueprint.ForeignUlid(name).References("id", "fk_user_id") s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("char"), - length: convert.Pointer(26), + ttype: new("char"), + length: new(26), }) var fkCmd *driver.Command @@ -382,7 +381,7 @@ func (s *BlueprintTestSuite) TestForeignUuid() { s.blueprint.ForeignUuid(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("uuid"), + ttype: new("uuid"), }) } @@ -391,7 +390,7 @@ func (s *BlueprintTestSuite) TestForeignUuidConstrained() { s.blueprint.ForeignUuid(name).Constrained("users", "id", "fk_user_id") s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("uuid"), + ttype: new("uuid"), }) var fkCmd *driver.Command @@ -412,7 +411,7 @@ func (s *BlueprintTestSuite) TestForeignUuidReferences() { s.blueprint.ForeignUuid(name).References("id", "fk_user_id") s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("uuid"), + ttype: new("uuid"), }) var fkCmd *driver.Command @@ -450,10 +449,10 @@ func (s *BlueprintTestSuite) TestIntegerIncrements() { name := "name" s.blueprint.IntegerIncrements(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ - autoIncrement: convert.Pointer(true), + autoIncrement: new(true), name: &name, - unsigned: convert.Pointer(true), - ttype: convert.Pointer("integer"), + unsigned: new(true), + ttype: new("integer"), }) } @@ -490,18 +489,18 @@ func (s *BlueprintTestSuite) TestIsCreate() { func (s *BlueprintTestSuite) TestID() { s.blueprint.ID() s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ - autoIncrement: convert.Pointer(true), - name: convert.Pointer("id"), - unsigned: convert.Pointer(true), - ttype: convert.Pointer("bigInteger"), + autoIncrement: new(true), + name: new("id"), + unsigned: new(true), + ttype: new("bigInteger"), }) s.blueprint.ID("name") s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ - autoIncrement: convert.Pointer(true), - name: convert.Pointer("name"), - unsigned: convert.Pointer(true), - ttype: convert.Pointer("bigInteger"), + autoIncrement: new(true), + name: new("name"), + unsigned: new(true), + ttype: new("bigInteger"), }) } @@ -510,7 +509,7 @@ func (s *BlueprintTestSuite) TestInteger() { s.blueprint.Integer(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("integer"), + ttype: new("integer"), }) } @@ -518,10 +517,10 @@ func (s *BlueprintTestSuite) TestMediumIncrements() { name := "name" s.blueprint.MediumIncrements(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ - autoIncrement: convert.Pointer(true), + autoIncrement: new(true), name: &name, - unsigned: convert.Pointer(true), - ttype: convert.Pointer("mediumInteger"), + unsigned: new(true), + ttype: new("mediumInteger"), }) } @@ -530,7 +529,7 @@ func (s *BlueprintTestSuite) TestMediumInteger() { s.blueprint.MediumInteger(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("mediumInteger"), + ttype: new("mediumInteger"), }) } @@ -538,10 +537,10 @@ func (s *BlueprintTestSuite) TestSmallIncrements() { name := "name" s.blueprint.SmallIncrements(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ - autoIncrement: convert.Pointer(true), + autoIncrement: new(true), name: &name, - unsigned: convert.Pointer(true), - ttype: convert.Pointer("smallInteger"), + unsigned: new(true), + ttype: new("smallInteger"), }) } @@ -550,7 +549,7 @@ func (s *BlueprintTestSuite) TestSmallInteger() { s.blueprint.SmallInteger(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("smallInteger"), + ttype: new("smallInteger"), }) } @@ -578,10 +577,10 @@ func (s *BlueprintTestSuite) TestTinyIncrements() { name := "name" s.blueprint.TinyIncrements(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ - autoIncrement: convert.Pointer(true), + autoIncrement: new(true), name: &name, - unsigned: convert.Pointer(true), - ttype: convert.Pointer("tinyInteger"), + unsigned: new(true), + ttype: new("tinyInteger"), }) } @@ -590,7 +589,7 @@ func (s *BlueprintTestSuite) TestTinyInteger() { s.blueprint.TinyInteger(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("tinyInteger"), + ttype: new("tinyInteger"), }) } @@ -847,8 +846,8 @@ func (s *BlueprintTestSuite) TestUnsignedBigInteger() { s.blueprint.UnsignedBigInteger(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("bigInteger"), - unsigned: convert.Pointer(true), + ttype: new("bigInteger"), + unsigned: new(true), }) } @@ -857,8 +856,8 @@ func (s *BlueprintTestSuite) TestUnsignedInteger() { s.blueprint.UnsignedInteger(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("integer"), - unsigned: convert.Pointer(true), + ttype: new("integer"), + unsigned: new(true), }) } @@ -867,8 +866,8 @@ func (s *BlueprintTestSuite) TestUnsignedMediumInteger() { s.blueprint.UnsignedMediumInteger(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("mediumInteger"), - unsigned: convert.Pointer(true), + ttype: new("mediumInteger"), + unsigned: new(true), }) } @@ -877,8 +876,8 @@ func (s *BlueprintTestSuite) TestUnsignedSmallInteger() { s.blueprint.UnsignedSmallInteger(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("smallInteger"), - unsigned: convert.Pointer(true), + ttype: new("smallInteger"), + unsigned: new(true), }) } @@ -887,8 +886,8 @@ func (s *BlueprintTestSuite) TestUnsignedTinyInteger() { s.blueprint.UnsignedTinyInteger(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("tinyInteger"), - unsigned: convert.Pointer(true), + ttype: new("tinyInteger"), + unsigned: new(true), }) } @@ -897,7 +896,7 @@ func (s *BlueprintTestSuite) TestUuid() { s.blueprint.Uuid(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("uuid"), + ttype: new("uuid"), }) } @@ -910,13 +909,13 @@ func (s *BlueprintTestSuite) TestMorphs() { s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), + ttype: new("string"), + length: new(255), }) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("bigInteger"), - unsigned: convert.Pointer(true), + ttype: new("bigInteger"), + unsigned: new(true), }) commands := s.blueprint.GetCommands() @@ -941,13 +940,13 @@ func (s *BlueprintTestSuite) TestNumericMorphs() { s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), + ttype: new("string"), + length: new(255), }) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("bigInteger"), - unsigned: convert.Pointer(true), + ttype: new("bigInteger"), + unsigned: new(true), }) commands := s.blueprint.GetCommands() @@ -972,12 +971,12 @@ func (s *BlueprintTestSuite) TestUuidMorphs() { s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), + ttype: new("string"), + length: new(255), }) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("uuid"), + ttype: new("uuid"), }) commands := s.blueprint.GetCommands() @@ -1002,12 +1001,12 @@ func (s *BlueprintTestSuite) TestUuidMorphsWithCustomIndex() { s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), + ttype: new("string"), + length: new(255), }) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("uuid"), + ttype: new("uuid"), }) commands := s.blueprint.GetCommands() @@ -1032,15 +1031,15 @@ func (s *BlueprintTestSuite) TestNullableMorphs() { s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), - nullable: convert.Pointer(true), + ttype: new("string"), + length: new(255), + nullable: new(true), }) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("bigInteger"), - unsigned: convert.Pointer(true), - nullable: convert.Pointer(true), + ttype: new("bigInteger"), + unsigned: new(true), + nullable: new(true), }) commands := s.blueprint.GetCommands() @@ -1065,13 +1064,13 @@ func (s *BlueprintTestSuite) TestMorphsWithDefaultKeyType() { s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), + ttype: new("string"), + length: new(255), }) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("bigInteger"), - unsigned: convert.Pointer(true), + ttype: new("bigInteger"), + unsigned: new(true), }) } @@ -1096,12 +1095,12 @@ func (s *BlueprintTestSuite) TestMorphsWithUuidKeyType() { s.Contains(blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), + ttype: new("string"), + length: new(255), }) s.Contains(blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("uuid"), + ttype: new("uuid"), }) } @@ -1125,14 +1124,14 @@ func (s *BlueprintTestSuite) TestNullableMorphsWithUuidKeyType() { s.Contains(blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), - nullable: convert.Pointer(true), + ttype: new("string"), + length: new(255), + nullable: new(true), }) s.Contains(blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("uuid"), - nullable: convert.Pointer(true), + ttype: new("uuid"), + nullable: new(true), }) } @@ -1141,8 +1140,8 @@ func (s *BlueprintTestSuite) TestUlid() { s.blueprint.Ulid(name) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &name, - ttype: convert.Pointer("char"), - length: convert.Pointer(26), + ttype: new("char"), + length: new(26), }) } @@ -1155,13 +1154,13 @@ func (s *BlueprintTestSuite) TestUlidMorphs() { s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), + ttype: new("string"), + length: new(255), }) s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("char"), - length: convert.Pointer(26), + ttype: new("char"), + length: new(26), }) commands := s.blueprint.GetCommands() @@ -1197,13 +1196,13 @@ func (s *BlueprintTestSuite) TestMorphsWithUlidKeyType() { s.Contains(blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), + ttype: new("string"), + length: new(255), }) s.Contains(blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("char"), - length: convert.Pointer(26), + ttype: new("char"), + length: new(26), }) } @@ -1227,15 +1226,15 @@ func (s *BlueprintTestSuite) TestNullableMorphsWithUlidKeyType() { s.Contains(blueprint.GetAddedColumns(), &ColumnDefinition{ name: &typeColumn, - ttype: convert.Pointer("string"), - length: convert.Pointer(255), - nullable: convert.Pointer(true), + ttype: new("string"), + length: new(255), + nullable: new(true), }) s.Contains(blueprint.GetAddedColumns(), &ColumnDefinition{ name: &idColumn, - ttype: convert.Pointer("char"), - length: convert.Pointer(26), - nullable: convert.Pointer(true), + ttype: new("char"), + length: new(26), + nullable: new(true), }) } @@ -1247,6 +1246,6 @@ func (s *BlueprintTestSuite) TestChange() { length: &customLength, name: &column, change: true, - ttype: convert.Pointer("string"), + ttype: new("string"), }) } diff --git a/database/schema/column.go b/database/schema/column.go index 0197a29ed..3d621f304 100644 --- a/database/schema/column.go +++ b/database/schema/column.go @@ -2,7 +2,6 @@ package schema import ( "github.com/goravel/framework/contracts/database/driver" - "github.com/goravel/framework/support/convert" ) type ColumnDefinition struct { @@ -31,7 +30,7 @@ type ColumnDefinition struct { func NewColumnDefinition(name string, ttype string) driver.ColumnDefinition { return &ColumnDefinition{ name: &name, - ttype: convert.Pointer(ttype), + ttype: new(ttype), } } @@ -48,7 +47,7 @@ func (r *ColumnDefinition) Always() driver.ColumnDefinition { } func (r *ColumnDefinition) AutoIncrement() driver.ColumnDefinition { - r.autoIncrement = convert.Pointer(true) + r.autoIncrement = new(true) return r } @@ -225,7 +224,7 @@ func (r *ColumnDefinition) IsSetGeneratedAs() bool { } func (r *ColumnDefinition) Nullable() driver.ColumnDefinition { - r.nullable = convert.Pointer(true) + r.nullable = new(true) return r } @@ -237,31 +236,31 @@ func (r *ColumnDefinition) OnUpdate(value any) driver.ColumnDefinition { } func (r *ColumnDefinition) Places(places int) driver.ColumnDefinition { - r.places = convert.Pointer(places) + r.places = new(places) return r } func (r *ColumnDefinition) Total(total int) driver.ColumnDefinition { - r.total = convert.Pointer(total) + r.total = new(total) return r } func (r *ColumnDefinition) Unsigned() driver.ColumnDefinition { - r.unsigned = convert.Pointer(true) + r.unsigned = new(true) return r } func (r *ColumnDefinition) UseCurrent() driver.ColumnDefinition { - r.useCurrent = convert.Pointer(true) + r.useCurrent = new(true) return r } func (r *ColumnDefinition) UseCurrentOnUpdate() driver.ColumnDefinition { - r.useCurrentOnUpdate = convert.Pointer(true) + r.useCurrentOnUpdate = new(true) return r } diff --git a/database/schema/column_test.go b/database/schema/column_test.go index 9b2cfdd3b..12cdbc44d 100644 --- a/database/schema/column_test.go +++ b/database/schema/column_test.go @@ -4,8 +4,6 @@ import ( "testing" "github.com/stretchr/testify/suite" - - "github.com/goravel/framework/support/convert" ) type ColumnDefinitionTestSuite struct { @@ -38,28 +36,28 @@ func (s *ColumnDefinitionTestSuite) GetDefault() { func (s *ColumnDefinitionTestSuite) GetName() { s.Empty(s.columnDefinition.GetName()) - s.columnDefinition.name = convert.Pointer("name") + s.columnDefinition.name = new("name") s.Equal("name", s.columnDefinition.GetName()) } func (s *ColumnDefinitionTestSuite) GetLength() { s.Equal(0, s.columnDefinition.GetLength()) - s.columnDefinition.length = convert.Pointer(255) + s.columnDefinition.length = new(255) s.Equal(255, s.columnDefinition.GetLength()) } func (s *ColumnDefinitionTestSuite) GetNullable() { s.False(s.columnDefinition.GetNullable()) - s.columnDefinition.nullable = convert.Pointer(true) + s.columnDefinition.nullable = new(true) s.True(s.columnDefinition.GetNullable()) } func (s *ColumnDefinitionTestSuite) GetType() { s.Empty(s.columnDefinition.GetType()) - s.columnDefinition.ttype = convert.Pointer("string") + s.columnDefinition.ttype = new("string") s.Equal("string", s.columnDefinition.GetType()) } diff --git a/database/schema/index.go b/database/schema/index.go index aec264fde..f71d7d864 100644 --- a/database/schema/index.go +++ b/database/schema/index.go @@ -5,7 +5,6 @@ import ( "github.com/goravel/framework/contracts/database/driver" "github.com/goravel/framework/contracts/database/schema" - "github.com/goravel/framework/support/convert" "github.com/goravel/framework/support/pluralizer" ) @@ -96,13 +95,13 @@ func (r *IndexDefinition) Algorithm(algorithm string) schema.IndexDefinition { } func (r *IndexDefinition) Deferrable() schema.IndexDefinition { - r.command.Deferrable = convert.Pointer(true) + r.command.Deferrable = new(true) return r } func (r *IndexDefinition) InitiallyImmediate() schema.IndexDefinition { - r.command.InitiallyImmediate = convert.Pointer(true) + r.command.InitiallyImmediate = new(true) return r } @@ -131,8 +130,7 @@ func (r *ForeignIDColumnDefinition) Constrained(table, column, indexName string) if table == "" { name := r.GetName() - if strings.HasSuffix(name, "_"+column) { - base := strings.TrimSuffix(name, "_"+column) + if base, ok := strings.CutSuffix(name, "_"+column); ok { table = pluralizer.Plural(base) } } diff --git a/database/schema/schema.go b/database/schema/schema.go index 98c524c0c..cd6cfc7ef 100644 --- a/database/schema/schema.go +++ b/database/schema/schema.go @@ -2,6 +2,7 @@ package schema import ( "fmt" + "maps" "reflect" "slices" "strings" @@ -479,9 +480,7 @@ func (r *Schema) newConnection(name string) (*Schema, error) { schema.goTypes = slices.Clone(r.goTypes) schema.models = slices.Clone(r.models) schema.modelsByFullName = make(map[string]any, len(r.modelsByFullName)) - for name, model := range r.modelsByFullName { - schema.modelsByFullName[name] = model - } + maps.Copy(schema.modelsByFullName, r.modelsByFullName) return schema, nil } diff --git a/event/application.go b/event/application.go index 7eee55f32..5755984df 100644 --- a/event/application.go +++ b/event/application.go @@ -1,6 +1,7 @@ package event import ( + "maps" "slices" "sync" @@ -108,9 +109,7 @@ func (app *Application) Register(events map[event.Event][]event.Listener) { app.events = map[event.Event][]event.Listener{} } - for e, listeners := range cloned { - app.events[e] = listeners - } + maps.Copy(app.events, cloned) for _, reg := range regs { for _, listener := range reg.listeners { diff --git a/event/application_listen.go b/event/application_listen.go index e10c9974c..1d91ab3cd 100644 --- a/event/application_listen.go +++ b/event/application_listen.go @@ -10,7 +10,7 @@ import ( "github.com/goravel/framework/errors" ) -var errorType = reflect.TypeOf((*error)(nil)).Elem() +var errorType = reflect.TypeFor[error]() // listener is the internal representation every accepted listener form is // normalized to when it is registered, so that dispatching stays free of type @@ -310,7 +310,7 @@ func newClosureListener(fn reflect.Value) *listener { return errors.EventListenerTypeMismatch.Args(eventName, param.String(), evt) } - if err, ok := fn.Call([]reflect.Value{value})[0].Interface().(error); ok { + if err, ok := reflect.TypeAssert[error](fn.Call([]reflect.Value{value})[0]); ok { return err } diff --git a/foundation/application_test.go b/foundation/application_test.go index 80f0338e6..e5be0539d 100644 --- a/foundation/application_test.go +++ b/foundation/application_test.go @@ -1395,27 +1395,23 @@ func TestRunnerWithInfoRaceFreedom(t *testing.T) { // Simulate multiple goroutines accessing running field for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { for j := 0; j < 100; j++ { runner.running.Store(true) time.Sleep(1 * time.Millisecond) _ = runner.running.Load() runner.running.Store(false) } - }() + }) } // Simulate multiple goroutines calling doneOnce for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { runner.doneOnce.Do(func() { // This should only be called once }) - }() + }) } wg.Wait() @@ -1436,15 +1432,13 @@ func TestDoneOnceGuarantee(t *testing.T) { // Simulate multiple goroutines trying to call Done for i := 0; i < 100; i++ { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { runner.doneOnce.Do(func() { mu.Lock() counter++ mu.Unlock() }) - }() + }) } wg.Wait() diff --git a/foundation/console/about_command.go b/foundation/console/about_command.go index 138b2c645..ca3b626c7 100644 --- a/foundation/console/about_command.go +++ b/foundation/console/about_command.go @@ -1,9 +1,10 @@ package console import ( + "cmp" "fmt" "runtime" - "sort" + "slices" "strings" "github.com/goravel/framework/contracts/console" @@ -130,8 +131,8 @@ func (info *information) Range(section string, ranger func(s string, items []fou } } if len(sections) > 1 { - sort.Slice(sections, func(i, j int) bool { - return info.section[sections[i]] < info.section[sections[j]] + slices.SortFunc(sections, func(a, b string) int { + return cmp.Compare(info.section[a], info.section[b]) }) } for i := range sections { diff --git a/foundation/provider_repository.go b/foundation/provider_repository.go index ce50b265a..b2f2d9404 100644 --- a/foundation/provider_repository.go +++ b/foundation/provider_repository.go @@ -2,7 +2,8 @@ package foundation import ( "fmt" - "sort" + "maps" + "slices" "strings" "github.com/goravel/framework/contracts/binding" @@ -336,11 +337,7 @@ func (r *ProviderRepository) detectCycle(graph map[string][]string, bindingToPro } // Sort nodes to make cycle detection deterministic. - var nodes []string - for node := range graph { - nodes = append(nodes, node) - } - sort.Strings(nodes) + nodes := slices.Sorted(maps.Keys(graph)) for _, node := range nodes { if !visited[node] { diff --git a/grpc/application.go b/grpc/application.go index 5069864e0..032f429d9 100644 --- a/grpc/application.go +++ b/grpc/application.go @@ -3,6 +3,7 @@ package grpc import ( "context" "fmt" + "maps" "net" "strings" "sync" @@ -238,9 +239,7 @@ func (r *Application) UnaryClientInterceptorGroups(groups map[string][]grpc.Unar } func (r *Application) ClientCredentials(groups map[string]credentials.TransportCredentials) { - for key, creds := range groups { - r.clientCredentialsGroups[key] = creds - } + maps.Copy(r.clientCredentialsGroups, groups) } func (r *Application) ClientStatsHandlerGroups(groups map[string][]stats.Handler) { diff --git a/hash/application_test.go b/hash/application_test.go index c8ee09f2b..f90772fc2 100644 --- a/hash/application_test.go +++ b/hash/application_test.go @@ -3,11 +3,11 @@ package hash import ( "testing" - "golang.org/x/crypto/bcrypt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "golang.org/x/crypto/bcrypt" "github.com/goravel/framework/contracts/binding" "github.com/goravel/framework/contracts/foundation" @@ -210,11 +210,9 @@ func BenchmarkMakeHash(b *testing.B) { s := new(ApplicationTestSuite) s.SetT(&testing.T{}) s.SetupTest() - b.StartTimer() - b.ResetTimer() for name, hasher := range s.hashers { b.Run(name, func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { _, err := hasher.Make("password") if err != nil { b.Fatal(err) @@ -222,49 +220,42 @@ func BenchmarkMakeHash(b *testing.B) { } }) } - b.StopTimer() } func BenchmarkCheckHash(b *testing.B) { s := new(ApplicationTestSuite) s.SetT(&testing.T{}) s.SetupTest() - b.StartTimer() - b.ResetTimer() for name, hasher := range s.hashers { b.Run(name, func(b *testing.B) { value, err := hasher.Make("password") if err != nil { b.Fatal(err) } - for i := 0; i < b.N; i++ { + for b.Loop() { if !hasher.Check("password", value) { b.Fatal("hash check failed") } } }) } - b.StopTimer() } func BenchmarkNeedsRehash(b *testing.B) { s := new(ApplicationTestSuite) s.SetT(&testing.T{}) s.SetupTest() - b.StartTimer() - b.ResetTimer() for name, hasher := range s.hashers { b.Run(name, func(b *testing.B) { value, err := hasher.Make("password") if err != nil { b.Fatal(err) } - for i := 0; i < b.N; i++ { + for b.Loop() { hasher.NeedsRehash(value) } }) } - b.StopTimer() } func getArgon2idHasher(mockConfig *mocksconfig.Config) *Argon2id { diff --git a/http/client/fake_sequence.go b/http/client/fake_sequence.go index a140e6af7..5a0377e2a 100644 --- a/http/client/fake_sequence.go +++ b/http/client/fake_sequence.go @@ -149,7 +149,7 @@ func (r *FakeSequence) pushSnapshot(snapshot responseSnapshot, count ...int) cli times = count[0] } - for i := 0; i < times; i++ { + for range times { r.snapshots = append(r.snapshots, snapshot) } diff --git a/http/client/fake_state.go b/http/client/fake_state.go index 0bcd500ee..ab4a8e177 100644 --- a/http/client/fake_state.go +++ b/http/client/fake_state.go @@ -1,9 +1,10 @@ package client import ( + "cmp" "net/http" "regexp" - "sort" + "slices" "strings" "sync" @@ -29,21 +30,21 @@ func NewFakeState(json foundation.Json, mocks map[string]any) *FakeState { // 1. Exact matches (no wildcards) take precedence over wildcards. // 2. Longer patterns take precedence over shorter ones. // 3. Alphabetical order determines priority for identical specificity. - sort.Slice(rules, func(i, j int) bool { - pI, pJ := rules[i].pattern, rules[j].pattern - - noWildI := !strings.Contains(pI, "*") - noWildJ := !strings.Contains(pJ, "*") - - if noWildI != noWildJ { - return noWildI - } - - if len(pI) != len(pJ) { - return len(pI) > len(pJ) + slices.SortFunc(rules, func(a, b *FakeRule) int { + wildA := strings.Contains(a.pattern, "*") + wildB := strings.Contains(b.pattern, "*") + + if wildA != wildB { + if wildA { + return 1 + } + return -1 } - return pI < pJ + return cmp.Or( + cmp.Compare(len(b.pattern), len(a.pattern)), + strings.Compare(a.pattern, b.pattern), + ) }) return &FakeState{ @@ -100,12 +101,7 @@ func (r *FakeState) AllowStrayRequests(patterns []string) { func (r *FakeState) AssertSent(f func(client.Request) bool) bool { r.mu.RLock() defer r.mu.RUnlock() - for _, recorded := range r.recorded { - if f(recorded) { - return true - } - } - return false + return slices.ContainsFunc(r.recorded, f) } func (r *FakeState) AssertSentCount(count int) bool { diff --git a/http/client/request_test.go b/http/client/request_test.go index f466635ab..6b065ae5d 100644 --- a/http/client/request_test.go +++ b/http/client/request_test.go @@ -354,29 +354,23 @@ func (s *RequestTestSuite) TestConcurrentRequests() { defer cancel() var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { resp, err := reqTimeout.WithContext(ctx).Get(server.URL) s.Error(err, "Should timeout") s.Nil(resp) - }() + }) - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { resp, err := reqWithParams.WithUrlParameter("hello", "world").Get(server.URL) s.NoError(err) s.NotNil(resp) - }() + }) - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { resp, err := req.Get(server.URL) s.NoError(err) s.NotNil(resp) - }() + }) wg.Wait() } diff --git a/log/logger/utils.go b/log/logger/utils.go index 1113ffb7b..162874ba2 100644 --- a/log/logger/utils.go +++ b/log/logger/utils.go @@ -127,7 +127,7 @@ func getContextValues(ctx any, out map[any]any) { t := v.Type() var key, val any - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { f := v.Field(i) if !f.CanAddr() { continue diff --git a/log/rotation/rotatelogs.go b/log/rotation/rotatelogs.go index c1a582db1..ebed4a8c6 100644 --- a/log/rotation/rotatelogs.go +++ b/log/rotation/rotatelogs.go @@ -5,7 +5,7 @@ import ( "io" "os" "path/filepath" - "sort" + "slices" "strings" "sync" "time" @@ -30,11 +30,11 @@ type RotateLogs struct { rotationTime time.Duration rotationCount uint - mutex sync.Mutex - currentFile *os.File - currentPath string - lastRotation time.Time - cleanupWg sync.WaitGroup // For testing - wait for cleanup to complete + mutex sync.Mutex + currentFile *os.File + currentPath string + lastRotation time.Time + cleanupWg sync.WaitGroup // For testing - wait for cleanup to complete } // Option is a functional option for RotateLogs @@ -97,10 +97,10 @@ func (rl *RotateLogs) Write(p []byte) (n int, err error) { // rotate performs the rotation if needed func (rl *RotateLogs) rotate() error { now := rl.clock.Now() - + // Calculate the base time for the current rotation period baseTime := now.Truncate(rl.rotationTime) - + // Check if we need to rotate if rl.currentFile != nil && rl.lastRotation.Equal(baseTime) { return nil // No rotation needed @@ -108,7 +108,7 @@ func (rl *RotateLogs) rotate() error { // Generate the new filename newPath := rl.pattern.Format(baseTime) - + // Close the current file if it exists if rl.currentFile != nil { if err := rl.currentFile.Close(); err != nil { @@ -135,11 +135,9 @@ func (rl *RotateLogs) rotate() error { // Clean up old files if rotation count is set if rl.rotationCount > 0 { - rl.cleanupWg.Add(1) - go func() { - defer rl.cleanupWg.Done() + rl.cleanupWg.Go(func() { rl.cleanup() - }() + }) } return nil @@ -150,7 +148,7 @@ func (rl *RotateLogs) cleanup() { // Generate glob pattern from the strftime pattern // For example, "storage/logs/daily-%Y-%m-%d.log" becomes "storage/logs/daily-*.log" globPattern := rl.generateGlobPattern() - + // Find all files matching the pattern matches, err := filepath.Glob(globPattern) if err != nil { @@ -164,23 +162,23 @@ func (rl *RotateLogs) cleanup() { if err != nil { continue } - + // Skip if not a regular file if !fi.Mode().IsRegular() { continue } - + logFiles = append(logFiles, path) } // Sort files by modification time (oldest first) - sort.Slice(logFiles, func(i, j int) bool { - infoI, errI := os.Stat(logFiles[i]) - infoJ, errJ := os.Stat(logFiles[j]) - if errI != nil || errJ != nil { - return false + slices.SortFunc(logFiles, func(a, b string) int { + infoA, errA := os.Stat(a) + infoB, errB := os.Stat(b) + if errA != nil || errB != nil { + return 0 } - return infoI.ModTime().Before(infoJ.ModTime()) + return infoA.ModTime().Compare(infoB.ModTime()) }) // Keep only the most recent rotationCount files @@ -196,7 +194,7 @@ func (rl *RotateLogs) cleanup() { func (rl *RotateLogs) generateGlobPattern() string { // Get the pattern and replace all strftime specifiers with * pattern := rl.pattern.pattern - + // Replace common strftime patterns with wildcards replacements := []string{ "%Y", "*", @@ -206,12 +204,12 @@ func (rl *RotateLogs) generateGlobPattern() string { "%M", "*", "%S", "*", } - + result := pattern for i := 0; i < len(replacements); i += 2 { result = strings.ReplaceAll(result, replacements[i], replacements[i+1]) } - + return result } diff --git a/log/utils.go b/log/utils.go index 0ada8ee7e..98e0d715b 100644 --- a/log/utils.go +++ b/log/utils.go @@ -3,6 +3,7 @@ package log import ( "context" "log/slog" + "slices" "github.com/goravel/framework/contracts/log" ) @@ -40,12 +41,7 @@ type hookAdapter struct { } func (h *hookAdapter) Enabled(level log.Level) bool { - for _, l := range h.hook.Levels() { - if l == level { - return true - } - } - return false + return slices.Contains(h.hook.Levels(), level) } func (h *hookAdapter) Handle(entry log.Entry) error { diff --git a/mail/mail.go b/mail/mail.go index 4732d216d..e9efc12b5 100644 --- a/mail/mail.go +++ b/mail/mail.go @@ -87,8 +87,7 @@ func (tr *trimReader) Read(buf []byte) (int, error) { func handleAddressList(v []string) []string { res := []string{} for _, a := range v { - w := strings.Split(a, ",") - for _, addr := range w { + for addr := range strings.SplitSeq(a, ",") { decodedAddr, err := (&mime.WordDecoder{}).DecodeHeader(strings.TrimSpace(addr)) if err == nil { res = append(res, decodedAddr) @@ -511,7 +510,7 @@ func (e *Email) Send(addr string, a smtp.Auth) error { // Merge the To, Cc, and Bcc fields to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc)) to = append(append(append(to, e.To...), e.Cc...), e.Bcc...) - for i := 0; i < len(to); i++ { + for i := range to { addr, err := mail.ParseAddress(to[i]) if err != nil { return err @@ -558,7 +557,7 @@ func (e *Email) SendWithTLS(addr string, a smtp.Auth, t *tls.Config) error { // Merge the To, Cc, and Bcc fields to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc)) to = append(append(append(to, e.To...), e.Cc...), e.Bcc...) - for i := 0; i < len(to); i++ { + for i := range to { addr, err := mail.ParseAddress(to[i]) if err != nil { return err @@ -632,7 +631,7 @@ func (e *Email) SendWithStartTLS(addr string, a smtp.Auth, t *tls.Config) error // Merge the To, Cc, and Bcc fields to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc)) to = append(append(append(to, e.To...), e.Cc...), e.Bcc...) - for i := 0; i < len(to); i++ { + for i := range to { addr, err := mail.ParseAddress(to[i]) if err != nil { return err diff --git a/mail/mail_test.go b/mail/mail_test.go index 08c8551ab..f23b1e537 100644 --- a/mail/mail_test.go +++ b/mail/mail_test.go @@ -868,7 +868,7 @@ func Benchmark_base64Wrap(b *testing.B) { if _, err := rand.Read(file); err != nil { panic(err) } - for i := 0; i <= b.N; i++ { + for b.Loop() { base64Wrap(io.Discard, file) } } diff --git a/notification/channels/mail.go b/notification/channels/mail.go index 5a625f987..805106966 100644 --- a/notification/channels/mail.go +++ b/notification/channels/mail.go @@ -3,7 +3,8 @@ package channels import ( "encoding/json" "fmt" - "sort" + "maps" + "slices" "strings" contractsmail "github.com/goravel/framework/contracts/mail" @@ -109,11 +110,7 @@ func (c *MailChannel) resolveAddresses( // ordered list of "Name
" (or bare "address" when name is // empty) strings. func formatAddresses(routes map[string]string) []string { - addresses := make([]string, 0, len(routes)) - for address := range routes { - addresses = append(addresses, address) - } - sort.Strings(addresses) // deterministic order — map iteration isn't + addresses := slices.Sorted(maps.Keys(routes)) // deterministic order — map iteration isn't formatted := make([]string, 0, len(addresses)) for _, address := range addresses { diff --git a/packages/setup.go b/packages/setup.go index 21d359aad..838628266 100644 --- a/packages/setup.go +++ b/packages/setup.go @@ -38,17 +38,17 @@ func Setup(args []string) packages.Setup { if arg == "--force" || arg == "-f" { st.force = true } - if strings.HasPrefix(arg, "--facade=") { - st.facade = strings.TrimPrefix(arg, "--facade=") + if after, ok := strings.CutPrefix(arg, "--facade="); ok { + st.facade = after } - if strings.HasPrefix(arg, "--driver=") { - st.driver = strings.TrimPrefix(arg, "--driver=") + if after, ok := strings.CutPrefix(arg, "--driver="); ok { + st.driver = after } - if strings.HasPrefix(arg, "--main-path=") { - mainName = strings.TrimPrefix(arg, "--main-path=") + if after, ok := strings.CutPrefix(arg, "--main-path="); ok { + mainName = after } - if strings.HasPrefix(arg, "--paths=") { - if err := json.Unmarshal([]byte(strings.TrimPrefix(arg, "--paths=")), &support.Config.Paths); err != nil { + if after, ok := strings.CutPrefix(arg, "--paths="); ok { + if err := json.Unmarshal([]byte(after), &support.Config.Paths); err != nil { panic(fmt.Sprintf("failed to unmarshal paths: %s", err)) } } diff --git a/process/pipeline.go b/process/pipeline.go index b1f515688..a3e069a78 100644 --- a/process/pipeline.go +++ b/process/pipeline.go @@ -218,7 +218,7 @@ func (r *Pipeline) start(configurer func(contractsprocess.Pipe)) (contractsproce cancel() } - for j := 0; j < started; j++ { + for j := range started { if commands[j].Process != nil { _ = kill(commands[j].Process) } diff --git a/process/pool.go b/process/pool.go index 84f196d1d..bb5990c0f 100644 --- a/process/pool.go +++ b/process/pool.go @@ -144,17 +144,15 @@ func (r *PoolBuilder) start(configurer func(contractsprocess.Pool)) (contractspr // It waits for all expected results before exiting. resultsWg.Add(len(commands)) go func() { - for i := 0; i < len(commands); i++ { + for range commands { rc := <-resultCh runningPool.setResult(rc.key, rc.res) resultsWg.Done() } }() - for i := 0; i < concurrency; i++ { - workersWg.Add(1) - go func() { - defer workersWg.Done() + for range concurrency { + workersWg.Go(func() { for command := range jobCh { if ctx.Err() != nil { // If the pool was stopped (Stop() called or timeout reached), we skip execution. @@ -196,7 +194,7 @@ func (r *PoolBuilder) start(configurer func(contractsprocess.Pool)) (contractspr resultCh <- result{key: command.key, res: res} } } - }() + }) } for _, command := range commands { diff --git a/process/running.go b/process/running.go index 1b1eff2d9..7f5c01604 100644 --- a/process/running.go +++ b/process/running.go @@ -149,8 +149,7 @@ func getExitCode(cmd *exec.Cmd, err error) int { if cmd != nil && cmd.ProcessState != nil { exitCode = cmd.ProcessState.ExitCode() } else if err != nil { - var ee *exec.ExitError - if errors.As(err, &ee) { + if ee, ok := errors.AsType[*exec.ExitError](err); ok { exitCode = ee.ExitCode() } } else { diff --git a/queue/config.go b/queue/config.go index 8fa9fba56..5ef717d55 100644 --- a/queue/config.go +++ b/queue/config.go @@ -178,7 +178,7 @@ func configuredQueue(config contractsconfig.Config, connection string) string { // duplicate names to match Laravel's semantics. func splitQueueNames(queue string) []string { queueNames := make([]string, 0, strings.Count(queue, ",")+1) - for _, queueName := range strings.Split(queue, ",") { + for queueName := range strings.SplitSeq(queue, ",") { queueName = strings.TrimSpace(queueName) if queueName != "" { queueNames = append(queueNames, queueName) diff --git a/queue/utils/convert_test.go b/queue/utils/convert_test.go index 98929b49b..9e62f2c7e 100644 --- a/queue/utils/convert_test.go +++ b/queue/utils/convert_test.go @@ -9,7 +9,6 @@ import ( mocksfoundation "github.com/goravel/framework/mocks/foundation" mocksqueue "github.com/goravel/framework/mocks/queue" "github.com/goravel/framework/support/carbon" - "github.com/goravel/framework/support/convert" ) func TestTaskToJson(t *testing.T) { @@ -100,7 +99,7 @@ func TestTaskToJson(t *testing.T) { UUID: "test-uuid", Job: Job{ Signature: "test_job_one", - Delay: convert.Pointer(carbon.Now().StdTime()), + Delay: new(carbon.Now().StdTime()), }, } mockJson.EXPECT().MarshalString(expectedTask).Return("{\"test\":true}", nil).Once() diff --git a/queue/worker.go b/queue/worker.go index 594f4c974..5c1358f5c 100644 --- a/queue/worker.go +++ b/queue/worker.go @@ -240,13 +240,11 @@ func (r *Worker) run() error { color.Infoln(errors.QueueProcessingJobs.Args(r.connection, strings.Join(queueNames, ",")).Error()) } - r.failedJobWg.Add(1) - go func() { - defer r.failedJobWg.Done() + r.failedJobWg.Go(func() { for job := range r.failedJobChan { r.logFailedJob(job) } - }() + }) if receiver, ok := r.driver.(queue.DriverWithReceive); ok && len(queueNames) == 1 { return r.runWithReceive(receiver, queueNames[0]) @@ -256,11 +254,8 @@ func (r *Worker) run() error { } func (r *Worker) runWithPop(queueNames []string) error { - for i := 0; i < r.concurrent; i++ { - r.jobWg.Add(1) - go func() { - defer r.jobWg.Done() - + for range r.concurrent { + r.jobWg.Go(func() { // TODO make the initial delay and max delay configurable currentDelay := 1 * time.Second maxDelay := 32 * time.Second @@ -293,7 +288,7 @@ func (r *Worker) runWithPop(queueNames []string) error { currentDelay = 1 * time.Second r.processReservedJob(reservedJob, queueName) } - }() + }) } r.jobWg.Wait() @@ -352,11 +347,9 @@ func (r *Worker) runWithReceive(receiver queue.DriverWithReceive, queueName stri var wg sync.WaitGroup for _, reservedJob := range jobs { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { r.processReservedJob(reservedJob, queueName) - }() + }) } wg.Wait() } diff --git a/session/driver/file_test.go b/session/driver/file_test.go index 6a1fb0329..30d75d081 100644 --- a/session/driver/file_test.go +++ b/session/driver/file_test.go @@ -115,15 +115,13 @@ func BenchmarkFile_ReadWrite(b *testing.B) { driver := f.getDriver() f.Nil(driver.Write("foo", "bar")) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { f.Nil(driver.Write("foo", "bar")) value, err := driver.Read("foo") f.Nil(err) f.Equal("bar", value) } - b.StopTimer() f.BeforeTest() } diff --git a/session/manager_test.go b/session/manager_test.go index 193c627a3..293f6fd23 100644 --- a/session/manager_test.go +++ b/session/manager_test.go @@ -160,8 +160,7 @@ func BenchmarkSession_ManagerInteraction(b *testing.B) { s.mockConfig.On("GetInt", "session.gc_interval").Return(30) s.mockConfig.On("GetString", "session.cookie").Return("bench_cookie") - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { driverInstance, err := s.manager.Driver() if err != nil { @@ -182,5 +181,4 @@ func BenchmarkSession_ManagerInteraction(b *testing.B) { s.manager.ReleaseSession(session) } - b.StopTimer() } diff --git a/support/collect/collect_test.go b/support/collect/collect_test.go index 8b81580c0..f76f0e48f 100644 --- a/support/collect/collect_test.go +++ b/support/collect/collect_test.go @@ -1,7 +1,7 @@ package collect import ( - "sort" + "slices" "strconv" "testing" @@ -58,8 +58,8 @@ func TestGroupBy(t *testing.T) { func TestKeys(t *testing.T) { keys1 := Keys[int, string](map[int]string{1: "foo", 2: "bar"}) keys2 := Keys[string, int](map[string]int{"foo": 1, "bar": 2}) - sort.Ints(keys1) - sort.Strings(keys2) + slices.Sort(keys1) + slices.Sort(keys2) assert.Equal(t, []int{1, 2}, keys1) assert.Equal(t, []string{"bar", "foo"}, keys2) } @@ -139,8 +139,8 @@ func TestUnique(t *testing.T) { func TestValues(t *testing.T) { values1 := Values[string, int](map[string]int{"foo": 1, "bar": 2}) values2 := Values[int, string](map[int]string{1: "foo", 2: "bar"}) - sort.Ints(values1) - sort.Strings(values2) + slices.Sort(values1) + slices.Sort(values2) assert.Equal(t, []int{1, 2}, values1) assert.Equal(t, []string{"bar", "foo"}, values2) } diff --git a/support/collect/collection.go b/support/collect/collection.go index ef3fa88d3..09b1d291e 100644 --- a/support/collect/collection.go +++ b/support/collect/collection.go @@ -4,18 +4,12 @@ import ( "encoding/json" "fmt" "math" - "math/rand" + "math/rand/v2" "reflect" + "slices" "sort" "strconv" "strings" - "sync" - "time" -) - -var ( - randomMu sync.Mutex - randomGen = rand.New(rand.NewSource(time.Now().UnixNano())) ) type Collection[T any] struct { @@ -66,10 +60,7 @@ func (c *Collection[T]) Chunk(size int) [][]T { var chunks [][]T for i := 0; i < len(c.items); i += size { - end := i + size - if end > len(c.items) { - end = len(c.items) - } + end := min(i+size, len(c.items)) chunks = append(chunks, c.items[i:end]) } return chunks @@ -110,8 +101,8 @@ func (c *Collection[T]) Collapse() *Collection[T] { for _, item := range c.items { v := reflect.ValueOf(item) if v.Kind() == reflect.Slice { - for i := 0; i < v.Len(); i++ { - if elem, ok := v.Index(i).Interface().(T); ok { + for i := range v.Len() { + if elem, ok := reflect.TypeAssert[T](v.Index(i)); ok { flattened = append(flattened, elem) } } @@ -247,8 +238,8 @@ func (c *Collection[T]) EachSpread(fn func(...T)) *Collection[T] { v := reflect.ValueOf(item) if v.Kind() == reflect.Slice { args := make([]T, v.Len()) - for i := 0; i < v.Len(); i++ { - if elem, ok := v.Index(i).Interface().(T); ok { + for i := range v.Len() { + if elem, ok := reflect.TypeAssert[T](v.Index(i)); ok { args[i] = elem } } @@ -333,10 +324,7 @@ func (c *Collection[T]) ForPage(page, perPage int) *Collection[T] { return &Collection[T]{items: []T{}} } - end := start + perPage - if end > len(c.items) { - end = len(c.items) - } + end := min(start+perPage, len(c.items)) return &Collection[T]{items: c.items[start:end]} } @@ -358,12 +346,7 @@ func (c *Collection[T]) Has(index int) bool { } func (c *Collection[T]) HasAny(indices ...int) bool { - for _, index := range indices { - if c.Has(index) { - return true - } - } - return false + return slices.ContainsFunc(indices, c.Has) } func (c *Collection[T]) Intersect(other *Collection[T]) *Collection[T] { @@ -477,8 +460,8 @@ func (c *Collection[T]) MapSpread(fn func(...T) T) *Collection[T] { v := reflect.ValueOf(item) if v.Kind() == reflect.Slice { args := make([]T, v.Len()) - for i := 0; i < v.Len(); i++ { - if elem, ok := v.Index(i).Interface().(T); ok { + for i := range v.Len() { + if elem, ok := reflect.TypeAssert[T](v.Index(i)); ok { args[i] = elem } } @@ -702,10 +685,7 @@ func (c *Collection[T]) Random() *T { if len(c.items) == 0 { return nil } - randomMu.Lock() - defer randomMu.Unlock() - - return &c.items[randomGen.Intn(len(c.items))] + return &c.items[rand.IntN(len(c.items))] } func (c *Collection[T]) Reject(predicate func(T, int) bool) *Collection[T] { @@ -769,11 +749,9 @@ func (c *Collection[T]) Shuffle() *Collection[T] { shuffled := make([]T, len(c.items)) copy(shuffled, c.items) - rng := rand.New(rand.NewSource(time.Now().UnixNano())) - for i := len(shuffled) - 1; i > 0; i-- { - j := rng.Intn(i + 1) + rand.Shuffle(len(shuffled), func(i, j int) { shuffled[i], shuffled[j] = shuffled[j], shuffled[i] - } + }) return &Collection[T]{items: shuffled} } @@ -816,10 +794,7 @@ func (c *Collection[T]) Slice(offset int, length ...int) *Collection[T] { end := len(c.items) if len(length) > 0 { - end = offset + length[0] - if end > len(c.items) { - end = len(c.items) - } + end = min(offset+length[0], len(c.items)) if end < offset { end = offset } @@ -871,10 +846,7 @@ func (c *Collection[T]) Splice(start, deleteCount int, replacement ...T) *Collec deleteCount = 0 } - end := start + deleteCount - if end > len(c.items) { - end = len(c.items) - } + end := min(start+deleteCount, len(c.items)) removed := make([]T, end-start) copy(removed, c.items[start:end]) @@ -1107,13 +1079,10 @@ func (c *Collection[T]) WhereNull(field string) *Collection[T] { } func (c *Collection[T]) Zip(other *Collection[T]) [][]T { - maxLen := len(c.items) - if len(other.items) > maxLen { - maxLen = len(other.items) - } + maxLen := max(len(c.items), len(other.items)) var result [][]T - for i := 0; i < maxLen; i++ { + for i := range maxLen { var pair []T if i < len(c.items) { pair = append(pair, c.items[i]) diff --git a/support/collect/lazy_collection.go b/support/collect/lazy_collection.go index 4d8658346..a019bf5d6 100644 --- a/support/collect/lazy_collection.go +++ b/support/collect/lazy_collection.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "reflect" + "slices" "sort" "strings" ) @@ -62,7 +63,7 @@ func LazyGenerate[T any](fn func(int) T, count int) *LazyCollection[T] { ch := make(chan T) go func() { defer close(ch) - for i := 0; i < count; i++ { + for i := range count { ch <- fn(i) } }() @@ -128,7 +129,7 @@ func LazyRepeat[T any](value T, count int) *LazyCollection[T] { ch := make(chan T) go func() { defer close(ch) - for i := 0; i < count; i++ { + for range count { ch <- value } }() @@ -493,8 +494,8 @@ func (lc *LazyCollection[T]) Reverse() *LazyCollection[T] { go func() { defer close(ch) items := lc.All() - for i := len(items) - 1; i >= 0; i-- { - ch <- items[i] + for _, item := range slices.Backward(items) { + ch <- item } }() return ch diff --git a/support/convert/convert.go b/support/convert/convert.go index f2a679fbb..a9650fe9d 100644 --- a/support/convert/convert.go +++ b/support/convert/convert.go @@ -24,6 +24,8 @@ func Default[T comparable](values ...T) T { // // Pointer("foo") // *string("foo") // Pointer(1) // *int(1) +// +// Deprecated: Use new(value) instead, Pointer will be removed in a future version. func Pointer[T any](value T) *T { return &value } diff --git a/support/convert/type.go b/support/convert/type.go index a76400731..c497f0774 100644 --- a/support/convert/type.go +++ b/support/convert/type.go @@ -27,7 +27,7 @@ func ToSliceE[T int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | case reflect.Slice, reflect.Array: s := reflect.ValueOf(i) a := make([]T, s.Len()) - for j := 0; j < s.Len(); j++ { + for j := range s.Len() { switch any(a).(type) { case []int8: val, err := cast.ToInt8E(s.Index(j).Interface()) @@ -119,7 +119,7 @@ func ToAnySlice[T any](s ...T) []any { if v.Kind() == reflect.Slice { // Handle slice case result := make([]any, v.Len()) - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { result[i] = v.Index(i).Interface() } return result diff --git a/support/database/database.go b/support/database/database.go index 1a44fdcae..7d9dda07d 100644 --- a/support/database/database.go +++ b/support/database/database.go @@ -22,7 +22,7 @@ func GetIDByReflect(t reflect.Type, v reflect.Value) any { v = v.Elem() } - for i := 0; i < t.NumField(); i++ { + for i := range t.NumField() { if !t.Field(i).IsExported() { continue } diff --git a/support/docker/utils.go b/support/docker/utils.go index 7f816ac77..6fb8c9fd0 100644 --- a/support/docker/utils.go +++ b/support/docker/utils.go @@ -2,7 +2,7 @@ package docker import ( "fmt" - "math/rand" + "math/rand/v2" "net" "strings" @@ -81,7 +81,7 @@ func IsPortUsing(port int) bool { func ValidPort() int { for range 60 { - random := rand.Intn(10000) + 10000 + random := rand.IntN(10000) + 10000 if !IsPortUsing(random) { return random } diff --git a/support/maps/maps.go b/support/maps/maps.go index 76b69f324..4033ad5b5 100644 --- a/support/maps/maps.go +++ b/support/maps/maps.go @@ -45,7 +45,7 @@ func FromStruct(data any) map[string]any { return res } - for i := 0; i < dataType.NumField(); i++ { + for i := range dataType.NumField() { fieldType := dataType.Field(i) fieldValue := dataValue.Field(i) diff --git a/support/str/str.go b/support/str/str.go index 16cbca2bb..f92344046 100644 --- a/support/str/str.go +++ b/support/str/str.go @@ -175,7 +175,7 @@ func (s *String) Dirname(levels ...int) *String { } dir := s.value - for i := 0; i < defaultLevels; i++ { + for range defaultLevels { dir = filepath.Dir(dir) } diff --git a/telemetry/instrumentation/database/database.go b/telemetry/instrumentation/database/database.go index d2c73ced5..e8a627e6f 100644 --- a/telemetry/instrumentation/database/database.go +++ b/telemetry/instrumentation/database/database.go @@ -6,6 +6,7 @@ import ( "database/sql/driver" "errors" "io" + "slices" "strings" "sync" "time" @@ -178,7 +179,7 @@ func (r *Instrument) endSpan(ctx context.Context, span trace.Span, start time.Ti operation = unknownOperation } - attrs := append([]telemetry.KeyValue{}, r.baseAttrs...) + attrs := slices.Clone(r.baseAttrs) attrs = append(attrs, semconv.DBOperationName(operation)) if operationName(query) != "" { attrs = append(attrs, semconv.DBQueryText(query)) @@ -197,7 +198,7 @@ func (r *Instrument) endSpan(ctx context.Context, span trace.Span, start time.Ti attrs = append(attrs, telemetry.String(attrResolverMode, resolverMode)) } - metricAttrs := append([]telemetry.KeyValue{}, r.baseAttrs...) + metricAttrs := slices.Clone(r.baseAttrs) metricAttrs = append(metricAttrs, semconv.DBOperationName(operation)) if table != "" { metricAttrs = append(metricAttrs, semconv.DBCollectionName(table)) diff --git a/telemetry/instrumentation/log/mapper.go b/telemetry/instrumentation/log/mapper.go index c60829fc9..051ed678e 100644 --- a/telemetry/instrumentation/log/mapper.go +++ b/telemetry/instrumentation/log/mapper.go @@ -113,7 +113,7 @@ func toReflectedValue(v any) attribute.Value { switch t.Kind() { case reflect.Slice, reflect.Array: items := make([]attribute.Value, val.Len()) - for i := 0; i < val.Len(); i++ { + for i := range val.Len() { items[i] = toValue(val.Index(i).Interface()) } return attribute.SliceValue(items...) diff --git a/telemetry/propagator.go b/telemetry/propagator.go index b374e8349..f52a54830 100644 --- a/telemetry/propagator.go +++ b/telemetry/propagator.go @@ -24,7 +24,7 @@ func newCompositeTextMapPropagator(nameStr string) (propagation.TextMapPropagato var propagators []propagation.TextMapPropagator - for _, name := range strings.Split(nameStr, ",") { + for name := range strings.SplitSeq(nameStr, ",") { name = strings.TrimSpace(name) if name == "" { continue diff --git a/tests/container.go b/tests/container.go index b3acb5055..72f145123 100644 --- a/tests/container.go +++ b/tests/container.go @@ -75,7 +75,7 @@ func (r *Container) Build() (docker.DatabaseDriver, error) { func (r *Container) Builds(num int) ([]docker.DatabaseDriver, error) { var databaseDrivers []docker.DatabaseDriver - for i := 0; i < num; i++ { + for range num { databaseDriver, err := r.Build() if err != nil { return nil, err diff --git a/tests/db_test.go b/tests/db_test.go index 8f4ac31d3..6389a72a1 100644 --- a/tests/db_test.go +++ b/tests/db_test.go @@ -16,7 +16,6 @@ import ( "github.com/goravel/framework/errors" mocksconfig "github.com/goravel/framework/mocks/config" "github.com/goravel/framework/support/carbon" - "github.com/goravel/framework/support/convert" "github.com/goravel/framework/support/str" "github.com/goravel/framework/testing/utils" "github.com/goravel/postgres" @@ -167,7 +166,7 @@ func (s *DBTestSuite) TestCursor() { s.Run(driver, func() { query.DB().Table("products").Insert([]Product{ { - Name: "cursor_product1", Weight: convert.Pointer(100), Model: Model{ + Name: "cursor_product1", Weight: new(100), Model: Model{ Timestamps: Timestamps{ CreatedAt: s.now, UpdatedAt: s.now, @@ -175,7 +174,7 @@ func (s *DBTestSuite) TestCursor() { }, }, { - Name: "cursor_product2", Weight: convert.Pointer(200), Model: Model{ + Name: "cursor_product2", Weight: new(200), Model: Model{ Timestamps: Timestamps{ CreatedAt: s.now, UpdatedAt: s.now, @@ -310,7 +309,7 @@ func (s *DBTestSuite) Test_DB_Select_Insert_Update_Delete_Statement() { func (s *DBTestSuite) TestDecrement() { for driver, query := range s.queries { s.Run(driver, func() { - query.DB().Table("products").Insert(Product{Name: "decrement_product", Weight: convert.Pointer(100)}) + query.DB().Table("products").Insert(Product{Name: "decrement_product", Weight: new(100)}) s.Run("decrement", func() { err := query.DB().Table("products").Where("name", "decrement_product").Decrement("weight", 1) @@ -339,7 +338,7 @@ func (s *DBTestSuite) TestDistinct() { for driver, query := range s.queries { s.Run(driver, func() { query.DB().Table("products").Insert([]Product{ - {Name: "distinct_product", Weight: convert.Pointer(1)}, + {Name: "distinct_product", Weight: new(1)}, {Name: "distinct_product"}, {Name: "distinct_product"}, }) @@ -441,7 +440,7 @@ func (s *DBTestSuite) TestGroupBy_Having() { func (s *DBTestSuite) TestIncrement() { for driver, query := range s.queries { s.Run(driver, func() { - query.DB().Table("products").Insert(Product{Name: "increment_product", Weight: convert.Pointer(100)}) + query.DB().Table("products").Insert(Product{Name: "increment_product", Weight: new(100)}) s.Run("increment", func() { err := query.DB().Table("products").Where("name", "increment_product").Increment("weight", 1) @@ -548,7 +547,7 @@ func (s *DBTestSuite) TestInsert_First_Get() { }, { Name: "multiple structs2", - Weight: convert.Pointer(1), + Weight: new(1), }, }) s.NoError(err) @@ -743,9 +742,9 @@ func (s *DBTestSuite) TestLeftJoin() { s.NoError(err) s.Equal(3, len(temps)) - s.Contains(temps, Temp{P1Name: convert.Pointer("join_product1"), P1Weight: convert.Pointer(100), P2Name: convert.Pointer("join_product2"), P2Weight: convert.Pointer(50)}) - s.Contains(temps, Temp{P1Name: convert.Pointer("join_product2"), P1Weight: convert.Pointer(50)}) - s.Contains(temps, Temp{P1Name: convert.Pointer("join_product3"), P1Weight: convert.Pointer(50)}) + s.Contains(temps, Temp{P1Name: new("join_product1"), P1Weight: new(100), P2Name: new("join_product2"), P2Weight: new(50)}) + s.Contains(temps, Temp{P1Name: new("join_product2"), P1Weight: new(50)}) + s.Contains(temps, Temp{P1Name: new("join_product3"), P1Weight: new(50)}) }) } } @@ -897,13 +896,13 @@ func (s *DBTestSuite) TestOrWhereColumn() { query.DB().Table("products").Insert([]Product{ { Name: "or where column model", - Height: convert.Pointer(100), - Weight: convert.Pointer(100), + Height: new(100), + Weight: new(100), }, { Name: "or where column model1", - Height: convert.Pointer(100), - Weight: convert.Pointer(110), + Height: new(100), + Weight: new(110), }, }) @@ -1110,9 +1109,9 @@ func (s *DBTestSuite) TestRightJoin() { s.NoError(err) s.Equal(3, len(temps)) - s.Contains(temps, Temp{P1Name: convert.Pointer("join_product1"), P1Weight: convert.Pointer(100), P2Name: convert.Pointer("join_product2"), P2Weight: convert.Pointer(50)}) - s.Contains(temps, Temp{P2Name: convert.Pointer("join_product1"), P2Weight: convert.Pointer(100)}) - s.Contains(temps, Temp{P2Name: convert.Pointer("join_product3"), P2Weight: convert.Pointer(50)}) + s.Contains(temps, Temp{P1Name: new("join_product1"), P1Weight: new(100), P2Name: new("join_product2"), P2Weight: new(50)}) + s.Contains(temps, Temp{P2Name: new("join_product1"), P2Weight: new(100)}) + s.Contains(temps, Temp{P2Name: new("join_product3"), P2Weight: new(50)}) }) } } @@ -1167,8 +1166,8 @@ func (s *DBTestSuite) TestSum() { for driver, query := range s.queries { s.Run(driver, func() { query.DB().Table("products").Insert([]Product{ - {Name: "sum_product1", Weight: convert.Pointer(100)}, - {Name: "sum_product2", Weight: convert.Pointer(200)}, + {Name: "sum_product1", Weight: new(100)}, + {Name: "sum_product2", Weight: new(200)}, }) var sum int64 @@ -1186,8 +1185,8 @@ func (s *DBTestSuite) TestAvg() { for driver, query := range s.queries { s.Run(driver, func() { query.DB().Table("products").Insert([]Product{ - {Name: "sum_product1", Weight: convert.Pointer(100)}, - {Name: "sum_product2", Weight: convert.Pointer(200)}, + {Name: "sum_product1", Weight: new(100)}, + {Name: "sum_product2", Weight: new(200)}, }) var avg float64 @@ -1205,8 +1204,8 @@ func (s *DBTestSuite) TestMin() { for driver, query := range s.queries { s.Run(driver, func() { query.DB().Table("products").Insert([]Product{ - {Name: "sum_product1", Weight: convert.Pointer(100)}, - {Name: "sum_product2", Weight: convert.Pointer(200)}, + {Name: "sum_product1", Weight: new(100)}, + {Name: "sum_product2", Weight: new(200)}, }) var min int64 @@ -1224,8 +1223,8 @@ func (s *DBTestSuite) TestMax() { for driver, query := range s.queries { s.Run(driver, func() { query.DB().Table("products").Insert([]Product{ - {Name: "sum_product1", Weight: convert.Pointer(100)}, - {Name: "sum_product2", Weight: convert.Pointer(200)}, + {Name: "sum_product1", Weight: new(100)}, + {Name: "sum_product2", Weight: new(200)}, }) var max int64 @@ -1439,7 +1438,7 @@ func (s *DBTestSuite) TestUpdateOrInsert() { result, err := query.DB().Table("products").Where("height", 100).UpdateOrInsert(Product{ Name: "update or insert product2", }, Product{ - Weight: convert.Pointer(200), + Weight: new(200), }) s.NoError(err) s.Equal(int64(1), result.RowsAffected) @@ -1455,7 +1454,7 @@ func (s *DBTestSuite) TestUpdateOrInsert() { result, err = query.DB().Table("products").UpdateOrInsert(Product{ Name: "update or insert product2", }, Product{ - Weight: convert.Pointer(300), + Weight: new(300), }) s.NoError(err) s.Equal(int64(1), result.RowsAffected) @@ -1538,10 +1537,10 @@ func (s *DBTestSuite) TestWhereAny() { for driver, query := range s.queries { s.Run(driver, func() { data := []Product{ - {Name: "where any product1", Weight: convert.Pointer(100), Height: convert.Pointer(50)}, - {Name: "where any product2", Weight: convert.Pointer(200), Height: convert.Pointer(100)}, - {Name: "where any product3", Weight: convert.Pointer(300), Height: convert.Pointer(150)}, - {Name: "where any product4", Weight: convert.Pointer(400), Height: convert.Pointer(200)}, + {Name: "where any product1", Weight: new(100), Height: new(50)}, + {Name: "where any product2", Weight: new(200), Height: new(100)}, + {Name: "where any product3", Weight: new(300), Height: new(150)}, + {Name: "where any product4", Weight: new(400), Height: new(200)}, } result, err := query.DB().Table("products").Insert(&data) s.Equal(int64(4), result.RowsAffected) @@ -1683,10 +1682,10 @@ func (s *DBTestSuite) TestWhereAll() { for driver, query := range s.queries { s.Run(driver, func() { data := []Product{ - {Name: "where all product1", Weight: convert.Pointer(100), Height: convert.Pointer(100)}, - {Name: "where all product2", Weight: convert.Pointer(200), Height: convert.Pointer(200)}, - {Name: "where all product3", Weight: convert.Pointer(150), Height: convert.Pointer(200)}, - {Name: "where all product4", Weight: convert.Pointer(200), Height: convert.Pointer(150)}, + {Name: "where all product1", Weight: new(100), Height: new(100)}, + {Name: "where all product2", Weight: new(200), Height: new(200)}, + {Name: "where all product3", Weight: new(150), Height: new(200)}, + {Name: "where all product4", Weight: new(200), Height: new(150)}, } result, err := query.DB().Table("products").Insert(&data) s.Equal(int64(4), result.RowsAffected) @@ -1841,10 +1840,10 @@ func (s *DBTestSuite) TestWhereNone() { for driver, query := range s.queries { s.Run(driver, func() { data := []Product{ - {Name: "where none product1", Weight: convert.Pointer(100), Height: convert.Pointer(50)}, - {Name: "where none product2", Weight: convert.Pointer(200), Height: convert.Pointer(100)}, - {Name: "where none product3", Weight: convert.Pointer(300), Height: convert.Pointer(150)}, - {Name: "where none product4", Weight: convert.Pointer(400), Height: convert.Pointer(200)}, + {Name: "where none product1", Weight: new(100), Height: new(50)}, + {Name: "where none product2", Weight: new(200), Height: new(100)}, + {Name: "where none product3", Weight: new(300), Height: new(150)}, + {Name: "where none product4", Weight: new(400), Height: new(200)}, } result, err := query.DB().Table("products").Insert(&data) s.Equal(int64(4), result.RowsAffected) @@ -2010,13 +2009,13 @@ func (s *DBTestSuite) TestWhereColumn() { query.DB().Table("products").Insert([]Product{ { Name: "where column model", - Weight: convert.Pointer(100), - Height: convert.Pointer(100), + Weight: new(100), + Height: new(100), }, { Name: "where column model1", - Weight: convert.Pointer(100), - Height: convert.Pointer(110), + Weight: new(100), + Height: new(110), }, }) @@ -2057,13 +2056,13 @@ func (s *DBTestSuite) TestWhereExists() { result, err := query.DB().Table("products").Insert([]Product{ { Name: "where exists model1", - Height: convert.Pointer(100), - Weight: convert.Pointer(100), + Height: new(100), + Weight: new(100), }, { Name: "where exists model2", - Height: convert.Pointer(100), - Weight: convert.Pointer(110), + Height: new(100), + Weight: new(110), }, }) s.NoError(err) @@ -2188,7 +2187,7 @@ func (s *DBTestSuite) TestJsonWhereClauses() { }, { name: "bool key(pointer)", - find: query.DB().Table("json_data").Where("data->bool", convert.Pointer(false)).Find, + find: query.DB().Table("json_data").Where("data->bool", new(false)).Find, assert: func(items []JsonData) { s.Len(items, 1) s.JSONEq(data[1].Data, items[0].Data) @@ -2333,7 +2332,7 @@ func (s *DBTestSuite) TestJsonColumnsUpdate() { }, { name: "update float(pointer)", - update: map[string]any{"data->float": convert.Pointer(456.789)}, + update: map[string]any{"data->float": new(456.789)}, assert: func(before JsonData, after JsonData) { s.NotContains(before.Data, "456.789") s.Contains(after.Data, "456.789") @@ -2538,9 +2537,7 @@ func Benchmark_DB(b *testing.B) { query := NewTestQueryBuilder().Postgres("", false) query.CreateTable(TestTableAuthors) - b.ResetTimer() - - for i := 0; i < b.N; i++ { + for b.Loop() { _, err := query.DB().Table("authors").Insert(Author{ BookID: 1, Name: "benchmark", diff --git a/tests/query_test.go b/tests/query_test.go index 0e3c50188..9a051a523 100644 --- a/tests/query_test.go +++ b/tests/query_test.go @@ -15,7 +15,6 @@ import ( "github.com/goravel/framework/database/orm" "github.com/goravel/framework/errors" "github.com/goravel/framework/support/carbon" - "github.com/goravel/framework/support/convert" "github.com/goravel/mysql" "github.com/goravel/postgres" "github.com/goravel/sqlite" @@ -4776,7 +4775,7 @@ func (s *QueryTestSuite) TestJsonWhereClauses() { }, { name: "bool key(pointer)", - find: query.Query().Where("data->bool", convert.Pointer(false)).Find, + find: query.Query().Where("data->bool", new(false)).Find, assert: func(items []JsonData) { s.Len(items, 1) s.JSONEq(data[1].Data, items[0].Data) @@ -4919,7 +4918,7 @@ func (s *QueryTestSuite) TestJsonColumnsUpdate() { }, { name: "update float(pointer)", - update: map[string]any{"data->float": convert.Pointer(456.789)}, + update: map[string]any{"data->float": new(456.789)}, assert: func(before JsonData, after JsonData) { s.NotContains(before.Data, "456.789") s.Contains(after.Data, "456.789") @@ -5357,10 +5356,10 @@ func (s *QueryTestSuite) TestWhereAny() { for driver, query := range s.queries { s.Run(driver, func() { users := []User{ - {Name: "where_any_user1", Avatar: "where_any_avatar1", Bio: convert.Pointer("bio1")}, - {Name: "where_any_user2", Avatar: "where_any_avatar2", Bio: convert.Pointer("bio2")}, - {Name: "where_any_user3", Avatar: "where_any_avatar3", Bio: convert.Pointer("bio3")}, - {Name: "where_any_user4", Avatar: "where_any_avatar4", Bio: convert.Pointer("bio4")}, + {Name: "where_any_user1", Avatar: "where_any_avatar1", Bio: new("bio1")}, + {Name: "where_any_user2", Avatar: "where_any_avatar2", Bio: new("bio2")}, + {Name: "where_any_user3", Avatar: "where_any_avatar3", Bio: new("bio3")}, + {Name: "where_any_user4", Avatar: "where_any_avatar4", Bio: new("bio4")}, } s.Nil(query.Query().Create(&users)) @@ -5432,10 +5431,10 @@ func (s *QueryTestSuite) TestWhereAll() { for driver, query := range s.queries { s.Run(driver, func() { users := []User{ - {Name: "where_all_user1", Avatar: "where_all_avatar1", Bio: convert.Pointer("bio1")}, - {Name: "where_all_user1", Avatar: "where_all_avatar2", Bio: convert.Pointer("bio2")}, - {Name: "where_all_user2", Avatar: "where_all_avatar1", Bio: convert.Pointer("bio3")}, - {Name: "where_all_user2", Avatar: "where_all_avatar2", Bio: convert.Pointer("bio4")}, + {Name: "where_all_user1", Avatar: "where_all_avatar1", Bio: new("bio1")}, + {Name: "where_all_user1", Avatar: "where_all_avatar2", Bio: new("bio2")}, + {Name: "where_all_user2", Avatar: "where_all_avatar1", Bio: new("bio3")}, + {Name: "where_all_user2", Avatar: "where_all_avatar2", Bio: new("bio4")}, } s.Nil(query.Query().Create(&users)) @@ -5509,10 +5508,10 @@ func (s *QueryTestSuite) TestWhereNone() { for driver, query := range s.queries { s.Run(driver, func() { users := []User{ - {Name: "where_none_user1", Avatar: "where_none_avatar1", Bio: convert.Pointer("bio1")}, - {Name: "where_none_user2", Avatar: "where_none_avatar2", Bio: convert.Pointer("bio2")}, - {Name: "where_none_user3", Avatar: "where_none_avatar3", Bio: convert.Pointer("bio3")}, - {Name: "where_none_user4", Avatar: "where_none_avatar4", Bio: convert.Pointer("bio4")}, + {Name: "where_none_user1", Avatar: "where_none_avatar1", Bio: new("bio1")}, + {Name: "where_none_user2", Avatar: "where_none_avatar2", Bio: new("bio2")}, + {Name: "where_none_user3", Avatar: "where_none_avatar3", Bio: new("bio3")}, + {Name: "where_none_user4", Avatar: "where_none_avatar4", Bio: new("bio4")}, } s.Nil(query.Query().Create(&users)) @@ -5595,9 +5594,7 @@ func Benchmark_Orm(b *testing.B) { query := NewTestQueryBuilder().Postgres("", false) query.CreateTable(TestTableAuthors) - b.ResetTimer() - - for i := 0; i < b.N; i++ { + for b.Loop() { author := Author{ Name: "benchmark", BookID: 1, diff --git a/translation/file_loader_test.go b/translation/file_loader_test.go index 96e3a7af2..7095b4db0 100644 --- a/translation/file_loader_test.go +++ b/translation/file_loader_test.go @@ -108,16 +108,13 @@ func Benchmark_Load(b *testing.B) { s.SetT(&testing.T{}) s.SetupSuite() s.SetupTest() - b.StartTimer() - b.ResetTimer() paths := []string{"./lang"} loader := NewFileLoader(paths, s.json) - for i := 0; i < b.N; i++ { + for b.Loop() { _, err := loader.Load("en", "test") s.NoError(err) } - b.StopTimer() s.TearDownSuite() } diff --git a/translation/fs_loader_test.go b/translation/fs_loader_test.go index 8e79208b2..71fccfe7b 100644 --- a/translation/fs_loader_test.go +++ b/translation/fs_loader_test.go @@ -75,14 +75,10 @@ func Benchmark_FSLoad(b *testing.B) { s := new(FSLoaderTestSuite) s.SetT(&testing.T{}) s.SetupTest() - b.StartTimer() - b.ResetTimer() loader := NewFSLoader(s.mockFS, s.json) - for i := 0; i < b.N; i++ { + for b.Loop() { _, err := loader.Load("en", "test") s.NoError(err) } - - b.StopTimer() } diff --git a/translation/translator.go b/translation/translator.go index b934ebca4..921c12164 100644 --- a/translation/translator.go +++ b/translation/translator.go @@ -115,7 +115,7 @@ func (t *Translator) Get(key string, options ...translationcontract.Option) stri if len(options) > 0 { fallbackOptions = options[0] } - fallbackOptions.Fallback = translationcontract.Bool(false) + fallbackOptions.Fallback = new(false) fallbackOptions.Locale = fallbackLocale return t.Get(key, fallbackOptions) } diff --git a/translation/translator_test.go b/translation/translator_test.go index 665de8a14..c19e01ba5 100644 --- a/translation/translator_test.go +++ b/translation/translator_test.go @@ -181,7 +181,7 @@ func (s *TranslatorTestSuite) TestGet() { "nonexistentKey": "French translation", }, nil) translation = translator.Get("test3.nonexistentKey", contractstranslation.Option{ - Fallback: contractstranslation.Bool(true), + Fallback: new(true), Locale: "en", }) s.Equal("French translation", translation) @@ -191,7 +191,7 @@ func (s *TranslatorTestSuite) TestGet() { s.mockLoader.On("Load", "en", "*").Once().Return(map[string]any{}, errors.LangFileNotExist) s.mockLoader.On("Load", "en", "test4").Once().Return(map[string]any{}, errors.LangFileNotExist) translation = translator.Get("test4.nonexistentKey", contractstranslation.Option{ - Fallback: contractstranslation.Bool(false), + Fallback: new(false), Locale: "en", }) s.Equal("test4.nonexistentKey", translation) @@ -214,7 +214,7 @@ func (s *TranslatorTestSuite) TestGet() { }, }, nil) translation = translator.Get("fallback.nonexistentKey", contractstranslation.Option{ - Fallback: contractstranslation.Bool(true), + Fallback: new(true), Locale: "en", }) s.Equal("French translation", translation) @@ -640,8 +640,6 @@ func Benchmark_Choice(b *testing.B) { s := new(TranslatorTestSuite) s.SetT(&testing.T{}) s.SetupTest() - b.StartTimer() - b.ResetTimer() translator := NewTranslator(s.ctx, nil, s.mockLoader, "en", "en", s.mockLog) s.mockLoader.On("Load", "en", "*").Return(map[string]any{ @@ -650,19 +648,15 @@ func Benchmark_Choice(b *testing.B) { }, }, nil) - for i := 0; i < b.N; i++ { + for b.Loop() { translator.Choice("test.foo", 1) } - - b.StopTimer() } func Benchmark_Get(b *testing.B) { s := new(TranslatorTestSuite) s.SetT(&testing.T{}) s.SetupTest() - b.StartTimer() - b.ResetTimer() translator := NewTranslator(s.ctx, nil, s.mockLoader, "en", "en", s.mockLog) s.mockLoader.On("Load", "en", "*").Return(map[string]any{ @@ -671,19 +665,15 @@ func Benchmark_Get(b *testing.B) { }, }, nil) - for i := 0; i < b.N; i++ { + for b.Loop() { translator.Get("test.foo") } - - b.StopTimer() } func Benchmark_Has(b *testing.B) { s := new(TranslatorTestSuite) s.SetT(&testing.T{}) s.SetupTest() - b.StartTimer() - b.ResetTimer() translator := NewTranslator(s.ctx, nil, s.mockLoader, "en", "en", s.mockLog) s.mockLoader.On("Load", "en", "*").Return(map[string]any{ @@ -692,9 +682,7 @@ func Benchmark_Has(b *testing.B) { }, }, nil) - for i := 0; i < b.N; i++ { + for b.Loop() { translator.Has("test.foo") } - - b.StopTimer() } diff --git a/validation/data_bag.go b/validation/data_bag.go index 7361e2f77..a4a1cc7f7 100644 --- a/validation/data_bag.go +++ b/validation/data_bag.go @@ -4,10 +4,11 @@ import ( "bytes" "fmt" "io" + "maps" "net/http" "net/url" "reflect" - "sort" + "slices" "strings" supportjson "github.com/goravel/framework/support/json" @@ -85,9 +86,7 @@ func NewDataBagFromRequest(r *http.Request, maxMemory int64) (*DataBag, error) { var jsonData map[string]any if err = supportjson.Unmarshal(body, &jsonData); err == nil { // Body data overrides query data - for k, v := range jsonData { - bag.data[k] = v - } + maps.Copy(bag.data, jsonData) } } case strings.HasPrefix(contentType, "multipart/form-data"): @@ -187,7 +186,7 @@ func (d *DataBag) Keys() []string { } keys := make([]string, 0) collectKeys(d.data, "", &keys) - sort.Strings(keys) + slices.Sort(keys) d.cachedKeys = keys return d.cachedKeys } diff --git a/validation/engine.go b/validation/engine.go index baa3b09f3..298d34494 100644 --- a/validation/engine.go +++ b/validation/engine.go @@ -3,8 +3,9 @@ package validation import ( "context" "fmt" + "maps" "regexp" - "sort" + "slices" "strings" contractstranslation "github.com/goravel/framework/contracts/translation" @@ -56,11 +57,7 @@ func (e *Engine) Validate() *Errors { expandedRules := e.expandWildcardRules() // Step 2: Sort keys for deterministic ordering - sortedFields := make([]string, 0, len(expandedRules)) - for field := range expandedRules { - sortedFields = append(sortedFields, field) - } - sort.Strings(sortedFields) + sortedFields := slices.Sorted(maps.Keys(expandedRules)) // Step 3: Validate each field for _, field := range sortedFields { @@ -248,11 +245,8 @@ func (e *Engine) handleExcludeRule(field string, rule ParsedRule) { } case "exclude_with": - for _, f := range rule.Parameters { - if e.data.Has(f) { - e.excludes[field] = true - break - } + if slices.ContainsFunc(rule.Parameters, e.data.Has) { + e.excludes[field] = true } case "exclude_without": diff --git a/validation/filters.go b/validation/filters.go index 948b8f2eb..6903e7e46 100644 --- a/validation/filters.go +++ b/validation/filters.go @@ -294,7 +294,7 @@ func callFilterFunc(ctx context.Context, filter validatecontract.Filter, val any // Check for error return if len(results) == 2 { - if errVal, ok := results[1].Interface().(error); ok && errVal != nil { + if errVal, ok := reflect.TypeAssert[error](results[1]); ok && errVal != nil { return val, errVal } } diff --git a/validation/lang.go b/validation/lang.go index 90bd577fb..7da809506 100644 --- a/validation/lang.go +++ b/validation/lang.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io/fs" + "maps" ) //go:embed lang @@ -37,9 +38,7 @@ func flattenMessages(prefix string, m map[string]any) map[string]string { case string: result[key] = val case map[string]any: - for fk, fv := range flattenMessages(key, val) { - result[fk] = fv - } + maps.Copy(result, flattenMessages(key, val)) } } return result diff --git a/validation/messages.go b/validation/messages.go index 353915731..f7657c129 100644 --- a/validation/messages.go +++ b/validation/messages.go @@ -1,7 +1,8 @@ package validation import ( - "sort" + "cmp" + "slices" "strings" contractstranslation "github.com/goravel/framework/contracts/translation" @@ -99,8 +100,8 @@ func formatMessage(message string, replacements map[string]string) string { for k := range replacements { keys = append(keys, k) } - sort.Slice(keys, func(i, j int) bool { - return len(keys[i]) > len(keys[j]) + slices.SortFunc(keys, func(a, b string) int { + return cmp.Compare(len(b), len(a)) }) for _, k := range keys { message = strings.ReplaceAll(message, k, replacements[k]) diff --git a/validation/rule_parser.go b/validation/rule_parser.go index 10f04891d..b709a47cd 100644 --- a/validation/rule_parser.go +++ b/validation/rule_parser.go @@ -92,23 +92,20 @@ func splitRules(ruleString string) []string { // extractRuleName extracts the rule name from a rule string (before the first ':'). func extractRuleName(rule string) string { - idx := strings.Index(rule, ":") - if idx == -1 { + before, _, ok := strings.Cut(rule, ":") + if !ok { return strings.TrimSpace(rule) } - return strings.TrimSpace(rule[:idx]) + return strings.TrimSpace(before) } // parseOneRule parses a single rule string "name:param1,param2" into a ParsedRule. func parseOneRule(raw string) ParsedRule { - idx := strings.Index(raw, ":") - if idx == -1 { + name, paramStr, ok := strings.Cut(raw, ":") + if !ok { return ParsedRule{Name: raw} } - name := raw[:idx] - paramStr := raw[idx+1:] - // For regex and not_regex, the entire parameter string is a single parameter if name == "regex" || name == "not_regex" { return ParsedRule{ diff --git a/validation/rules.go b/validation/rules.go index b33dad102..47598ce09 100644 --- a/validation/rules.go +++ b/validation/rules.go @@ -16,6 +16,7 @@ import ( "net/url" "reflect" "regexp" + "slices" "strconv" "strings" "time" @@ -358,10 +359,8 @@ func rulePresentUnless(ctx *RuleContext) bool { } func rulePresentWith(ctx *RuleContext) bool { - for _, field := range ctx.Parameters { - if ctx.Data.Has(field) { - return ctx.Data.Has(ctx.Attribute) - } + if slices.ContainsFunc(ctx.Parameters, ctx.Data.Has) { + return ctx.Data.Has(ctx.Attribute) } return true } @@ -396,10 +395,8 @@ func ruleMissingUnless(ctx *RuleContext) bool { } func ruleMissingWith(ctx *RuleContext) bool { - for _, field := range ctx.Parameters { - if ctx.Data.Has(field) { - return !ctx.Data.Has(ctx.Attribute) - } + if slices.ContainsFunc(ctx.Parameters, ctx.Data.Has) { + return !ctx.Data.Has(ctx.Attribute) } return true } @@ -1250,22 +1247,12 @@ func ruleUint(ctx *RuleContext) bool { func ruleIn(ctx *RuleContext) bool { s := fmt.Sprintf("%v", ctx.Value) - for _, allowed := range ctx.Parameters { - if s == allowed { - return true - } - } - return false + return slices.Contains(ctx.Parameters, s) } func ruleNotIn(ctx *RuleContext) bool { s := fmt.Sprintf("%v", ctx.Value) - for _, disallowed := range ctx.Parameters { - if s == disallowed { - return false - } - } - return true + return !slices.Contains(ctx.Parameters, s) } func ruleInArray(ctx *RuleContext) bool { @@ -1286,10 +1273,8 @@ func ruleInArray(ctx *RuleContext) bool { } } case []string: - for _, item := range arr { - if item == s { - return true - } + if slices.Contains(arr, s) { + return true } } return false @@ -1432,12 +1417,7 @@ func ruleImage(ctx *RuleContext) bool { return false } imageTypes := []string{"image/jpeg", "image/png", "image/gif", "image/bmp", "image/svg+xml", "image/webp"} - for _, t := range imageTypes { - if mtype.Is(t) { - return true - } - } - return false + return slices.ContainsFunc(imageTypes, mtype.Is) } if fh, ok := ctx.Value.(*multipart.FileHeader); ok { @@ -1489,12 +1469,7 @@ func ruleMimetypes(ctx *RuleContext) bool { if err != nil { return false } - for _, allowed := range ctx.Parameters { - if mtype.Is(allowed) { - return true - } - } - return false + return slices.ContainsFunc(ctx.Parameters, mtype.Is) } if fh, ok := ctx.Value.(*multipart.FileHeader); ok { diff --git a/validation/utils.go b/validation/utils.go index 62bcff6b4..551ab7709 100644 --- a/validation/utils.go +++ b/validation/utils.go @@ -2,10 +2,12 @@ package validation import ( "fmt" + "maps" "mime/multipart" "net/url" "reflect" "regexp" + "slices" "strconv" "strings" "sync" @@ -112,10 +114,8 @@ func getAttributeType(attribute string, value any, rules map[string][]ParsedRule // matchesOtherValue checks if otherValue matches any of the comparison values. func matchesOtherValue(otherValue any, comparisonValues []string) bool { otherStr := fmt.Sprintf("%v", otherValue) - for _, cv := range comparisonValues { - if otherStr == cv { - return true - } + if slices.Contains(comparisonValues, otherStr) { + return true } // Handle boolean matching if b, ok := otherValue.(bool); ok { @@ -349,7 +349,7 @@ func toAnySlice(val any) ([]any, bool) { return nil, false } out := make([]any, rv.Len()) - for i := 0; i < rv.Len(); i++ { + for i := range rv.Len() { out[i] = rv.Index(i).Interface() } return out, true @@ -478,7 +478,7 @@ func collectKeys(data any, prefix string, keys *[]string) { return } if rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array { - for i := 0; i < rv.Len(); i++ { + for i := range rv.Len() { fullKey := strconv.Itoa(i) if prefix != "" { fullKey = prefix + "." + strconv.Itoa(i) @@ -547,7 +547,7 @@ func structToMap(rv reflect.Value) map[string]any { result := make(map[string]any) rt := rv.Type() - for i := 0; i < rt.NumField(); i++ { + for i := range rt.NumField() { field := rt.Field(i) if !field.IsExported() { continue @@ -575,9 +575,7 @@ func structToMap(rv reflect.Value) map[string]any { val := rv.Field(i) if field.Anonymous && val.Kind() == reflect.Struct { embedded := structToMap(val) - for k, v := range embedded { - result[k] = v - } + maps.Copy(result, embedded) continue } @@ -602,7 +600,7 @@ func normalizeValue(rv reflect.Value) any { return structToMap(rv) case reflect.Slice, reflect.Array: result := make([]any, rv.Len()) - for i := 0; i < rv.Len(); i++ { + for i := range rv.Len() { result[i] = normalizeValue(rv.Index(i)) } return result diff --git a/validation/validator.go b/validation/validator.go index 0a110d1d1..1a701273e 100644 --- a/validation/validator.go +++ b/validation/validator.go @@ -1,6 +1,7 @@ package validation import ( + "maps" "reflect" "time" @@ -33,9 +34,7 @@ func (v *Validator) Bind(ptr any) error { // Merge validated data with original unvalidated data. // Original data provides fallback values for fields not in rules. data := make(map[string]any) - for key, value := range v.data.All() { - data[key] = value - } + maps.Copy(data, v.data.All()) // Validated data overrides deepMerge(data, v.validated) @@ -118,57 +117,57 @@ func (v *Validator) castValue() mapstructure.DecodeHookFunc { } case reflect.Struct: switch to.Type() { - case reflect.TypeOf(carbon.Carbon{}): + case reflect.TypeFor[carbon.Carbon](): castedValue = castCarbon(from, nil) - case reflect.TypeOf(carbon.DateTime{}): + case reflect.TypeFor[carbon.DateTime](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewDateTime(c) }) - case reflect.TypeOf(carbon.DateTimeMilli{}): + case reflect.TypeFor[carbon.DateTimeMilli](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewDateTimeMilli(c) }) - case reflect.TypeOf(carbon.DateTimeMicro{}): + case reflect.TypeFor[carbon.DateTimeMicro](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewDateTimeMicro(c) }) - case reflect.TypeOf(carbon.DateTimeNano{}): + case reflect.TypeFor[carbon.DateTimeNano](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewDateTimeNano(c) }) - case reflect.TypeOf(carbon.Date{}): + case reflect.TypeFor[carbon.Date](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewDate(c) }) - case reflect.TypeOf(carbon.DateMilli{}): + case reflect.TypeFor[carbon.DateMilli](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewDateMilli(c) }) - case reflect.TypeOf(carbon.DateMicro{}): + case reflect.TypeFor[carbon.DateMicro](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewDateMicro(c) }) - case reflect.TypeOf(carbon.DateNano{}): + case reflect.TypeFor[carbon.DateNano](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewDateNano(c) }) - case reflect.TypeOf(carbon.Timestamp{}): + case reflect.TypeFor[carbon.Timestamp](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewTimestamp(c) }) - case reflect.TypeOf(carbon.TimestampMilli{}): + case reflect.TypeFor[carbon.TimestampMilli](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewTimestampMilli(c) }) - case reflect.TypeOf(carbon.TimestampMicro{}): + case reflect.TypeFor[carbon.TimestampMicro](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewTimestampMicro(c) }) - case reflect.TypeOf(carbon.TimestampNano{}): + case reflect.TypeFor[carbon.TimestampNano](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return carbon.NewTimestampNano(c) }) - case reflect.TypeOf(time.Time{}): + case reflect.TypeFor[time.Time](): castedValue = castCarbon(from, func(c *carbon.Carbon) any { return c.StdTime() }) diff --git a/validation/validator_test.go b/validation/validator_test.go index da16c399f..840219db6 100644 --- a/validation/validator_test.go +++ b/validation/validator_test.go @@ -18,7 +18,6 @@ import ( "github.com/goravel/framework/errors" "github.com/goravel/framework/foundation/json" "github.com/goravel/framework/support/carbon" - "github.com/goravel/framework/support/convert" ) func TestBind_Rule(t *testing.T) { @@ -756,7 +755,7 @@ func TestCastValue(t *testing.T) { PointerTimestampMilli: carbon.NewTimestampMilli(carbon.FromTimestampMilli(timestampMilli)), PointerTimestampMicro: carbon.NewTimestampMicro(carbon.FromTimestampMicro(timestampMicro)), PointerTimestampNano: carbon.NewTimestampNano(carbon.FromTimestampNano(timestampNano)), - PointerTime: convert.Pointer(carbon.NewDateTime(carbon.Parse(dateTime)).StdTime()), + PointerTime: new(carbon.NewDateTime(carbon.Parse(dateTime)).StdTime()), Carbon: *carbon.Parse(dateTime), DateTime: *carbon.NewDateTime(carbon.Parse(dateTime)), DateTimeMilli: *carbon.NewDateTimeMilli(carbon.Parse(dateTime)), @@ -814,7 +813,7 @@ func TestCastValue(t *testing.T) { PointerTimestampMilli: carbon.NewTimestampMilli(carbon.Parse(dateTime)), PointerTimestampMicro: carbon.NewTimestampMicro(carbon.Parse(dateTime)), PointerTimestampNano: carbon.NewTimestampNano(carbon.Parse(dateTime)), - PointerTime: convert.Pointer(carbon.NewDateTime(carbon.Parse(dateTime)).StdTime()), + PointerTime: new(carbon.NewDateTime(carbon.Parse(dateTime)).StdTime()), Carbon: *carbon.Parse(dateTime), DateTime: *carbon.NewDateTime(carbon.Parse(dateTime)), DateTimeMilli: *carbon.NewDateTimeMilli(carbon.Parse(dateTime)),