package services import ( "database/sql" "errors" "fmt" "hash/fnv" "sync" models "vibeStonk/server/models/v1" "vibeStonk/server/repository" ) var ( ErrExistingStock = errors.New("stock with symbol exists") ) func NewStockService(config *repository.Config) (StockService, error) { db, err := repository.GetStockConnector(config) if err != nil { return nil, fmt.Errorf("failed to initialize stock service: %w", err) } stockRepo, err := repository.NewStockRepo(config, db) if err != nil { return nil, fmt.Errorf("failed to initialize stockRepo: %w", err) } return &stockService{db: db, lock: &sync.Mutex{}, stockRepo: stockRepo}, nil } type StockService interface { NewStock(symbol, name string) (*models.Stock, error) // Update(*models.Stock)(*models.Stock, error) Get(id int) (*models.Stock, error) GetBySymbol(symbol string) (*models.Stock, error) GetAll() ([]*models.Stock, error) GetByIDs(ids []int) ([]*models.Stock, error) Close() error } type stockService struct { db *sql.DB lock *sync.Mutex stockRepo repository.StockRepo } func (s *stockService) Get(id int) (*models.Stock, error) { return s.stockRepo.Get(int64(id)) } func (s *stockService) GetBySymbol(symbol string) (*models.Stock, error) { return s.stockRepo.GetBySymbol(symbol) } func (s *stockService) NewStock(symbol, name string) (*models.Stock, error) { s.lock.Lock() defer s.lock.Unlock() stock := &models.Stock{ Id: -1, Symbol: symbol, Name: name, Color: createColor(symbol, name), } stonk, err := s.stockRepo.GetBySymbol(stock.Symbol) if err == nil || stonk != nil { return nil, ErrExistingStock } stock, err = s.stockRepo.Create(stock) if err != nil { return nil, fmt.Errorf("failed to create new stock: %w", err) } return stock, nil } func (s *stockService) GetAll() ([]*models.Stock, error) { stocks, err := s.stockRepo.List() if err != nil { return nil, fmt.Errorf("failed to get all stocks: %w", err) } return stocks, nil } func (s *stockService) GetByIDs(ids []int) ([]*models.Stock, error) { stocks, err := s.stockRepo.GetByIDs(ids) if err != nil { return nil, fmt.Errorf("failed to filter stocks by IDs: %w", err) } return stocks, nil } func (s *stockService) Close() error { return s.db.Close() } // helpers func createColor(symbol, name string) string { // Generate a deterministic color based on symbol+name formatted as #RRGGBB hex h := fnv.New32a() h.Write([]byte(symbol + name)) hash := h.Sum32() // Use the hash to generate RGB values r := (hash & 0xFF0000) >> 16 g := (hash & 0x00FF00) >> 8 b := hash & 0x0000FF // Format as #RRGGBB hex return fmt.Sprintf("#%02X%02X%02X", r, g, b) }