master
Bel LaPointe 2021-12-15 07:33:50 -07:00
parent bc8a0d82df
commit 44ce475100
5 changed files with 147 additions and 13 deletions

2
db.go
View File

@ -42,6 +42,8 @@ func New(key Type, params ...string) (db DB, err error) {
db, err = rclone.NewRClone(params[0], params[1]) db, err = rclone.NewRClone(params[0], params[1])
case FILES: case FILES:
db, err = NewFiles(params[0]) db, err = NewFiles(params[0])
case YAML:
db, err = NewYaml(params[0])
case BOLT: case BOLT:
db, err = NewBolt(params[0]) db, err = NewBolt(params[0])
case MINIO: case MINIO:

View File

@ -14,6 +14,7 @@ import (
"strings" "strings"
"sync" "sync"
"testing" "testing"
"time"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -198,19 +199,20 @@ type = local
for _, db := range cases { for _, db := range cases {
t.Run(fmt.Sprintf("%T", db), func(t *testing.T) { t.Run(fmt.Sprintf("%T", db), func(t *testing.T) {
log.Printf("Trying %T", db) log.Printf("Trying %T", db)
t.Logf(" %T: set", db) t.Logf(" %T: list @[ns1, ns2] against empty", db)
if keys, err := db.List([]string{"ns1", "ns2"}); err != nil || len(keys) > 0 { if keys, err := db.List([]string{"ns1", "ns2"}); err != nil || len(keys) > 0 {
t.Errorf("%T) cannot List() empty: (%T) %+v: %v", db, err, err, keys) t.Errorf("%T) cannot List() empty: (%T) %+v: %v", db, err, err, keys)
} }
if keys, err := db.List([]string{path.Join("ns1", "ns2")}); err != nil || len(keys) > 0 { t.Logf(" %T: set %s @[ns1, ns2]", db, validKey)
t.Errorf("%T) cannot List() empty w/ /: (%T) %+v: %v", db, err, err, keys)
}
if err := db.Set(validKey, validValue, "ns1", "ns2"); err != nil { if err := db.Set(validKey, validValue, "ns1", "ns2"); err != nil {
t.Errorf("%T) cannot set: %v", db, err) t.Errorf("%T) cannot set: %v", db, err)
} }
t.Logf(" %T: get", db) t.Logf(" %T: db: %+v", db, db)
t.Logf(" %T: get %s @[ns1, ns2]", db, validKey)
if v, err := db.Get(validKey, "ns1", "ns2"); err != nil { if v, err := db.Get(validKey, "ns1", "ns2"); err != nil {
t.Errorf("%T) cannot get: %v", db, err) t.Errorf("%T) cannot get: %v", db, err)
log.Printf("%T) cannot get: %v (%+v)", db, err, db)
time.Sleep(time.Second * 10)
} else if !bytes.Equal(v, validValue) { } else if !bytes.Equal(v, validValue) {
t.Errorf("%T) wrong get: %q vs %q", db, v, validValue) t.Errorf("%T) wrong get: %q vs %q", db, v, validValue)
} }

View File

@ -21,6 +21,7 @@ const (
MINIO = Type(iota) MINIO = Type(iota)
RCLONE = Type(iota) RCLONE = Type(iota)
MAPSTREAM = Type(iota) MAPSTREAM = Type(iota)
YAML = Type(iota)
) )
func (t Type) String() string { func (t Type) String() string {
@ -37,6 +38,8 @@ func (t Type) String() string {
return "rclone" return "rclone"
case COCKROACH: case COCKROACH:
return "cockroach" return "cockroach"
case YAML:
return "yaml"
case FILES: case FILES:
return "files" return "files"
case BOLT: case BOLT:

132
yaml.go
View File

@ -2,9 +2,12 @@ package storage
import ( import (
"bytes" "bytes"
"encoding/base64"
"errors" "errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"local/storage/resolve"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@ -43,7 +46,19 @@ func (y *Yaml) Namespaces() ([][]string, error) {
} }
func (y *Yaml) List(ns []string, limits ...string) ([]string, error) { func (y *Yaml) List(ns []string, limits ...string) ([]string, error) {
return nil, errors.New("not impl") namespace := resolve.Namespace(ns)
m, err := y.getMap(namespace)
if err != nil {
return nil, err
}
limits = resolve.Limits(limits)
ks := make([]string, 0, len(m))
for k := range m {
if k >= limits[0] && k <= limits[1] {
ks = append(ks, k)
}
}
return ks, nil
} }
func (y *Yaml) Get(key string, ns ...string) ([]byte, error) { func (y *Yaml) Get(key string, ns ...string) ([]byte, error) {
@ -55,7 +70,21 @@ func (y *Yaml) Get(key string, ns ...string) ([]byte, error) {
} }
func (y *Yaml) GetStream(key string, ns ...string) (io.Reader, error) { func (y *Yaml) GetStream(key string, ns ...string) (io.Reader, error) {
return nil, errors.New("not impl") namespace := resolve.Namespace(ns)
m, err := y.getMap(namespace)
if err != nil {
return nil, err
}
v, ok := m[key]
if !ok {
return nil, ErrNotFound
}
s, ok := v.(string)
if !ok {
return nil, ErrNotFound
}
b, err := base64.StdEncoding.DecodeString(s)
return bytes.NewReader(b), err
} }
func (y *Yaml) Set(key string, value []byte, ns ...string) error { func (y *Yaml) Set(key string, value []byte, ns ...string) error {
@ -67,30 +96,98 @@ func (y *Yaml) Set(key string, value []byte, ns ...string) error {
} }
func (y *Yaml) Del(key string, ns ...string) error { func (y *Yaml) Del(key string, ns ...string) error {
return errors.New("not impl") return y.SetStream(key, nil, ns...)
} }
func (y *Yaml) SetStream(key string, r io.Reader, ns ...string) error { func (y *Yaml) SetStream(key string, r io.Reader, ns ...string) error {
return errors.New("not impl") namespace := resolve.Namespace(ns)
var v interface{} = nil
if r != nil {
b, err := ioutil.ReadAll(r)
if err != nil {
return err
}
v = base64.StdEncoding.EncodeToString(b)
}
m, err := y.getMap()
if err != nil {
return err
}
if err := setInMap(m, []string{namespace}, key, v); err != nil {
return err
}
return y.setMap(m)
} }
func (y *Yaml) Close() error { func (y *Yaml) Close() error {
return nil return nil
} }
func (y *Yaml) getMap() (map[string]interface{}, error) { func (y *Yaml) getMap(keys ...string) (map[string]interface{}, error) {
b, err := y.get() b, err := y.get()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var m map[string]interface{} var mBad map[interface{}]interface{}
err = yaml.Unmarshal(b, &m) if err := yaml.Unmarshal(b, &mBad); err != nil {
return nil, err
}
m, err := mbadToM(mBad)
if err != nil {
return nil, err
}
if m == nil {
m = map[string]interface{}{}
}
for _, k := range keys {
subv, ok := m[k]
if !ok {
subv = map[string]interface{}{}
m[k] = subv
}
subm, ok := subv.(map[string]interface{})
if !ok {
return nil, ErrNotFound
}
m = subm
}
return m, err return m, err
} }
func (y *Yaml) setMap(m map[string]interface{}) error {
b, err := yaml.Marshal(m)
if err != nil {
return err
}
return y.set(b)
}
func setInMap(m map[string]interface{}, keys []string, key string, v interface{}) error {
if len(keys) == 0 {
m[key] = v
if v == nil {
delete(m, key)
}
return nil
}
subv, ok := m[keys[0]]
if !ok {
subv = map[string]interface{}{}
}
subm, ok := subv.(map[string]interface{})
if !ok {
return errors.New("clobber")
}
if err := setInMap(subm, keys[1:], key, v); err != nil {
return err
}
m[keys[0]] = subm
return nil
}
func (y *Yaml) get() ([]byte, error) { func (y *Yaml) get() ([]byte, error) {
b, err := ioutil.ReadFile(y.path) b, err := ioutil.ReadFile(y.path)
if err == os.ErrNotExist { if os.IsNotExist(err) {
return []byte{}, nil return []byte{}, nil
} }
return b, err return b, err
@ -127,3 +224,22 @@ func _keysDFS(m map[string]interface{}) ([][]string, bool, error) {
} }
return keys, hasNonMaps, nil return keys, hasNonMaps, nil
} }
func mbadToM(mBad map[interface{}]interface{}) (map[string]interface{}, error) {
m := map[string]interface{}{}
for k, v := range mBad {
s, ok := k.(string)
if !ok {
s = fmt.Sprint(k)
}
m[s] = v
if m2, ok := v.(map[interface{}]interface{}); ok {
v2, err := mbadToM(m2)
if err != nil {
return nil, err
}
m[s] = v2
}
}
return m, nil
}

View File

@ -43,6 +43,17 @@ func TestKeysDFS(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := range c.want {
found := false
for i := range got {
if fmt.Sprint(got[i]) == fmt.Sprint(c.want[j]) {
found = true
}
}
if !found {
t.Errorf("want %+v among %+v", c.want[j], got)
}
}
if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", c.want) { if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", c.want) {
t.Fatalf("want: %+v\ngot: %+v", c.want, got) t.Fatalf("want: %+v\ngot: %+v", c.want, got)
} }