Init, Get, GetNode, Inc, Dec
This commit is contained in:
parent
18218c30cf
commit
0bb1bb4379
10 changed files with 356 additions and 21 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -21,3 +21,4 @@
|
|||
# Go workspace file
|
||||
go.work
|
||||
|
||||
testdata2
|
||||
|
|
43
README.md
43
README.md
|
@ -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
13
go.mod
Normal 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
10
go.sum
Normal 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
1
testdata/counter
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
12
|
1
testdata/list/1/key2
vendored
Normal file
1
testdata/list/1/key2
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
value2
|
1
testdata/list/2/key2
vendored
Normal file
1
testdata/list/2/key2
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
value3
|
1
testdata/object/key1
vendored
Normal file
1
testdata/object/key1
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
value1
|
197
zordfsdb.go
Normal file
197
zordfsdb.go
Normal 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
109
zordfsdb_test.go
Normal 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")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue