Plan stuff. Log stuff.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

197 lines
3.8 KiB

  1. package bolt
  2. import (
  3. "context"
  4. "github.com/gisle/stufflog/database/repositories"
  5. "github.com/gisle/stufflog/models"
  6. "github.com/gisle/stufflog/slerrors"
  7. "github.com/vmihailenco/msgpack/v4"
  8. "go.etcd.io/bbolt"
  9. )
  10. var bnItems = []byte("Item")
  11. type itemRepository struct {
  12. db *bbolt.DB
  13. userIdIdx *index
  14. }
  15. func (r *itemRepository) FindID(ctx context.Context, id string) (*models.Item, error) {
  16. item := new(models.Item)
  17. err := r.db.View(func(tx *bbolt.Tx) error {
  18. value := tx.Bucket(bnItems).Get(unsafeStringToBytes(id))
  19. if value == nil {
  20. return slerrors.NotFound("Period")
  21. }
  22. err := msgpack.Unmarshal(value, item)
  23. if err != nil {
  24. return err
  25. }
  26. return nil
  27. })
  28. if err != nil {
  29. return nil, err
  30. }
  31. return item, nil
  32. }
  33. func (r *itemRepository) List(ctx context.Context) ([]*models.Item, error) {
  34. items := make([]*models.Item, 0, 16)
  35. err := r.db.View(func(tx *bbolt.Tx) error {
  36. cursor := tx.Bucket(bnItems).Cursor()
  37. for key, value := cursor.First(); key != nil; key, value = cursor.Next() {
  38. item := new(models.Item)
  39. err := msgpack.Unmarshal(value, item)
  40. if err != nil {
  41. return err
  42. }
  43. items = append(items, item)
  44. }
  45. return nil
  46. })
  47. if err != nil {
  48. return nil, err
  49. }
  50. return items, nil
  51. }
  52. func (r *itemRepository) ListUser(ctx context.Context, user models.User) ([]*models.Item, error) {
  53. items := make([]*models.Item, 0, 16)
  54. err := r.db.View(func(tx *bbolt.Tx) error {
  55. bucket := tx.Bucket(bnItems)
  56. ids, err := r.userIdIdx.WithTx(tx).Get(user.ID)
  57. if err != nil {
  58. return err
  59. }
  60. for _, id := range ids {
  61. value := bucket.Get(id)
  62. if value == nil {
  63. continue
  64. }
  65. item := new(models.Item)
  66. err := msgpack.Unmarshal(value, item)
  67. if err != nil {
  68. return err
  69. }
  70. items = append(items, item)
  71. }
  72. return nil
  73. })
  74. if err != nil {
  75. return nil, err
  76. }
  77. return items, nil
  78. }
  79. func (r *itemRepository) Insert(ctx context.Context, item models.Item) error {
  80. value, err := msgpack.Marshal(&item)
  81. if err != nil {
  82. return err
  83. }
  84. return r.db.Update(func(tx *bbolt.Tx) error {
  85. err := tx.Bucket(bnItems).Put(unsafeStringToBytes(item.ID), value)
  86. if err != nil {
  87. return err
  88. }
  89. err = r.index(tx, &item)
  90. if err != nil {
  91. return err
  92. }
  93. return nil
  94. })
  95. }
  96. func (r *itemRepository) Update(ctx context.Context, item models.Item, updates []*models.ItemUpdate) (*models.Item, error) {
  97. for _, update := range updates {
  98. err := item.ApplyUpdate(*update)
  99. if err != nil {
  100. return nil, err
  101. }
  102. }
  103. value, err := msgpack.Marshal(&item)
  104. if err != nil {
  105. return nil, err
  106. }
  107. err = r.db.Update(func(tx *bbolt.Tx) error {
  108. return tx.Bucket(bnItems).Put(unsafeStringToBytes(item.ID), value)
  109. })
  110. if err != nil {
  111. return nil, err
  112. }
  113. return &item, nil
  114. }
  115. func (r *itemRepository) Remove(ctx context.Context, item models.Item) error {
  116. return r.db.Update(func(tx *bbolt.Tx) error {
  117. err := tx.Bucket(bnItems).Delete(unsafeStringToBytes(item.ID))
  118. if err != nil {
  119. return err
  120. }
  121. err = r.unIndex(tx, &item)
  122. if err != nil {
  123. return err
  124. }
  125. return nil
  126. })
  127. }
  128. func (r *itemRepository) index(tx *bbolt.Tx, item *models.Item) error {
  129. idBytes := unsafeStringToBytes(item.ID)
  130. err := r.userIdIdx.WithTx(tx).Set(idBytes, item.UserID)
  131. if err != nil {
  132. return err
  133. }
  134. return nil
  135. }
  136. func (r *itemRepository) unIndex(tx *bbolt.Tx, item *models.Item) error {
  137. idBytes := unsafeStringToBytes(item.ID)
  138. err := r.userIdIdx.WithTx(tx).Set(idBytes)
  139. if err != nil {
  140. return err
  141. }
  142. return nil
  143. }
  144. func newItemRepository(db *bbolt.DB) (repositories.ItemRepository, error) {
  145. err := db.Update(func(tx *bbolt.Tx) error {
  146. _, err := tx.CreateBucketIfNotExists(bnItems)
  147. return err
  148. })
  149. if err != nil {
  150. return nil, err
  151. }
  152. userIdIdx, err := newModelIndex(db, "Item", "UserID")
  153. if err != nil {
  154. return nil, err
  155. }
  156. return &itemRepository{
  157. db: db,
  158. userIdIdx: userIdIdx,
  159. }, nil
  160. }