| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- package leveldb
- import (
- "bytes"
- "context"
- "encoding/binary"
- "errors"
- "os"
- "path/filepath"
- "sync"
- "github.com/syndtr/goleveldb/leveldb"
- "github.com/syndtr/goleveldb/leveldb/opt"
- "github.com/syndtr/goleveldb/leveldb/util"
- "github.com/kellegous/go/internal"
- )
- const (
- routesDbFilename = "routes.db"
- idLogFilename = "id"
- )
- // Backend provides access to the leveldb store.
- type Backend struct {
- // Path contains the location on disk where this DB exists.
- path string
- db *leveldb.DB
- lck sync.Mutex
- id uint64
- }
- // Commit the given ID to the data store.
- func commit(filename string, id uint64) error {
- w, err := os.Create(filename)
- if err != nil {
- return err
- }
- defer w.Close()
- if err := binary.Write(w, binary.LittleEndian, id); err != nil {
- return err
- }
- return w.Sync()
- }
- // Load the current ID from the data store.
- func load(filename string) (uint64, error) {
- if _, err := os.Stat(filename); err != nil {
- return 0, commit(filename, 0)
- }
- r, err := os.Open(filename)
- if err != nil {
- return 0, err
- }
- defer r.Close()
- var id uint64
- if err := binary.Read(r, binary.LittleEndian, &id); err != nil {
- return 0, err
- }
- return id, nil
- }
- // New instantiates a new Backend
- func New(path string) (*Backend, error) {
- backend := Backend{
- path: path,
- }
- if _, err := os.Stat(backend.path); err != nil {
- if err := os.MkdirAll(path, os.ModePerm); err != nil {
- return nil, err
- }
- }
- // open the database
- db, err := leveldb.OpenFile(filepath.Join(backend.path, routesDbFilename), nil)
- if err != nil {
- return nil, err
- }
- backend.db = db
- id, err := load(filepath.Join(backend.path, idLogFilename))
- if err != nil {
- return nil, err
- }
- backend.id = id
- return &backend, nil
- }
- // Close the resources associated with this backend.
- func (backend *Backend) Close() error {
- return backend.db.Close()
- }
- // Get retreives a shortcut from the data store.
- func (backend *Backend) Get(ctx context.Context, name string) (*internal.Route, error) {
- val, err := backend.db.Get([]byte(name), nil)
- if err != nil {
- if errors.Is(err, leveldb.ErrNotFound) {
- return nil, internal.ErrRouteNotFound
- }
- return nil, err
- }
- rt := &internal.Route{}
- if err := rt.Read(bytes.NewBuffer(val)); err != nil {
- return nil, err
- }
- return rt, nil
- }
- // Put stores a new shortcut in the data store.
- func (backend *Backend) Put(ctx context.Context, key string, rt *internal.Route) error {
- var buf bytes.Buffer
- if err := rt.Write(&buf); err != nil {
- return err
- }
- return backend.db.Put([]byte(key), buf.Bytes(), &opt.WriteOptions{Sync: true})
- }
- // Del removes an existing shortcut from the data store.
- func (backend *Backend) Del(ctx context.Context, key string) error {
- return backend.db.Delete([]byte(key), &opt.WriteOptions{Sync: true})
- }
- // List all routes in an iterator, starting with the key prefix of start (which can also be nil).
- func (backend *Backend) List(ctx context.Context, start string) (internal.RouteIterator, error) {
- return &RouteIterator{
- it: backend.db.NewIterator(&util.Range{
- Start: []byte(start),
- Limit: nil,
- }, nil),
- }, nil
- }
- // GetAll gets everything in the db to dump it out for backup purposes
- func (backend *Backend) GetAll(ctx context.Context) (map[string]internal.Route, error) {
- golinks := map[string]internal.Route{}
- iter := backend.db.NewIterator(nil, nil)
- defer iter.Release()
- for iter.Next() {
- key := iter.Key()
- val := iter.Value()
- rt := &internal.Route{}
- if err := rt.Read(bytes.NewBuffer(val)); err != nil {
- return nil, err
- }
- golinks[string(key[:])] = *rt
- }
- if err := iter.Error(); err != nil {
- return nil, err
- }
- return golinks, nil
- }
- func (backend *Backend) commit(id uint64) error {
- w, err := os.Create(filepath.Join(backend.path, idLogFilename))
- if err != nil {
- return err
- }
- defer w.Close()
- if err := binary.Write(w, binary.LittleEndian, id); err != nil {
- return err
- }
- return w.Sync()
- }
- // NextID generates the next numeric ID to be used for an auto-named shortcut.
- func (backend *Backend) NextID(ctx context.Context) (uint64, error) {
- backend.lck.Lock()
- defer backend.lck.Unlock()
- backend.id++
- if err := commit(filepath.Join(backend.path, idLogFilename), backend.id); err != nil {
- return 0, err
- }
- return backend.id, nil
- }
|