LamerAlert/config/config.go

198 lines
3.8 KiB
Go

package config
import (
"encoding/json"
"fmt"
"sync"
"time"
"github.com/boltdb/bolt"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
var (
bucket = []byte("config")
)
type configValue struct {
Key string `json:"key"`
Value interface{} `json:"value"`
}
func (value configValue) getBytes() ([]byte, error) {
return json.Marshal(value)
}
func decodeConfig(in []byte) (configValue, error) {
var out configValue
err := json.Unmarshal(in, &out)
return out, err
}
type Config struct {
// I'm not completely sure if this mutex is necessary.
// It is left here because I'm opening an closing the db
// for every operation.
sync.Mutex
path string
}
func (c *Config) db() *bolt.DB {
db, err := bolt.Open(c.path, 0666,
&bolt.Options{
Timeout: 1 * time.Second,
})
if err != nil {
log.Fatal().Msgf("Could not open database: %s", err)
}
err = db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists(bucket)
return err
})
if err != nil {
log.Fatal().Err(err).Msg("Could not open bucket")
}
return db
}
func (c *Config) getAll() (map[string]configValue, error) {
c.Lock()
defer c.Unlock()
db := c.db()
defer db.Close()
vals := map[string]configValue{}
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket)
err := b.ForEach(func(k, v []byte) error {
value, err := decodeConfig(v)
if err != nil {
return fmt.Errorf("could not convert value: %w", err)
}
vals[string(k)] = value
return nil
})
if err != nil {
return fmt.Errorf("error in forEach: %w", err)
}
return nil
})
return vals, err
}
func (c *Config) get(key string) (configValue, error) {
c.Lock()
defer c.Unlock()
db := c.db()
defer db.Close()
var out configValue
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket)
value, err := decodeConfig(b.Get([]byte(key)))
if err != nil {
return err
}
out = value
return nil
})
if err != nil {
return configValue{}, err
}
return out, nil
}
func (c *Config) set(key string, value configValue) error {
c.Lock()
defer c.Unlock()
db := c.db()
defer db.Close()
err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket)
v, err := value.getBytes()
if err != nil {
return err
}
err = b.Put([]byte(key), v)
return err
})
if err != nil {
log.Error().Msgf("Error setting key: %s", err)
}
return err
}
func (c *Config) delete(key string) error {
c.Lock()
defer c.Unlock()
db := c.db()
defer db.Close()
err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket)
return b.Delete([]byte(key))
})
return err
}
// Public interface
func New(r *mux.Router) *Config {
c := &Config{
path: "lameralert.db",
}
c.setup(r)
return c
}
func (c *Config) GetInt64(key string, defaultValue int64) int64 {
v, err := c.get(key)
if err != nil {
return defaultValue
}
return int64(v.Value.(float64))
}
func (c *Config) GetInt(key string, defaultValue int) int {
v, err := c.get(key)
if err != nil {
return defaultValue
}
return int(v.Value.(float64))
}
func (c *Config) GetStringSlice(key string, defaultValue []string) []string {
v, err := c.get(key)
if err != nil {
log.Error().Msgf("GetStringSlice errored looking for %s: %s", key, err)
return defaultValue
}
return interfaceSliceToStringSlice(v.Value.([]interface{}))
}
func interfaceSliceToStringSlice(in []interface{}) []string {
out := []string{}
for _, it := range in {
out = append(out, it.(string))
}
return out
}
func (c *Config) GetStringMap(key string, defaultValue map[string]string) map[string]string {
v, err := c.get(key)
if err != nil {
return defaultValue
}
return v.Value.(map[string]string)
}
func (c *Config) GetString(key, defaultValue string) string {
v, err := c.get(key)
if err != nil {
return defaultValue
}
return v.Value.(string)
}
func (c *Config) Set(key string, value interface{}) error {
return c.set(key, configValue{key, value})
}