package store import ( "encoding/json" "fmt" "os" "sync" ) // User represents a user in the system type User struct { Email string `json:"email"` GivenName string `json:"given_name"` FamilyName string `json:"family_name"` UserID string `json:"user_id"` Picture string `json:"picture,omitempty"` } // UserStore provides thread-safe user storage type UserStore struct { mu sync.RWMutex users map[string]*User } // NewUserStore creates a new user store func NewUserStore() *UserStore { return &UserStore{ users: make(map[string]*User), } } // LoadFromFile loads users from a JSON file func (s *UserStore) LoadFromFile(path string) error { data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil // File doesn't exist, start with empty store } return fmt.Errorf("read users file: %w", err) } var rawUsers map[string]json.RawMessage if err := json.Unmarshal(data, &rawUsers); err != nil { return fmt.Errorf("parse users file: %w", err) } s.mu.Lock() defer s.mu.Unlock() for email, raw := range rawUsers { var user User if err := json.Unmarshal(raw, &user); err != nil { return fmt.Errorf("parse user %s: %w", email, err) } user.Email = email // Ensure email is set if user.UserID == "" { user.UserID = email } s.users[email] = &user } return nil } // GetByEmail retrieves a user by email func (s *UserStore) GetByEmail(email string) (*User, bool) { s.mu.RLock() defer s.mu.RUnlock() user, ok := s.users[email] if !ok { return nil, false } // Return a copy to prevent external modification userCopy := *user return &userCopy, true } // Create adds a new user func (s *UserStore) Create(email string, user *User) { s.mu.Lock() defer s.mu.Unlock() user.Email = email if user.UserID == "" { user.UserID = email } s.users[email] = user } // Update modifies an existing user func (s *UserStore) Update(email string, updates *User) (*User, bool) { s.mu.Lock() defer s.mu.Unlock() existing, ok := s.users[email] if !ok { return nil, false } // Apply updates (only non-empty fields) if updates.GivenName != "" { existing.GivenName = updates.GivenName } if updates.FamilyName != "" { existing.FamilyName = updates.FamilyName } if updates.Picture != "" { existing.Picture = updates.Picture } // Return a copy userCopy := *existing return &userCopy, true } // List returns all users func (s *UserStore) List() []*User { s.mu.RLock() defer s.mu.RUnlock() users := make([]*User, 0, len(s.users)) for _, user := range s.users { userCopy := *user users = append(users, &userCopy) } return users }