Init, Get, GetNode, Inc, Dec

This commit is contained in:
Arnas Udovic 2024-07-18 12:40:53 +03:00
parent 18218c30cf
commit 0bb1bb4379
10 changed files with 356 additions and 21 deletions

1
.gitignore vendored
View file

@ -21,3 +21,4 @@
# Go workspace file
go.work
testdata2

View file

@ -1,23 +1,24 @@
# zordfsdb
Simple filesystem based key/value db. Provided as golang lib.
## Configuration
Init root directory
## Supported command
* GET - to get value. Depends on return type. Can be single value or map (to object or to list of object)
* INS - insert object into list
* SAVE - update value or object. Depends on path
* INC - increase abcex value
* DEC - decrease abcex value
* NOW - save current datetime
* DEL - delete
## dictionary
* object - directory of key/value
* list - directory of objects named by abcex as key
* path - path to key or object or list
Simple filesystem based key/value db. Provided as golang lib.
## Configuration
Init root directory
## Supported command
* GET - to get value. Depends on return type. Can be single value or map (to object or to list of object)
* INS - insert object into list
* SAVE - update value or object. Depends on path
* INC - increase abcex value
* DEC - decrease abcex value
* NOW - save current datetime
* DEL - delete
* KEYS - return possible keys
## dictionary
* object - directory of key/value
* list - directory of objects named by abcex as key
* path - path to key or object or list

13
go.mod Normal file
View file

@ -0,0 +1,13 @@
module g.arns.lt/zordsdavini/zordfsdb
go 1.22.5
require (
g.arns.lt/zordsdavini/abcex v1.0.0
github.com/otiai10/copy v1.14.0
)
require (
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
)

10
go.sum Normal file
View file

@ -0,0 +1,10 @@
g.arns.lt/zordsdavini/abcex v1.0.0 h1:qQqlZ4DMfethCGK4I6yGaLqMrTzKNIshqpINd1l3t0E=
g.arns.lt/zordsdavini/abcex v1.0.0/go.mod h1:YRcJgts3XZwI+LEkngpfUab3DkUAW387Irpr43hIym8=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

1
testdata/counter vendored Normal file
View file

@ -0,0 +1 @@
12

1
testdata/list/1/key2 vendored Normal file
View file

@ -0,0 +1 @@
value2

1
testdata/list/2/key2 vendored Normal file
View file

@ -0,0 +1 @@
value3

1
testdata/object/key1 vendored Normal file
View file

@ -0,0 +1 @@
value1

197
zordfsdb.go Normal file
View file

@ -0,0 +1,197 @@
package zordfsdb
import (
"os"
"path"
"strings"
"g.arns.lt/zordsdavini/abcex"
)
type DB struct {
Root string
Nodes map[string]Node
}
type Node struct {
Key string
Value string
Nodes map[string]Node
Object bool
List bool
}
func InitDB(root string) (DB, error) {
db := DB{Root: root}
err := db.Refresh()
return db, err
}
func (db *DB) Refresh() error {
db.Nodes = make(map[string]Node)
files, err := os.ReadDir(db.Root)
if err != nil {
return err
}
for _, file := range files {
node := Node{Key: file.Name()}
node.Nodes = make(map[string]Node)
if file.IsDir() {
node, err = readDir(db.Root, node)
if err != nil {
return err
}
} else {
b, err := os.ReadFile(path.Join(db.Root, file.Name()))
if err != nil {
return err
}
node.Value = strings.Trim(string(b), "\n")
}
db.Nodes[file.Name()] = node
}
return nil
}
func readDir(root string, node Node) (Node, error) {
newRoot := path.Join(root, node.Key)
files, err := os.ReadDir(newRoot)
if err != nil {
return node, err
}
for _, file := range files {
child := Node{Key: file.Name()}
child.Nodes = make(map[string]Node)
if file.IsDir() {
child, err = readDir(newRoot, child)
if err != nil {
return node, err
}
} else {
b, err := os.ReadFile(path.Join(newRoot, file.Name()))
if err != nil {
return node, err
}
child.Value = strings.Trim(string(b), "\n")
}
node.Nodes[file.Name()] = child
}
node.FixType()
return node, nil
}
func (node *Node) FixType() {
node.List = false
node.Object = false
for _, child := range node.Nodes {
if child.Object {
node.List = true
break
}
node.Object = true
break
}
}
func (db *DB) Keys(vpath string) []string {
keys := []string{}
fullPath := []string{db.Root}
fullPath = append(fullPath, strings.Split(vpath, ".")...)
root := path.Join(fullPath...)
files, err := os.ReadDir(root)
if err != nil {
return keys
}
for _, file := range files {
keys = append(keys, file.Name())
}
return keys
}
func (db *DB) GetNode(vpath string) (Node, bool) {
nodes := db.Nodes
current := Node{}
STEP_LOOP:
for _, step := range strings.Split(vpath, ".") {
for _, node := range nodes {
if step == node.Key {
nodes = node.Nodes
current = node
continue STEP_LOOP
}
}
return current, false
}
return current, true
}
func (db *DB) Get(vpath string) (string, bool) {
node, found := db.GetNode(vpath)
if !found {
return "", false
}
return node.Value, true
}
func (db *DB) Inc(vpath string) bool {
fullPath := []string{db.Root}
fullPath = append(fullPath, strings.Split(vpath, ".")...)
val, found := db.Get(vpath)
if !found {
return false
}
valAbc := abcex.Decode(val)
valAbc++
val = abcex.Encode(valAbc)
os.WriteFile(path.Join(fullPath...), []byte(string(val)), 0644)
err := db.Refresh()
if err != nil {
return false
}
return true
}
func (db *DB) Dec(vpath string) bool {
fullPath := []string{db.Root}
fullPath = append(fullPath, strings.Split(vpath, ".")...)
val, found := db.Get(vpath)
if !found {
return false
}
valAbc := abcex.Decode(val)
valAbc--
val = abcex.Encode(valAbc)
os.WriteFile(path.Join(fullPath...), []byte(string(val)), 0644)
err := db.Refresh()
if err != nil {
return false
}
return true
}

109
zordfsdb_test.go Normal file
View file

@ -0,0 +1,109 @@
package zordfsdb
import (
"fmt"
"os"
"testing"
cp "github.com/otiai10/copy"
)
func TestInit(t *testing.T) {
_, err := InitDB("./testdata")
if err != nil {
t.Fatal(err)
}
}
func TestKeys(t *testing.T) {
db, err := InitDB("./testdata")
if err != nil {
t.Fatal(err)
}
keys := db.Keys("")
fmt.Println(keys)
if len(keys) != 3 {
t.Fatal("Wrong keys count on root")
}
keys = db.Keys("list")
fmt.Println(keys)
if len(keys) != 2 {
t.Fatal("Wrong keys count on list")
}
keys = db.Keys("list.1")
fmt.Println(keys)
if len(keys) != 1 {
t.Fatal("Wrong keys count on list.1")
}
keys = db.Keys("object")
fmt.Println(keys)
if len(keys) != 1 {
t.Fatal("Wrong keys count on object")
}
}
func TestGet(t *testing.T) {
db, err := InitDB("./testdata")
if err != nil {
t.Fatal(err)
}
val, found := db.Get("counter")
if !found || val != "12" {
t.Fatal("counter value get fail")
}
_, found = db.Get("not_exist")
if found {
t.Fatal("found not existing")
}
val, found = db.Get("list.2.key2")
if !found || val != "value3" {
t.Fatal("list.2.key2 value get fail")
}
}
func copy(t *testing.T) {
err := os.RemoveAll("./testdata2")
if err != nil {
}
err = cp.Copy("./testdata", "./testdata2")
if err != nil {
t.Fatal("Couldn't prepare data for testing")
}
}
func TestInc(t *testing.T) {
copy(t)
db, err := InitDB("./testdata2")
if err != nil {
t.Fatal(err)
}
db.Inc("counter")
val, found := db.Get("counter")
if !found || val != "13" {
t.Fatal("counter value get fail")
}
}
func TestDec(t *testing.T) {
copy(t)
db, err := InitDB("./testdata2")
if err != nil {
t.Fatal(err)
}
db.Dec("counter")
val, found := db.Get("counter")
if !found || val != "11" {
t.Fatal("counter value get fail")
}
}