package badger import ( "context" "encoding/binary" "errors" "fmt" "git.loyso.art/frx/eway/internal/encoding/fbs" "git.loyso.art/frx/eway/internal/entity" badger "github.com/dgraph-io/badger/v4" ) type categoryClient struct { db *badger.DB seqGen *badger.Sequence } func newCategoryClient(db *badger.DB, seqGen *badger.Sequence) categoryClient { return categoryClient{ db: db, seqGen: seqGen, } } func (categoryClient) prefix() []byte { return []byte("!!category!!") } func (c categoryClient) prefixed(key []byte) []byte { return append(c.prefix(), key...) } func (c categoryClient) prefixedInt(key int64) []byte { var keyBytes [8]byte binary.BigEndian.PutUint64(keyBytes[:], uint64(key)) return c.prefixed(keyBytes[:]) } func (c categoryClient) List( ctx context.Context, ) (out []entity.Category, err error) { err = c.db.View(func(txn *badger.Txn) error { opts := badger.DefaultIteratorOptions opts.PrefetchSize = 10 opts.PrefetchValues = true iter := txn.NewIterator(opts) defer iter.Close() prefix := c.prefix() for iter.Seek(prefix); iter.ValidForPrefix(prefix); iter.Next() { current := iter.Item() err = current.Value(func(val []byte) error { category := fbs.ParseCategory(val) out = append(out, category) return nil }) if err != nil { return fmt.Errorf("getting value: %w", err) } } return nil }) if err != nil { return nil, fmt.Errorf("viewing: %w", err) } return out, nil } func (c categoryClient) Get(ctx context.Context, id int64) (out entity.Category, err error) { err = c.db.View(func(txn *badger.Txn) error { key := c.prefixedInt(id) item, err := txn.Get(key) if err != nil { return fmt.Errorf("getting key: %w", err) } err = item.Value(func(val []byte) error { out = fbs.ParseCategory(val) return nil }) if err != nil { return fmt.Errorf("reading value: %w", err) } return nil }) if err != nil { if errors.Is(err, badger.ErrKeyNotFound) { return out, entity.ErrNotFound } return out, fmt.Errorf("viewing: %w", err) } return out, nil } // Create new category inside DB. It also applies new id to it. func (c categoryClient) Create(ctx context.Context, name string) (out entity.Category, err error) { nextid, err := c.seqGen.Next() if err != nil { return out, fmt.Errorf("getting next id: %w", err) } out = entity.Category{ // Because first value from sequence generator is 0 ID: int64(nextid + 1), Name: name, } err = c.db.Update(func(txn *badger.Txn) error { key := c.prefixedInt(out.ID) err = txn.Set(key, fbs.MakeCategoryFinished(out)) if err != nil { return fmt.Errorf("setting: %w", err) } return nil }) if err != nil { return out, fmt.Errorf("updating: %w", err) } return out, nil }