Files
herolib/lib/data/dbfs/namedb.v
2025-10-12 12:30:19 +03:00

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
}