diff options
| author | nirav <nirav@teisuu.com> | 2020-08-11 15:01:12 +0000 | 
|---|---|---|
| committer | nirav <nirav@teisuu.com> | 2020-08-11 15:01:12 +0000 | 
| commit | 55583a7ad898a40542cd10e888e0df930459607b (patch) | |
| tree | a8f2f0c9b1c97cb56edb9980aa96ef13274798a8 | |
| download | kv-55583a7ad898a40542cd10e888e0df930459607b.tar.gz kv-55583a7ad898a40542cd10e888e0df930459607b.zip  | |
| -rw-r--r-- | .gitingore | 1 | ||||
| -rw-r--r-- | kv.go | 93 | ||||
| -rw-r--r-- | kv_test.go | 74 | 
3 files changed, 168 insertions, 0 deletions
diff --git a/.gitingore b/.gitingore new file mode 100644 index 0000000..0a7dfa0 --- /dev/null +++ b/.gitingore @@ -0,0 +1 @@ +testdb @@ -0,0 +1,93 @@ +package kv + +import ( +	"errors" +	"io/ioutil" +	"os" +	"path/filepath" +	"strings" +	"sync" +) + +var ( +	ErrInvalidKey = errors.New("kv: invalid key") +	ErrNoSuchKey  = errors.New("kv: no such key") +) + +type DB struct { +	cache   map[string][]byte +	basedir string +	m       sync.RWMutex +} + +func NewDB(basedir string) (db *DB, err error) { +	err = os.Mkdir(basedir, 0755) +	if err != nil && !os.IsExist(err) { +		return +	} + +	return &DB{ +		cache:   make(map[string][]byte), +		basedir: basedir, +	}, nil +} + +func (db *DB) Set(key string, val []byte) (err error) { +	if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) { +		return ErrInvalidKey +	} +	db.m.Lock() +	defer db.m.Unlock() + +	err = ioutil.WriteFile(filepath.Join(db.basedir, key), val, 0644) +	if err != nil { +		return +	} + +	db.cache[key] = val +	return +} + +func (db *DB) Get(key string) (val []byte, err error) { +	if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) { +		return nil, ErrInvalidKey +	} +	db.m.Lock() +	defer db.m.Unlock() + +	data, ok := db.cache[key] +	if !ok { +		data, err = ioutil.ReadFile(filepath.Join(db.basedir, key)) +		if err != nil { +			if os.IsNotExist(err) { +				err = ErrNoSuchKey +			} +			return nil, err +		} + +		db.cache[key] = data +	} + +	val = make([]byte, len(data)) +	copy(val, data) +	return +} + +func (db *DB) Remove(key string) (err error) { +	if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) { +		return ErrInvalidKey +	} +	db.m.Lock() +	defer db.m.Unlock() + +	err = os.Remove(filepath.Join(db.basedir, key)) +	if err != nil { +		if os.IsNotExist(err) { +			err = ErrNoSuchKey +		} +		return +	} + +	delete(db.cache, key) +	return +} diff --git a/kv_test.go b/kv_test.go new file mode 100644 index 0000000..0e22ad9 --- /dev/null +++ b/kv_test.go @@ -0,0 +1,74 @@ +package kv + +import ( +	"os" +	"strings" +	"testing" +) + +type row struct { +	key   string +	val   string +	valid bool +} + +func testRow(key string, val string) row { +	return row{key, val, !strings.ContainsRune(key, os.PathSeparator)} +} + +func TestOne(t *testing.T) { +	db, err := NewDatabse("testdb") +	if err != nil { +		t.Errorf("NewDatabase: %s", err) +	} + +	table := []row{ +		testRow("k1", "v1"), +		testRow("k2", "v2"), +		testRow("k3/k3", "v3"), +		testRow("!@#\\(&", "v4"), +		testRow("k5\\\\k5", "v5"), +		testRow("k6**6", "v6"), +		testRow("k7\\**7", "v7"), +	} + +	for _, r := range table { +		err = db.Set(r.key, []byte(r.val)) +		if err != nil { +			if r.valid { +				t.Errorf("db.Set(%+v) expected success, got error: %s", r, err) +			} +			continue +		} +		if !r.valid { +			t.Errorf("db.Set(%+v) expected error, got success", r) +			continue +		} + +		val, err := db.Get(r.key) +		if err != nil { +			t.Errorf("db.Get(%+v) expected success, got error: %s", r, err) +			continue +		} +		if string(val) != r.val { +			t.Errorf("db.Get(%+v) expected val %+v, got %+v", r, r.val, string(val)) +		} + +		err = db.Remove(r.key) +		if err != nil { +			t.Errorf("db.Remove(%+v) expected success, got error: %s", r, err) +			continue +		} +	} + +	k := "non existstent key" +	_, err = db.Get(k) +	if err == nil || err != ErrNoSuchKey { +		t.Errorf("db.Get(%s) expected error (%s), got (%s):", k, ErrNoSuchKey, err) +	} + +	err = db.Remove(k) +	if err == nil || err != ErrNoSuchKey { +		t.Errorf("db.Remove(%s) expected error (%s), got (%s):", k, ErrNoSuchKey, err) +	} +}  | 
