Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.cursor
.DS_Store
.vscode
.vscode
.windsurf
38 changes: 38 additions & 0 deletions syncmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cachex

import (
"context"
"sync"

"github.com/pkg/errors"
)

// SyncMap is a cache implementation using sync.Map
type SyncMap[T any] struct {
sync.Map
Comment on lines +11 to +12
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Embedding sync.Map exposes its untyped methods (Store, Load, Delete, Range, etc.) which bypass the generic type safety provided by the Cache interface. This creates a confusing API where users can accidentally store values of the wrong type using cache.Store() directly, leading to runtime panics in Get(). Consider using composition instead of embedding (e.g., having a private field 'data sync.Map') to maintain type safety and provide a cleaner API surface.

Copilot uses AI. Check for mistakes.
}

var _ Cache[any] = &SyncMap[any]{}

func NewSyncMap[T any]() *SyncMap[T] {
return &SyncMap[T]{}
}

func (s *SyncMap[T]) Set(_ context.Context, key string, value T) error {
s.Store(key, value)
return nil
}

func (s *SyncMap[T]) Get(_ context.Context, key string) (T, error) {
var zero T
v, ok := s.Load(key)
if !ok {
return zero, errors.Wrapf(&ErrKeyNotFound{}, "key not found in syncmap for key: %s", key)
}
return v.(T), nil
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unchecked type assertion can panic if a value of the wrong type is stored. This is particularly dangerous because the embedded sync.Map allows direct access to Store/Load/Range methods, which bypass the generic type safety. For example, if someone calls cache.Store("key", wrongTypeValue) directly and then calls cache.Get(ctx, "key"), the type assertion will panic. Consider either: (1) using a safe type assertion with ok check and returning an error if types don't match, or (2) not embedding sync.Map to prevent direct access to its methods.

Copilot uses AI. Check for mistakes.
}

func (s *SyncMap[T]) Del(_ context.Context, key string) error {
s.Delete(key)
return nil
}
43 changes: 43 additions & 0 deletions syncmap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cachex

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestSyncMapBasics(t *testing.T) {
ctx := context.Background()
cache := NewSyncMap[string]()

require.NoError(t, cache.Set(ctx, "key1", "value1"))

value, err := cache.Get(ctx, "key1")
require.NoError(t, err)
assert.Equal(t, "value1", value)

require.NoError(t, cache.Del(ctx, "key1"))

_, err = cache.Get(ctx, "key1")
assert.True(t, IsErrKeyNotFound(err))
}

func TestSyncMapEmbeddedMethods(t *testing.T) {
cache := NewSyncMap[int]()

cache.Store("a", 1)
cache.Store("b", 2)

v, ok := cache.Load("a")
require.True(t, ok)
assert.Equal(t, 1, v)

count := 0
cache.Range(func(k, v any) bool {
count++
return true
})
assert.Equal(t, 2, count)
}
Comment on lines +27 to +43
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test demonstrates accessing embedded sync.Map methods directly, which bypasses type safety and can lead to runtime panics. While this test verifies that the embedding works, it doesn't test the critical failure scenario: what happens when someone stores a value of the wrong type (e.g., cache.Store("a", "wrongtype")) and then calls the typed Get method. This scenario would cause a panic that should be either tested or prevented through better API design.

Copilot uses AI. Check for mistakes.
Loading