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
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							197 lines
						
					
					
						
							3.8 KiB
						
					
					
				| 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 | |
| }
 |