191 lines
5.1 KiB
V
191 lines
5.1 KiB
V
module dbfs
|
|
|
|
import json
|
|
import crypto.md5
|
|
import incubaid.herolib.core.pathlib
|
|
// import incubaid.herolib.ui.console
|
|
|
|
@[heap]
|
|
pub struct NameDB {
|
|
pub mut:
|
|
path pathlib.Path
|
|
config NameDBConfig
|
|
}
|
|
|
|
pub struct NameDBConfig {
|
|
pub mut:
|
|
levels int = 1
|
|
}
|
|
|
|
// TODO: need to put levels in, so that use less directories if nr of items in DB is small
|
|
|
|
// purpose of this file is to create an index (can optionally have data attached to this index per key)
|
|
// so we can easily map between a key and an id or other way around
|
|
|
|
// if key and ok to hash, then we can generated unique id out of the hashed key
|
|
|
|
pub fn namedb_new(path string) !NameDB {
|
|
mut p := pathlib.get_dir(path: path, create: true)!
|
|
mut p_meta := p.file_get('.meta') or {
|
|
p2 := pathlib.get_file(path: '${p.path}/.meta', create: true)!
|
|
p2
|
|
}
|
|
data := p_meta.read()!
|
|
mut cfg := NameDBConfig{}
|
|
if data.len > 0 {
|
|
cfg = json.decode(NameDBConfig, data)!
|
|
}
|
|
return NameDB{
|
|
path: p
|
|
config: cfg
|
|
}
|
|
}
|
|
|
|
pub fn (mut db NameDB) save() ! {
|
|
mut p := pathlib.get_file(path: '${db.path.path}/.meta', create: false)!
|
|
data := json.encode(db.config)
|
|
p.write(data)!
|
|
}
|
|
|
|
// will store in a place where it can easily be found back and it returns a unique u32
|
|
pub fn (mut db NameDB) set(key string, data string) !u32 {
|
|
myid, mut mypath := db.key2path(key)!
|
|
// Check if the pubkey already exists in the file
|
|
mut line_num := u32(0)
|
|
content := mypath.read()!
|
|
mut lines := content.trim_space().split_into_lines()
|
|
mut lines_out := []string{}
|
|
mut idfound := u32(0)
|
|
for mut line in lines {
|
|
key_in_file, _ := namedb_process_line(mypath.path, line)
|
|
if key_in_file == key {
|
|
line = '${key}:${data}'
|
|
if idfound > 0 {
|
|
panic('bug, there is double key, should not be possible')
|
|
}
|
|
idfound = myid + line_num
|
|
}
|
|
line_num += 1
|
|
lines_out << line
|
|
}
|
|
if idfound == 0 {
|
|
// need to add the line was not in file yet
|
|
lines << '${key}:${data}'
|
|
}
|
|
mypath.write(lines.join('\n'))!
|
|
|
|
return myid + u32(lines.len) - 1
|
|
}
|
|
|
|
pub fn (mut db NameDB) delete(key string) ! {
|
|
_, mut mypath := db.key2path(key)!
|
|
content := mypath.read()!
|
|
mut lines := content.trim_space().split_into_lines()
|
|
mut lines_out := []string{}
|
|
mut found := false
|
|
for mut line in lines {
|
|
key_in_file, _ := namedb_process_line(mypath.path, line)
|
|
if key_in_file == key {
|
|
found = true
|
|
continue // skip
|
|
}
|
|
lines_out << line
|
|
}
|
|
if found {
|
|
mypath.write(lines.join('\n'))!
|
|
}
|
|
}
|
|
|
|
// will store in a place where it can easily be found back and it returns a unique u32
|
|
pub fn (mut db NameDB) get(key string) !(u32, string) {
|
|
myid, mut mypath := db.key2path(key)!
|
|
mut line_num := u32(0)
|
|
content := mypath.read()!
|
|
mut lines := content.trim_space().split_into_lines()
|
|
for line in lines {
|
|
key_in_file, data := namedb_process_line(mypath.path, line)
|
|
if key_in_file == key {
|
|
return myid + line_num, data
|
|
}
|
|
line_num += 1
|
|
}
|
|
return error("can't find key:${key} in db:${db.path.path}")
|
|
}
|
|
|
|
pub fn (mut db NameDB) exists(key string) !bool {
|
|
_, mut mypath := db.key2path(key)!
|
|
content := mypath.read()!
|
|
mut lines := content.trim_space().split_into_lines()
|
|
for line in lines {
|
|
key_in_file, _ := namedb_process_line(mypath.path, line)
|
|
if key_in_file == key {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
pub fn (mut db NameDB) get_from_id(myid u32) !(string, string) {
|
|
// console.print_debug("key get: ${myid}")
|
|
mut mypath := db.dbpath(myid)!
|
|
// console.print_debug("path: ${mypath.path}")
|
|
_, _, c := namedb_dbid(myid)
|
|
// console.print_debug("ids: ${a} ${b} ${c}")
|
|
content := mypath.read()!
|
|
mut lines := content.trim_space().split_into_lines()
|
|
// console.print_debug(lines)
|
|
if c < lines.len {
|
|
myline := lines[c] or {
|
|
return error('out of bounds for: ${mypath.path}. Nrlines:${lines.len}. Line:${c}')
|
|
}
|
|
key_in_file, data := namedb_process_line(mypath.path, myline)
|
|
return key_in_file, data
|
|
}
|
|
return error('Line nr higher than file nr of lines: ${mypath.path}. Nrlines:${lines.len}. Line:${c}')
|
|
}
|
|
|
|
// calculate the id's as needed to create the path
|
|
fn namedb_dbid(myid u32) (u8, u8, u16) {
|
|
a := u8(myid / u32(256 * 256))
|
|
a_post := myid - u32(a) * u32(256 * 256)
|
|
b := u8(a_post / 256)
|
|
b_post := a_post - u32(b) * u32(256)
|
|
c := u16(b_post)
|
|
return a, b, c
|
|
}
|
|
|
|
fn (mut db NameDB) key2path(key string) !(u32, pathlib.Path) {
|
|
hash_bytes := md5.sum(key.bytes())
|
|
if key.len < 2 {
|
|
return error('key needs to be at least 2 chars')
|
|
}
|
|
a := hash_bytes[0] or { panic('bug') }
|
|
b := hash_bytes[1] or { panic('bug') }
|
|
myid := u32(int(a) * 256 * 256 + int(b) * 256)
|
|
mut mypath := db.dbpath(myid)!
|
|
return myid, mypath
|
|
}
|
|
|
|
fn namedb_process_line(path string, line string) (string, string) {
|
|
if line.contains(':') {
|
|
myline_parts := line.split(':').map(it.trim_space())
|
|
if myline_parts.len != 2 {
|
|
panic('syntax error in line ${line} in ${path}, not enough parts.')
|
|
}
|
|
return myline_parts[0], myline_parts[1]
|
|
}
|
|
return line.trim_space(), ''
|
|
}
|
|
|
|
fn (mut db NameDB) dbpath(myid u32) !pathlib.Path {
|
|
a, b, _ := namedb_dbid(myid)
|
|
// console.print_debug("dbpath ids: ${a} ${b} ${c}")
|
|
dir_name := a.hex()
|
|
file_name := b.hex()
|
|
mut mydatafile := pathlib.get_file(
|
|
path: '${db.path.path}/${dir_name}/${file_name}.txt'
|
|
create: true
|
|
)!
|
|
return mydatafile
|
|
}
|