Add simple 'read-only' mode.

It just closes the database as soon as it's done loading it.
To refresh, you have to close out and reopen.
This commit is contained in:
Brian Buller 2018-04-26 09:13:13 -05:00
parent 6261d4da06
commit 7d7e0bfcd3
2 changed files with 87 additions and 15 deletions

View File

@ -364,6 +364,9 @@ func addBucketFromBoltBucket(path []string, bb *BoltBucket) error {
} }
func deleteKey(path []string) error { func deleteKey(path []string) error {
if AppArgs.ReadOnly {
return errors.New("DB is in Read-Only Mode")
}
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
// len(b.path)-1 is the key we need to delete, // len(b.path)-1 is the key we need to delete,
// the rest are buckets leading to that key // the rest are buckets leading to that key
@ -469,6 +472,9 @@ func renameBucket(path []string, name string) error {
} }
func updatePairKey(path []string, k string) error { func updatePairKey(path []string, k string) error {
if AppArgs.ReadOnly {
return errors.New("DB is in Read-Only Mode")
}
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
// len(b.path)-1 is the key for the pair we're updating, // len(b.path)-1 is the key for the pair we're updating,
// the rest are buckets leading to that key // the rest are buckets leading to that key
@ -498,6 +504,9 @@ func updatePairKey(path []string, k string) error {
} }
func updatePairValue(path []string, v string) error { func updatePairValue(path []string, v string) error {
if AppArgs.ReadOnly {
return errors.New("DB is in Read-Only Mode")
}
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
// len(b.GetPath())-1 is the key for the pair we're updating, // len(b.GetPath())-1 is the key for the pair we're updating,
// the rest are buckets leading to that key // the rest are buckets leading to that key
@ -521,6 +530,9 @@ func updatePairValue(path []string, v string) error {
} }
func insertBucket(path []string, n string) error { func insertBucket(path []string, n string) error {
if AppArgs.ReadOnly {
return errors.New("DB is in Read-Only Mode")
}
// Inserts a new bucket named 'n' at 'path' // Inserts a new bucket named 'n' at 'path'
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
if len(path) == 0 { if len(path) == 0 {
@ -558,6 +570,9 @@ func insertBucket(path []string, n string) error {
} }
func insertPair(path []string, k string, v string) error { func insertPair(path []string, k string, v string) error {
if AppArgs.ReadOnly {
return errors.New("DB is in Read-Only Mode")
}
// Insert a new pair k => v at path // Insert a new pair k => v at path
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
if len(path) == 0 { if len(path) == 0 {

View File

@ -1,9 +1,10 @@
package main package main
import ( import (
"flag" "errors"
"fmt" "fmt"
"os" "os"
"strings"
"time" "time"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
@ -20,27 +21,79 @@ var currentFilename string
const DefaultDBOpenTimeout = time.Second const DefaultDBOpenTimeout = time.Second
var args struct { var AppArgs struct {
DBOpenTimeout time.Duration DBOpenTimeout time.Duration
ReadOnly bool
} }
func init() { func init() {
flag.DurationVar(&args.DBOpenTimeout, "timeout", DefaultDBOpenTimeout, "DB file open timeout") AppArgs.DBOpenTimeout = DefaultDBOpenTimeout
flag.Usage = func() { AppArgs.ReadOnly = false
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <filename(s)>\nOptions:\n", ProgramName)
flag.PrintDefaults()
} }
func parseArgs() {
var err error
if len(os.Args) == 1 {
printUsage(nil)
}
parms := os.Args[1:]
for i := range parms {
// All 'option' arguments start with "-"
if !strings.HasPrefix(parms[i], "-") {
databaseFiles = append(databaseFiles, parms[i])
continue
}
if strings.Contains(parms[i], "=") {
// Key/Value pair Arguments
pts := strings.Split(parms[i], "=")
key, val := pts[0], pts[1]
switch key {
case "-timeout":
AppArgs.DBOpenTimeout, err = time.ParseDuration(val)
if err != nil {
// See if we can successfully parse by adding a 's'
AppArgs.DBOpenTimeout, err = time.ParseDuration(val + "s")
}
// If err is still not nil, print usage
if err != nil {
printUsage(err)
}
case "-readonly", "-ro":
if val == "true" {
AppArgs.ReadOnly = true
}
case "-help":
printUsage(nil)
default:
printUsage(errors.New("Invalid option"))
}
} else {
// Single-word arguments
switch parms[i] {
case "-readonly", "-ro":
AppArgs.ReadOnly = true
case "-help":
printUsage(nil)
default:
printUsage(errors.New("Invalid option"))
}
}
}
}
func printUsage(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
}
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <filename(s)>\nOptions:\n", ProgramName)
fmt.Fprintf(os.Stderr, " -timeout=duration\n DB file open timeout (default 1s)\n")
fmt.Fprintf(os.Stderr, " -ro, -readonly \n Open the DB in read-only mode\n")
} }
func main() { func main() {
var err error var err error
flag.Parse() parseArgs()
if flag.NArg() == 0 {
flag.Usage()
os.Exit(2)
}
err = termbox.Init() err = termbox.Init()
if err != nil { if err != nil {
@ -50,10 +103,9 @@ func main() {
style := defaultStyle() style := defaultStyle()
termbox.SetOutputMode(termbox.Output256) termbox.SetOutputMode(termbox.Output256)
databaseFiles := flag.Args()
for _, databaseFile := range databaseFiles { for _, databaseFile := range databaseFiles {
currentFilename = databaseFile currentFilename = databaseFile
db, err = bolt.Open(databaseFile, 0600, &bolt.Options{Timeout: args.DBOpenTimeout}) db, err = bolt.Open(databaseFile, 0600, &bolt.Options{Timeout: AppArgs.DBOpenTimeout})
if err == bolt.ErrTimeout { if err == bolt.ErrTimeout {
termbox.Close() termbox.Close()
fmt.Printf("File %s is locked. Make sure it's not used by another app and try again\n", databaseFile) fmt.Printf("File %s is locked. Make sure it's not used by another app and try again\n", databaseFile)
@ -65,12 +117,17 @@ func main() {
} else { } else {
termbox.Close() termbox.Close()
fmt.Printf("Error reading file: %q\n", err.Error()) fmt.Printf("Error reading file: %q\n", err.Error())
os.Exit(111) os.Exit(1)
} }
} }
// First things first, load the database into memory // First things first, load the database into memory
memBolt.refreshDatabase() memBolt.refreshDatabase()
if AppArgs.ReadOnly {
// If we're opening it in readonly mode, close it now
db.Close()
}
// Kick off the UI loop // Kick off the UI loop
mainLoop(memBolt, style) mainLoop(memBolt, style)
defer db.Close() defer db.Close()