commit afaa7b518bb84a617d5fe1c043d3fd5a471823b2 Author: Brian Buller Date: Thu Jul 7 09:40:51 2016 -0500 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..6cd1df2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..52526b6 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +boltease +======== + +A library for easing the use of bolt databases \ No newline at end of file diff --git a/boltease.go b/boltease.go new file mode 100644 index 0000000..c68832a --- /dev/null +++ b/boltease.go @@ -0,0 +1,285 @@ +package boltease + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/boltdb/bolt" +) + +// This is a library for easing the use of bolt dbs + +// DB is a struct for accomplishing this +type DB struct { + localFile string + localDB *bolt.DB +} + +// Open returns the DB object +func Open(filename string) (*DB, error) { + var err error + b := DB{localFile: filename} + b.localDB, err = bolt.Open(filename, 0644, nil) + if err != nil { + return nil, err + } + // Go ahead and make sure it's fresh + return &b, nil +} + +// MkBucketPath builds all buckets in the string slice +func (b *DB) MkBucketPath(path []string) error { + err := b.localDB.Update(func(tx *bolt.Tx) error { + var err error + bkt := tx.Bucket([]byte(path[0])) + if bkt == nil { + // Create it + bkt, err = tx.CreateBucket([]byte(path[0])) + if err != nil { + // error creating + return err + } + } + if len(path) > 1 { + path = path[1:] + for i := range path { + nextBkt := bkt.Bucket([]byte(path[i])) + if nextBkt == nil { + // Create it + nextBkt, err = bkt.CreateBucket([]byte(path[i])) + if err != nil { + return err + } + } + bkt = nextBkt + } + } + return err + }) + return err +} + +// GetValue returns the value at path +// path is a slice of strings +// key is the key to get +func (b *DB) GetValue(path []string, key string) (string, error) { + var err error + var ret string + b.localDB.View(func(tx *bolt.Tx) error { + bkt := tx.Bucket([]byte(path[0])) + if bkt == nil { + return fmt.Errorf("Couldn't find bucket " + path[0]) + } + var newBkt *bolt.Bucket + for idx := 1; idx < len(path); idx++ { + newBkt = bkt.Bucket([]byte(path[idx])) + if newBkt == nil { + return fmt.Errorf("Couldn't find bucket " + strings.Join(path[:idx], "/")) + } + } + // newBkt should have the last bucket in the path + ret = string(newBkt.Get([]byte(key))) + return nil + }) + return ret, err +} + +// SetValue sets the value of key at path to val +// path is a slice of tokens +func (b *DB) SetValue(path []string, key, val string) error { + err := b.MkBucketPath(path) + if err != nil { + return err + } + b.localDB.Update(func(tx *bolt.Tx) error { + newBkt := tx.Bucket([]byte(path[0])) + if newBkt == nil { + return fmt.Errorf("Couldn't find bucket " + path[0]) + } + for idx := 1; idx < len(path); idx++ { + newBkt, err = newBkt.CreateBucketIfNotExists([]byte(path[idx])) + if err != nil { + return err + } + } + // newBkt should have the last bucket in the path + return newBkt.Put([]byte(key), []byte(val)) + }) + return err +} + +// GetInt returns the value at path +// If the value cannot be parsed as an int, error +func (b *DB) GetInt(path []string, key string) (int, error) { + var ret int + r, err := b.GetValue(path, key) + if err == nil { + ret, err = strconv.Atoi(r) + } + return ret, err +} + +// SetInt Sets an integer value +func (b *DB) SetInt(path []string, key string, val int) error { + return b.SetValue(path, key, strconv.Itoa(val)) +} + +// GetBool returns the value at 'path' +// If the value cannot be parsed as a bool, error +// We check 'true/false' and '1/0', else error +func (b *DB) GetBool(path []string, key string) (bool, error) { + var ret bool + r, err := b.GetValue(path, key) + if err == nil { + if r == "true" || r == "1" { + ret = true + } else if r != "false" && r != "0" { + err = fmt.Errorf("Cannot parse as a boolean") + } + } + return ret, err +} + +// SetBool Sets a boolean value +func (b *DB) SetBool(path []string, key string, val bool) error { + if val { + return b.SetValue(path, key, "true") + } + return b.SetValue(path, key, "false") +} + +// GetTimestamp returns the value at 'path' +// If the value cannot be parsed as a RFC3339, error +func (b *DB) GetTimestamp(path []string, key string) (time.Time, error) { + r, err := b.GetValue(path, key) + if err == nil { + return time.Parse(time.RFC3339, r) + } + return time.Unix(0, 0), err +} + +// SetTimestamp returns the value at 'path' +func (b *DB) SetTimestamp(path []string, key string, val time.Time) error { + return b.SetValue(path, key, val.Format(time.RFC3339)) +} + +// GetBucketList returns a list of all sub-buckets at path +func (b *DB) GetBucketList(path []string) ([]string, error) { + var err error + var ret []string + err = b.localDB.Update(func(tx *bolt.Tx) error { + bkt := tx.Bucket([]byte(path[0])) + if bkt == nil { + return fmt.Errorf("Couldn't find bucket " + path[0]) + } + var newBkt *bolt.Bucket + var berr error + if len(path) > 1 { + for idx := 1; idx < len(path); idx++ { + newBkt = bkt.Bucket([]byte(path[idx])) + if newBkt == nil { + return fmt.Errorf("Couldn't find bucket " + strings.Join(path[:idx], "/")) + } + } + bkt = newBkt + } + + // newBkt should have the last bucket in the path + berr = bkt.ForEach(func(k, v []byte) error { + if v == nil { + // Must be a bucket + ret = append(ret, string(k)) + } + return nil + }) + return berr + }) + return ret, err +} + +// GetKeyList returns a list of all keys at path +func (b *DB) GetKeyList(path []string) ([]string, error) { + var err error + var ret []string + err = b.localDB.Update(func(tx *bolt.Tx) error { + bkt := tx.Bucket([]byte(path[0])) + if bkt == nil { + return fmt.Errorf("Couldn't find bucket " + path[0]) + } + if len(path) > 1 { + var newBkt *bolt.Bucket + for idx := 1; idx < len(path); idx++ { + newBkt = bkt.Bucket([]byte(path[idx])) + if newBkt == nil { + return fmt.Errorf("Couldn't find bucket " + strings.Join(path[:idx], "/")) + } + } + bkt = newBkt + } + // newBkt should have the last bucket in the path + berr := bkt.ForEach(func(k, v []byte) error { + if v != nil { + // Not a bucket + ret = append(ret, string(k)) + } + return nil + }) + return berr + }) + return ret, err +} + +// DeletePair deletes the pair with key at path +func (b *DB) DeletePair(path []string, key string) error { + var err error + err = b.localDB.Update(func(tx *bolt.Tx) error { + bkt := tx.Bucket([]byte(path[0])) + if bkt == nil { + return fmt.Errorf("Couldn't find bucket " + path[0]) + } + if len(path) > 1 { + var newBkt *bolt.Bucket + for idx := 1; idx < len(path); idx++ { + newBkt = bkt.Bucket([]byte(path[idx])) + if newBkt == nil { + return fmt.Errorf("Couldn't find bucket " + strings.Join(path[:idx], "/")) + } + } + bkt = newBkt + } + // bkt should have the last bucket in the path + // Test to make sure that key is a pair, if so, delete it + if tst := bkt.Bucket([]byte(key)); tst == nil { + return bkt.Delete([]byte(key)) + } + return nil + }) + return err +} + +// DeleteBucket deletes the bucket key at path +func (b *DB) DeleteBucket(path []string, key string) error { + var err error + err = b.localDB.Update(func(tx *bolt.Tx) error { + bkt := tx.Bucket([]byte(path[0])) + if bkt == nil { + return fmt.Errorf("Couldn't find bucket " + path[0]) + } + var newBkt *bolt.Bucket + for idx := 1; idx < len(path); idx++ { + newBkt = bkt.Bucket([]byte(path[idx])) + if newBkt == nil { + return fmt.Errorf("Couldn't find bucket " + strings.Join(path[:idx], "/")) + } + } + // newBkt should have the last bucket in the path + // Test to make sure that key is a bucket, if so, delete it + if tst := newBkt.Bucket([]byte(key)); tst != nil { + return newBkt.Delete([]byte(key)) + } + return nil + }) + return err +}