|
|
- // Copyright 2020 Andrew Thornton. All rights reserved.
- // Use of this source code is governed by a MIT-style
- // license that can be found in the LICENSE file.
-
- package levelqueue
-
- import (
- "fmt"
-
- "github.com/syndtr/goleveldb/leveldb"
- )
-
- const (
- uniqueQueuePrefixStr = "unique"
- )
-
- // UniqueQueue defines an unique queue struct
- type UniqueQueue struct {
- q *Queue
- set *Set
- db *leveldb.DB
- closeUnderlyingDB bool
- }
-
- // OpenUnique opens an unique queue from the db path or creates a set if it doesn't exist.
- // The keys in the queue portion will not be prefixed, and the set keys will be prefixed with "set-"
- func OpenUnique(dataDir string) (*UniqueQueue, error) {
- db, err := leveldb.OpenFile(dataDir, nil)
- if err != nil {
- return nil, err
- }
- return NewUniqueQueue(db, []byte{}, []byte(uniqueQueuePrefixStr), true)
- }
-
- // NewUniqueQueue creates a new unique queue from a db.
- // The queue keys will be prefixed with queuePrefix and the set keys with setPrefix
- // and at close the db will be closed as per closeUnderlyingDB
- func NewUniqueQueue(db *leveldb.DB, queuePrefix []byte, setPrefix []byte, closeUnderlyingDB bool) (*UniqueQueue, error) {
- internal, err := NewQueue(db, queuePrefix, false)
- if err != nil {
- return nil, err
- }
- set, err := NewSet(db, setPrefix, false)
- if err != nil {
- return nil, err
- }
- queue := &UniqueQueue{
- q: internal,
- set: set,
- db: db,
- closeUnderlyingDB: closeUnderlyingDB,
- }
-
- return queue, err
- }
-
- // LPush pushes data to the left of the queue
- func (queue *UniqueQueue) LPush(data []byte) error {
- return queue.LPushFunc(data, nil)
- }
-
- // LPushFunc pushes data to the left of the queue and calls the callback if it is added
- func (queue *UniqueQueue) LPushFunc(data []byte, fn func() error) error {
- added, err := queue.set.Add(data)
- if err != nil {
- return err
- }
- if !added {
- return ErrAlreadyInQueue
- }
-
- if fn != nil {
- err = fn()
- if err != nil {
- _, remErr := queue.set.Remove(data)
- if remErr != nil {
- return fmt.Errorf("%v & %v", err, remErr)
- }
- return err
- }
- }
-
- return queue.q.LPush(data)
- }
-
- // RPush pushes data to the right of the queue
- func (queue *UniqueQueue) RPush(data []byte) error {
- return queue.RPushFunc(data, nil)
- }
-
- // RPushFunc pushes data to the right of the queue and calls the callback if is added
- func (queue *UniqueQueue) RPushFunc(data []byte, fn func() error) error {
- added, err := queue.set.Add(data)
- if err != nil {
- return err
- }
- if !added {
- return ErrAlreadyInQueue
- }
-
- if fn != nil {
- err = fn()
- if err != nil {
- _, remErr := queue.set.Remove(data)
- if remErr != nil {
- return fmt.Errorf("%v & %v", err, remErr)
- }
- return err
- }
- }
-
- return queue.q.RPush(data)
- }
-
- // RPop pop data from the right of the queue
- func (queue *UniqueQueue) RPop() ([]byte, error) {
- popped, err := queue.q.RPop()
- if err != nil {
- return popped, err
- }
- _, err = queue.set.Remove(popped)
-
- return popped, err
- }
-
- // RHandle receives a user callback function to handle the right element of the queue, if the function returns nil, then delete the element, otherwise keep the element.
- func (queue *UniqueQueue) RHandle(h func([]byte) error) error {
- return queue.q.RHandle(func(data []byte) error {
- err := h(data)
- if err != nil {
- return err
- }
- _, err = queue.set.Remove(data)
- return err
- })
- }
-
- // LPop pops data from left of the queue
- func (queue *UniqueQueue) LPop() ([]byte, error) {
- popped, err := queue.q.LPop()
- if err != nil {
- return popped, err
- }
- _, err = queue.set.Remove(popped)
-
- return popped, err
- }
-
- // LHandle receives a user callback function to handle the left element of the queue, if the function returns nil, then delete the element, otherwise keep the element.
- func (queue *UniqueQueue) LHandle(h func([]byte) error) error {
- return queue.q.LHandle(func(data []byte) error {
- err := h(data)
- if err != nil {
- return err
- }
- _, err = queue.set.Remove(data)
- return err
- })
- }
-
- // Has checks whether the data is already in the queue
- func (queue *UniqueQueue) Has(data []byte) (bool, error) {
- return queue.set.Has(data)
- }
-
- // Len returns the length of the queue
- func (queue *UniqueQueue) Len() int64 {
- queue.set.lock.Lock()
- defer queue.set.lock.Unlock()
- return queue.q.Len()
- }
-
- // Close closes the queue (and the underlying DB if set to closeUnderlyingDB)
- func (queue *UniqueQueue) Close() error {
- _ = queue.q.Close()
- _ = queue.set.Close()
- if !queue.closeUnderlyingDB {
- queue.db = nil
- return nil
- }
- err := queue.db.Close()
- queue.db = nil
- return err
- }
|