package kvdb import ( "encoding/json" "errors" "fmt" "io/ioutil" "os" "path/filepath" "strconv" "strings" ) type KVDB struct { Prefix string Ext string } func (kv *KVDB) Load( keyif interface{}, typ ...interface{}, ) (value interface{}, ok bool, err error) { key, _ := keyif.(string) if "" == key || strings.Contains(key, "..") || strings.ContainsAny(key, "$#!:| \n") { return nil, false, nil } userFile := filepath.Join(kv.Prefix, key+"."+kv.Ext) fmt.Println("Debug user file:", userFile) b, err := ioutil.ReadFile(userFile) if nil != err { if os.IsNotExist(err) { return nil, false, nil } fmt.Println("kvdb debug read:", err) return nil, false, errors.New("database read failed") } ok = true value = b if 1 == len(typ) { err := json.Unmarshal(b, typ[0]) if nil != err { return nil, false, err } value = typ[0] } else if len(b) > 0 && '"' == b[0] { var str string err := json.Unmarshal(b, &str) if nil == err { value = str } } return value, ok, nil } func (kv *KVDB) Store(keyif interface{}, value interface{}) (err error) { key, _ := keyif.(string) if "" == key || strings.Contains(key, "..") || strings.ContainsAny(key, "$#! \n") { return errors.New("invalid key name") } keypath := filepath.Join(kv.Prefix, key+"."+kv.Ext) f, err := os.Open(keypath) if nil == err { s, err := f.Stat() if nil != err { // if we can open, we should be able to stat return errors.New("database connection failure") } ts := strconv.FormatInt(s.ModTime().Unix(), 10) bakpath := filepath.Join(kv.Prefix, key+"."+ts+"."+kv.Ext) if err := os.Rename(keypath, bakpath); nil != err { // keep the old record as a backup return errors.New("database write failure") } } var b []byte switch v := value.(type) { case []byte: b = v case string: b, _ = json.Marshal(v) default: fmt.Println("kvdb: not []byte or string:", v) jsonb, err := json.Marshal(v) if nil != err { return err } b = jsonb } if err := ioutil.WriteFile( keypath, b, os.FileMode(0600), ); nil != err { fmt.Println("write failure:", err) return errors.New("database write failed") } return nil } func (kv *KVDB) Delete(keyif interface{}) (err error) { key, _ := keyif.(string) if "" == key || strings.Contains(key, "..") || strings.ContainsAny(key, "$#! \n") { return errors.New("invalid key name") } keypath := filepath.Join(kv.Prefix, key+"."+kv.Ext) f, err := os.Open(keypath) if nil == err { s, err := f.Stat() if nil != err { return errors.New("database connection failure") } ts := strconv.FormatInt(s.ModTime().Unix(), 64) if err := os.Rename(keypath, filepath.Join(kv.Prefix, key+"."+ts+"."+kv.Ext)); nil != err { return errors.New("database connection failure") } } return nil } func (kv *KVDB) Vacuum() (err error) { return nil }