package bolt import ( "context" "github.com/gisle/stufflog/database/repositories" "github.com/gisle/stufflog/models" "github.com/gisle/stufflog/slerrors" "github.com/vmihailenco/msgpack/v4" "go.etcd.io/bbolt" ) var bnItems = []byte("Item") type itemRepository struct { db *bbolt.DB userIdIdx *index } func (r *itemRepository) FindID(ctx context.Context, id string) (*models.Item, error) { item := new(models.Item) err := r.db.View(func(tx *bbolt.Tx) error { value := tx.Bucket(bnItems).Get(unsafeStringToBytes(id)) if value == nil { return slerrors.NotFound("Period") } err := msgpack.Unmarshal(value, item) if err != nil { return err } return nil }) if err != nil { return nil, err } return item, nil } func (r *itemRepository) List(ctx context.Context) ([]*models.Item, error) { items := make([]*models.Item, 0, 16) err := r.db.View(func(tx *bbolt.Tx) error { cursor := tx.Bucket(bnItems).Cursor() for key, value := cursor.First(); key != nil; key, value = cursor.Next() { item := new(models.Item) err := msgpack.Unmarshal(value, item) if err != nil { return err } items = append(items, item) } return nil }) if err != nil { return nil, err } return items, nil } func (r *itemRepository) ListUser(ctx context.Context, user models.User) ([]*models.Item, error) { items := make([]*models.Item, 0, 16) err := r.db.View(func(tx *bbolt.Tx) error { bucket := tx.Bucket(bnItems) ids, err := r.userIdIdx.WithTx(tx).Get(user.ID) if err != nil { return err } for _, id := range ids { value := bucket.Get(id) if value == nil { continue } item := new(models.Item) err := msgpack.Unmarshal(value, item) if err != nil { return err } items = append(items, item) } return nil }) if err != nil { return nil, err } return items, nil } func (r *itemRepository) Insert(ctx context.Context, item models.Item) error { value, err := msgpack.Marshal(&item) if err != nil { return err } return r.db.Update(func(tx *bbolt.Tx) error { err := tx.Bucket(bnItems).Put(unsafeStringToBytes(item.ID), value) if err != nil { return err } err = r.index(tx, &item) if err != nil { return err } return nil }) } func (r *itemRepository) Update(ctx context.Context, item models.Item, updates []*models.ItemUpdate) (*models.Item, error) { for _, update := range updates { err := item.ApplyUpdate(*update) if err != nil { return nil, err } } value, err := msgpack.Marshal(&item) if err != nil { return nil, err } err = r.db.Update(func(tx *bbolt.Tx) error { return tx.Bucket(bnItems).Put(unsafeStringToBytes(item.ID), value) }) if err != nil { return nil, err } return &item, nil } func (r *itemRepository) Remove(ctx context.Context, item models.Item) error { return r.db.Update(func(tx *bbolt.Tx) error { err := tx.Bucket(bnItems).Delete(unsafeStringToBytes(item.ID)) if err != nil { return err } err = r.unIndex(tx, &item) if err != nil { return err } return nil }) } func (r *itemRepository) index(tx *bbolt.Tx, item *models.Item) error { idBytes := unsafeStringToBytes(item.ID) err := r.userIdIdx.WithTx(tx).Set(idBytes, item.UserID) if err != nil { return err } return nil } func (r *itemRepository) unIndex(tx *bbolt.Tx, item *models.Item) error { idBytes := unsafeStringToBytes(item.ID) err := r.userIdIdx.WithTx(tx).Set(idBytes) if err != nil { return err } return nil } func newItemRepository(db *bbolt.DB) (repositories.ItemRepository, error) { err := db.Update(func(tx *bbolt.Tx) error { _, err := tx.CreateBucketIfNotExists(bnItems) return err }) if err != nil { return nil, err } userIdIdx, err := newModelIndex(db, "Item", "UserID") if err != nil { return nil, err } return &itemRepository{ db: db, userIdIdx: userIdIdx, }, nil }