Skip to content

Commit

Permalink
feat: lists
Browse files Browse the repository at this point in the history
  • Loading branch information
nalgeon committed Apr 29, 2024
1 parent a1941e3 commit e37e377
Show file tree
Hide file tree
Showing 5 changed files with 2,097 additions and 0 deletions.
172 changes: 172 additions & 0 deletions internal/rlist/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Package rlist is a database-backed list repository.
// It provides methods to interact with lists in the database.
package rlist

import (
"database/sql"

"github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/sqlx"
)

// DB is a database-backed list repository.
// A list is a sequence of strings ordered by insertion order.
// Use the list repository to work with lists and their elements.
type DB struct {
*sqlx.DB[*Tx]
}

// New connects to the list repository.
// Does not create the database schema.
func New(rw *sql.DB, ro *sql.DB) *DB {
d := sqlx.New(rw, ro, NewTx)
return &DB{d}
}

// Delete deletes all occurrences of an element from a list.
// Returns the number of elements deleted.
// Does nothing if the key does not exist or is not a list.
func (d *DB) Delete(key string, elem any) (int, error) {
tx := NewTx(d.RW)
return tx.Delete(key, elem)
}

// DeleteBack deletes the first count occurrences of an element
// from a list, starting from the back. Count must be positive.
// Returns the number of elements deleted.
// Does nothing if the key does not exist or is not a list.
func (d *DB) DeleteBack(key string, elem any, count int) (int, error) {
tx := NewTx(d.RW)
return tx.DeleteBack(key, elem, count)
}

// DeleteFront deletes the first count occurrences of an element
// from a list, starting from the front. Count must be positive.
// Returns the number of elements deleted.
// Does nothing if the key does not exist or is not a list.
func (d *DB) DeleteFront(key string, elem any, count int) (int, error) {
tx := NewTx(d.RW)
return tx.DeleteFront(key, elem, count)
}

// Get returns an element from a list by index (0-based).
// Negative index count from the end of the list
// (-1 is the last element, -2 is the second last, etc.)
// If the index is out of bounds, returns ErrNotFound.
// If the key does not exist or is not a list, returns ErrNotFound.
func (d *DB) Get(key string, idx int) (core.Value, error) {
tx := NewTx(d.RO)
return tx.Get(key, idx)
}

// InsertAfter inserts an element after another element (pivot).
// Returns the length of the list after the operation.
// If the pivot does not exist, returns (-1, ErrNotFound).
// If the key does not exist or is not a list, returns (0, ErrNotFound).
func (d *DB) InsertAfter(key string, mark, elem any) (int, error) {
tx := NewTx(d.RW)
return tx.InsertAfter(key, mark, elem)
}

// InsertBefore inserts an element before another element (pivot).
// Returns the length of the list after the operation.
// If the pivot does not exist, returns (-1, ErrNotFound).
// If the key does not exist or is not a list, returns (0, ErrNotFound).
func (d *DB) InsertBefore(key string, mark, elem any) (int, error) {
tx := NewTx(d.RW)
return tx.InsertBefore(key, mark, elem)
}

// Len returns the number of elements in a list.
// If the key does not exist or is not a list, returns 0.
func (d *DB) Len(key string) (int, error) {
tx := NewTx(d.RO)
return tx.Len(key)
}

// PopBack removes and returns the last element of a list.
// If the key does not exist or is not a list, returns ErrNotFound.
func (d *DB) PopBack(key string) (core.Value, error) {
tx := NewTx(d.RW)
return tx.PopBack(key)
}

// PopBackPushFront removes the last element of a list
// and prepends it to another list (or the same list).
// If the source key does not exist or is not a list, returns ErrNotFound.
func (d *DB) PopBackPushFront(src, dest string) (core.Value, error) {
var elem core.Value
err := d.Update(func(tx *Tx) error {
var err error
elem, err = tx.PopBackPushFront(src, dest)
return err
})
return elem, err
}

// PopFront removes and returns the first element of a list.
// If the key does not exist or is not a list, returns ErrNotFound.
func (d *DB) PopFront(key string) (core.Value, error) {
tx := NewTx(d.RW)
return tx.PopFront(key)
}

// PushBack appends an element to a list.
// Returns the length of the list after the operation.
// If the key does not exist, creates it.
func (d *DB) PushBack(key string, elem any) (int, error) {
var count int
err := d.Update(func(tx *Tx) error {
var err error
count, err = tx.PushBack(key, elem)
return err
})
return count, err
}

// PushFront prepends an element to a list.
// Returns the length of the list after the operation.
// If the key does not exist, creates it.
func (d *DB) PushFront(key string, elem any) (int, error) {
var count int
err := d.Update(func(tx *Tx) error {
var err error
count, err = tx.PushFront(key, elem)
return err
})
return count, err
}

// Range returns a range of elements from a list.
// Both start and stop are zero-based, inclusive.
// Negative indexes count from the end of the list
// (-1 is the last element, -2 is the second last, etc.)
// If the key does not exist or is not a list, returns an empty slice.
func (d *DB) Range(key string, start, stop int) ([]core.Value, error) {
tx := NewTx(d.RO)
return tx.Range(key, start, stop)
}

// Set sets an element in a list by index (0-based).
// Negative index count from the end of the list
// (-1 is the last element, -2 is the second last, etc.)
// If the index is out of bounds, returns ErrNotFound.
// If the key does not exist or is not a list, returns ErrNotFound.
func (d *DB) Set(key string, idx int, elem any) error {
tx := NewTx(d.RW)
return tx.Set(key, idx, elem)
}

// Trim removes elements from both ends of a list so that
// only the elements between start and stop indexes remain.
// Returns the number of elements removed.
//
// Both start and stop are zero-based, inclusive.
// Negative indexes count from the end of the list
// (-1 is the last element, -2 is the second last, etc.)
//
// Does nothing if the key does not exist or is not a list.
func (d *DB) Trim(key string, start, stop int) (int, error) {
tx := NewTx(d.RW)
return tx.Trim(key, start, stop)
}

0 comments on commit e37e377

Please sign in to comment.