Files
go-omni/cmd/go-omni/scaffold/templates/internal-repository-memory.go.tmpl

127 lines
3.1 KiB
Cheetah

package repository
import (
"context"
"sort"
"sync"
"time"
"{{.Module}}/internal/model"
)
// MemoryUsers stores data only for this process. Replace it with a database
// implementation of UserRepository when persistence is required.
type MemoryUsers struct {
mu sync.RWMutex
users map[int64]model.User
emails map[string]int64
nextID int64
}
func NewMemoryUsers() *MemoryUsers {
return &MemoryUsers{
users: make(map[int64]model.User),
emails: make(map[string]int64),
nextID: 1,
}
}
func (r *MemoryUsers) Create(ctx context.Context, input model.UserInput) (model.User, error) {
r.mu.Lock()
defer r.mu.Unlock()
if err := ctx.Err(); err != nil {
return model.User{}, err
}
if _, exists := r.emails[input.Email]; exists {
return model.User{}, model.ErrEmailTaken
}
now := time.Now().UTC()
user := model.User{
ID: r.nextID, Name: input.Name, Email: input.Email,
CreatedAt: now, UpdatedAt: now,
}
r.nextID++
r.users[user.ID] = user
r.emails[user.Email] = user.ID
return user, nil
}
func (r *MemoryUsers) Get(ctx context.Context, id int64) (model.User, error) {
r.mu.RLock()
defer r.mu.RUnlock()
if err := ctx.Err(); err != nil {
return model.User{}, err
}
user, exists := r.users[id]
if !exists {
return model.User{}, model.ErrUserNotFound
}
return user, nil
}
func (r *MemoryUsers) List(ctx context.Context, page, pageSize int) (model.UserPage, error) {
if page < 1 || pageSize < 1 || pageSize > 100 {
return model.UserPage{}, model.ErrInvalidPage
}
r.mu.RLock()
if err := ctx.Err(); err != nil {
r.mu.RUnlock()
return model.UserPage{}, err
}
users := make([]model.User, 0, len(r.users))
for _, user := range r.users {
users = append(users, user)
}
r.mu.RUnlock()
// A stable order is essential: Go map iteration order changes between calls.
sort.Slice(users, func(i, j int) bool { return users[i].ID < users[j].ID })
result := model.UserPage{
Items: []model.User{}, Total: len(users), Page: page, PageSize: pageSize,
}
// Check the page before multiplying so even a very large page cannot overflow.
if len(users) == 0 || page-1 > (len(users)-1)/pageSize {
return result, nil
}
start := (page - 1) * pageSize
count := min(pageSize, len(users)-start)
result.Items = users[start : start+count]
return result, nil
}
func (r *MemoryUsers) Update(ctx context.Context, id int64, input model.UserInput) (model.User, error) {
r.mu.Lock()
defer r.mu.Unlock()
if err := ctx.Err(); err != nil {
return model.User{}, err
}
user, exists := r.users[id]
if !exists {
return model.User{}, model.ErrUserNotFound
}
if owner, exists := r.emails[input.Email]; exists && owner != id {
return model.User{}, model.ErrEmailTaken
}
delete(r.emails, user.Email)
user.Name, user.Email = input.Name, input.Email
user.UpdatedAt = time.Now().UTC()
r.users[id] = user
r.emails[user.Email] = id
return user, nil
}
func (r *MemoryUsers) Delete(ctx context.Context, id int64) error {
r.mu.Lock()
defer r.mu.Unlock()
if err := ctx.Err(); err != nil {
return err
}
user, exists := r.users[id]
if !exists {
return model.ErrUserNotFound
}
delete(r.users, id)
delete(r.emails, user.Email)
return nil
}