1
0
Fork 0
test/internal/store/in_memory_store.go

148 lines
4.9 KiB
Go
Raw Normal View History

2019-10-03 14:51:00 +00:00
package store
import (
"sort"
"sync"
"gitlab.com/toby3d/test/internal/model"
"golang.org/x/xerrors"
)
type (
// InMemoryProductStore представляет собой объект хранилища продуктов доступный только для чтения
InMemoryProductStore struct {
mutex sync.RWMutex
Products []*model.Product
}
// InMemoryCartStore представляет собой простой менеджер объектов корзины.
InMemoryCartStore struct {
mutex sync.RWMutex
items []*model.Item
}
)
var (
ErrNoProductId = xerrors.New("product_id not provided")
ErrZeroQuanity = xerrors.New("item quanity is zero")
ErrNotExist = xerrors.New("item not exists or already removed")
)
// NewInMemoryProductStore создаёт новый менеджер продуктов
func NewInMemoryProductStore() *InMemoryProductStore {
return &InMemoryProductStore{mutex: sync.RWMutex{}}
}
// GetById возвращает информацию о продукте по его ID, если он существует
func (imps *InMemoryProductStore) GetById(id uint64) *model.Product {
for _, product := range imps.Products {
if product.GetId() != id {
continue
}
return product
}
return nil
}
// NewInMemoryCartStore создаёт новый менеджер объектов корзины
func NewInMemoryCartStore() *InMemoryCartStore { return &InMemoryCartStore{mutex: sync.RWMutex{}} }
// Add добавляет новый объект в корзину.
//
// * Если объекта не существует, то он будет добавлен в список.
// * Если объект уже существует в корзине, то его количество в корзине будет увеличено.
// * Ошибка будет возвращена если не был указан ID продукта или его количество равно или меньше ноля.
//
// BUG(toby3d): InMemoryStore не проверяет наличие продукта по его ID, подразумевая, что он всегда существует в базе.
func (ims *InMemoryCartStore) Add(i *model.Item) error {
switch {
case i == nil, i.GetProductId() <= 0:
return ErrNoProductId
case i.GetQuanity() <= 0:
return ErrZeroQuanity
}
if item := ims.GetById(i.GetProductId()); item != nil {
ims.mutex.Lock()
item.Quanity += i.GetQuanity()
ims.mutex.Unlock()
return nil
}
ims.mutex.Lock()
ims.items = append(ims.items, &model.Item{
ProductId: i.GetProductId(),
Quanity: i.GetQuanity(),
})
sort.Slice(ims.items, func(i, j int) bool {
return ims.items[i].GetProductId() < ims.items[j].GetProductId()
})
ims.mutex.Unlock()
return nil
}
// GetById возвращает объект корзины по ID его продукта (если существует).
func (ims *InMemoryCartStore) GetById(id uint64) *model.Item {
if id == 0 {
return nil
}
ims.mutex.RLock()
defer ims.mutex.RUnlock()
for _, item := range ims.items {
if item.GetProductId() != id {
continue
}
return item
}
return nil
}
// GetList возвращает массив всех доступных в корзине объектов.
func (ims *InMemoryCartStore) GetList() (int, []*model.Item) {
ims.mutex.RLock()
defer ims.mutex.RUnlock()
return len(ims.items), ims.items
}
// Update обновляет конкретный объект в корзине.
//
// * Если объекта с указанным ProductId ещё не существует в корзине, то он будет добавлен.
// * Если объект уже существует в корзине, то его количество будет перезаписано.
// * Если желаемое количество объекта равна нулю или отрицательно, то объект будет удалён из корзины.
func (ims *InMemoryCartStore) Update(i *model.Item) error {
if item := ims.GetById(i.GetProductId()); item != nil {
if i.GetQuanity() <= 0 {
return ims.Delete(i.GetProductId())
}
ims.mutex.Lock()
item.Quanity = i.GetQuanity()
ims.mutex.Unlock()
return nil
}
return ims.Add(i)
}
// Delete удаляет объект из корзины по его ProductId (если он существует).
func (ims *InMemoryCartStore) Delete(id uint64) error {
if item := ims.GetById(id); item == nil {
return ErrNotExist
}
ims.mutex.Lock()
defer ims.mutex.Unlock()
for i := range ims.items {
if ims.items[i].GetProductId() != id {
continue
}
// NOTE(toby3d): см. https://github.com/golang/go/wiki/SliceTricks
ims.items[i] = ims.items[len(ims.items)-1]
ims.items[len(ims.items)-1] = nil
ims.items = ims.items[:len(ims.items)-1]
break
}
sort.Slice(ims.items, func(i, j int) bool {
return ims.items[i].GetProductId() < ims.items[j].GetProductId()
})
return nil
}