40 KiB
<file_map> /Users/despiegk/code/github/incubaid/herolib ├── .github │ └── workflows ├── .zed ├── aiprompts │ ├── .openhands │ ├── bizmodel │ ├── documentor │ ├── docusaurus │ ├── herolib_advanced │ ├── herolib_core │ ├── instructions_archive │ │ ├── models_from_v │ │ └── processing │ ├── v_advanced │ ├── v_core │ │ ├── array │ │ ├── benchmark │ │ ├── builtin │ │ ├── crypto │ │ ├── encoding │ │ ├── io │ │ ├── json │ │ ├── json2 │ │ ├── maps │ │ ├── net │ │ ├── orm │ │ ├── regex │ │ ├── string │ │ ├── time │ │ ├── toml │ │ └── veb │ └── v_veb_webserver ├── cli ├── docker │ ├── herolib │ │ └── scripts │ └── postgresql ├── examples │ ├── aiexamples │ ├── biztools │ │ ├── _archive │ │ ├── bizmodel_docusaurus │ │ │ └── archive │ │ │ └── img │ │ └── examples │ │ └── full │ ├── builder │ │ └── remote_executor │ ├── clients │ ├── core │ │ ├── base │ │ ├── db │ │ ├── logger │ │ ├── openapi │ │ │ └── gitea │ │ ├── openrpc │ │ │ └── examples │ │ │ ├── openrpc_client │ │ │ ├── openrpc_docs │ │ │ └── petstore_client │ │ └── pathlib │ │ └── examples │ │ ├── list │ │ ├── md5 │ │ ├── scanner │ │ └── sha256 │ ├── data │ │ ├── location │ │ ├── ourdb_syncer │ │ ├── params │ │ │ ├── args │ │ │ │ └── data │ │ │ └── paramsfilter │ │ └── resp │ ├── develop │ │ ├── codewalker │ │ ├── gittools │ │ ├── heroprompt │ │ ├── ipapi │ │ ├── juggler │ │ │ └── hero │ │ │ └── playbook │ │ ├── luadns │ │ ├── openai │ │ ├── runpod │ │ ├── vastai │ │ └── wireguard │ ├── hero │ │ ├── db │ │ ├── generation │ │ │ ├── blank_generation │ │ │ └── openapi_generation │ │ │ └── example_actor │ │ │ └── specs │ │ ├── herofs │ │ ├── heromodels │ │ └── openapi │ │ └── data │ ├── installers │ │ ├── db │ │ ├── infra │ │ ├── lang │ │ ├── net │ │ ├── sysadmintools │ │ ├── threefold │ │ └── virt │ ├── installers_remote │ ├── jobs │ ├── lang │ │ └── python │ ├── mcp │ │ ├── http_demo │ │ ├── http_server │ │ ├── inspector │ │ └── simple_http │ ├── osal │ │ ├── coredns │ │ ├── download │ │ ├── ping │ │ ├── process │ │ │ ├── process_bash │ │ │ └── process_python │ │ ├── rsync │ │ ├── sandbox │ │ │ └── examples │ │ ├── sshagent │ │ ├── tmux │ │ │ └── heroscripts │ │ ├── ubuntu │ │ └── zinit │ │ ├── rpc │ │ └── simple │ ├── schemas │ │ ├── example │ │ │ └── testdata │ │ ├── openapi │ │ │ └── codegen │ │ └── openrpc │ ├── sshagent │ ├── threefold │ │ ├── grid │ │ │ ├── deploy │ │ │ └── utils │ │ ├── gridproxy │ │ ├── holochain │ │ ├── incatokens │ │ │ └── data │ │ ├── solana │ │ └── tfgrid3deployer │ │ ├── gw_over_wireguard │ │ ├── heroscript │ │ ├── hetzner │ │ ├── open_webui_gw │ │ └── vm_gw_caddy │ ├── tools │ │ └── imagemagick │ │ └── .backup │ ├── ui │ │ ├── console │ │ │ ├── console2 │ │ │ └── flow1 │ │ └── telegram │ ├── vfs │ │ └── vfs_db │ ├── virt │ │ ├── daguserver │ │ ├── docker │ │ │ └── ai_web_ui │ │ ├── heropods │ │ ├── hetzner │ │ ├── lima │ │ ├── podman │ │ └── windows │ ├── web │ │ ├── doctree │ │ │ └── content │ │ └── markdown_renderer │ └── webdav ├── lib │ ├── ai │ │ ├── escalayer │ │ ├── mcp │ │ │ ├── baobab │ │ │ ├── cmd │ │ │ ├── mcpgen │ │ │ │ ├── schemas │ │ │ │ └── templates │ │ │ ├── pugconvert │ │ │ │ ├── cmd │ │ │ │ ├── logic │ │ │ │ │ └── templates │ │ │ │ └── mcp │ │ │ ├── rhai │ │ │ │ ├── cmd │ │ │ │ ├── example │ │ │ │ ├── logic │ │ │ │ │ ├── prompts │ │ │ │ │ └── templates │ │ │ │ └── mcp │ │ │ ├── rust │ │ │ └── vcode │ │ │ ├── cmd │ │ │ ├── logic │ │ │ └── mcp │ │ └── utils │ ├── biz │ │ ├── bizmodel │ │ │ ├── docu │ │ │ ├── exampledata │ │ │ └── templates │ │ ├── investortool │ │ │ └── simulator │ │ │ └── templates │ │ ├── planner │ │ │ ├── examples │ │ │ └── models │ │ └── spreadsheet │ │ └── docu │ ├── builder │ ├── clients │ │ ├── giteaclient │ │ ├── ipapi │ │ ├── jina │ │ │ └── py_specs │ │ ├── livekit │ │ ├── mailclient │ │ ├── meilisearch │ │ ├── mycelium │ │ ├── mycelium_rpc │ │ ├── openai │ │ │ ├── audio │ │ │ ├── embeddings │ │ │ ├── files │ │ │ ├── finetune │ │ │ ├── images │ │ │ └── moderation │ │ ├── postgresql_client │ │ ├── qdrant │ │ ├── rclone │ │ ├── runpod │ │ ├── sendgrid │ │ ├── traefik │ │ ├── vastai │ │ ├── wireguard │ │ ├── zerodb_client │ │ └── zinit │ ├── conversiontools │ │ ├── docsorter │ │ │ └── pythonscripts │ │ ├── imagemagick │ │ ├── pdftotext │ │ └── text_extractor │ ├── core │ │ ├── base │ │ ├── code │ │ │ └── templates │ │ │ ├── comment │ │ │ ├── function │ │ │ ├── interface │ │ │ └── struct │ │ ├── generator │ │ │ └── generic │ │ │ └── templates │ │ ├── herocmds │ │ ├── httpconnection │ │ ├── logger │ │ ├── openrpc_remove │ │ │ ├── examples │ │ │ └── specs │ │ ├── pathlib │ │ ├── playbook │ │ ├── playcmds │ │ ├── playmacros │ │ ├── redisclient │ │ ├── rootpath │ │ ├── smartid │ │ ├── texttools │ │ │ └── regext │ │ │ └── testdata │ │ └── vexecutor │ ├── crypt │ │ ├── aes_symmetric │ │ ├── crpgp │ │ ├── ed25519 │ │ ├── keychain │ │ ├── keysafe │ │ ├── openssl │ │ ├── pgp │ │ └── secrets │ ├── data │ │ ├── cache │ │ ├── countries │ │ │ └── data │ │ ├── currency │ │ ├── dbfs │ │ ├── dedupestor │ │ │ └── dedupe_ourdb │ │ ├── doctree │ │ │ ├── collection │ │ │ │ ├── data │ │ │ │ ├── template │ │ │ │ └── testdata │ │ │ │ └── export_test │ │ │ │ ├── export_expected │ │ │ │ │ └── src │ │ │ │ │ └── col1 │ │ │ │ │ └── img │ │ │ │ └── mytree │ │ │ │ └── dir1 │ │ │ │ └── dir2 │ │ │ ├── pointer │ │ │ └── testdata │ │ │ ├── actions │ │ │ │ └── functionality │ │ │ ├── export_test │ │ │ │ ├── export_expected │ │ │ │ │ ├── col1 │ │ │ │ │ │ └── img │ │ │ │ │ └── col2 │ │ │ │ └── mytree │ │ │ │ ├── dir1 │ │ │ │ │ └── dir2 │ │ │ │ └── dir3 │ │ │ ├── process_defs_test │ │ │ │ ├── col1 │ │ │ │ └── col2 │ │ │ ├── process_includes_test │ │ │ │ ├── col1 │ │ │ │ └── col2 │ │ │ ├── rpc │ │ │ └── tree_test │ │ │ ├── fruits │ │ │ │ └── berries │ │ │ │ └── img │ │ │ └── vegetables │ │ │ └── cruciferous │ │ ├── encoder │ │ ├── encoderhero │ │ ├── flist │ │ ├── gid │ │ ├── graphdb │ │ ├── ipaddress │ │ ├── location │ │ ├── markdown │ │ │ ├── elements │ │ │ ├── parsers │ │ │ ├── testdata │ │ │ └── tools │ │ ├── markdownparser2 │ │ ├── markdownrenderer │ │ ├── mnemonic │ │ ├── models │ │ │ └── hr │ │ ├── ourdb │ │ ├── ourdb_syncer │ │ │ ├── http │ │ │ └── streamer │ │ ├── ourjson │ │ ├── ourtime │ │ ├── paramsparser │ │ ├── radixtree │ │ ├── resp │ │ ├── serializers │ │ ├── tst │ │ ├── verasure │ │ └── vstor │ ├── dav │ │ └── webdav │ │ ├── bin │ │ ├── specs │ │ └── templates │ ├── develop │ │ ├── codewalker │ │ ├── gittools │ │ │ └── tests │ │ ├── heroprompt │ │ │ └── templates │ │ ├── luadns │ │ ├── performance │ │ │ └── cmd │ │ ├── sourcetree │ │ ├── vscode │ │ └── vscode_extensions │ │ └── ourdb │ │ └── templates │ ├── hero │ │ ├── db │ │ ├── herocluster │ │ │ └── example │ │ ├── herofs │ │ ├── herohandlers │ │ ├── heromodels │ │ └── heromodels copy │ │ └── examples │ ├── installers │ │ ├── base │ │ │ └── templates │ │ ├── db │ │ │ ├── cometbft │ │ │ │ └── templates │ │ │ ├── meilisearch_installer │ │ │ ├── postgresql │ │ │ │ └── templates │ │ │ ├── qdrant_installer │ │ │ │ └── templates │ │ │ ├── zerodb │ │ │ └── zerofs │ │ ├── develapps │ │ │ ├── chrome │ │ │ └── vscode │ │ ├── infra │ │ │ ├── coredns │ │ │ │ └── templates │ │ │ ├── gitea │ │ │ │ └── templates │ │ │ ├── livekit │ │ │ │ └── templates │ │ │ └── zinit_installer │ │ ├── lang │ │ │ ├── golang │ │ │ ├── herolib │ │ │ ├── nodejs │ │ │ ├── python │ │ │ ├── rust │ │ │ └── vlang │ │ ├── net │ │ │ ├── mycelium_installer │ │ │ ├── wireguard_installer │ │ │ └── yggdrasil │ │ ├── sysadmintools │ │ │ ├── actrunner │ │ │ │ └── templates │ │ │ ├── b2 │ │ │ ├── fungistor │ │ │ ├── garage_s3 │ │ │ │ └── templates │ │ │ ├── grafana │ │ │ ├── prometheus │ │ │ │ └── templates │ │ │ ├── rclone │ │ │ │ └── templates │ │ │ ├── restic │ │ │ └── s3 │ │ ├── threefold │ │ │ ├── griddriver │ │ │ └── tfrobot │ │ ├── ulist │ │ ├── virt │ │ │ ├── cloudhypervisor │ │ │ ├── docker │ │ │ ├── herorunner │ │ │ ├── lima │ │ │ │ └── templates │ │ │ ├── pacman │ │ │ │ └── templates │ │ │ ├── podman │ │ │ ├── qemu │ │ │ └── youki │ │ └── web │ │ ├── bun │ │ ├── imagemagick │ │ ├── lighttpd │ │ │ └── templates │ │ ├── tailwind │ │ ├── tailwind4 │ │ ├── traefik │ │ │ └── templates │ │ └── zola │ ├── lang │ │ ├── python │ │ │ └── templates │ │ └── rust │ ├── mcp │ │ ├── baobab │ │ ├── cmd │ │ ├── mcpgen │ │ │ ├── schemas │ │ │ └── templates │ │ ├── pugconvert │ │ │ ├── cmd │ │ │ ├── logic │ │ │ │ └── templates │ │ │ └── mcp │ │ ├── rhai │ │ │ ├── cmd │ │ │ ├── example │ │ │ ├── logic │ │ │ │ ├── prompts │ │ │ │ └── templates │ │ │ └── mcp │ │ ├── transport │ │ └── vcode │ │ ├── cmd │ │ ├── logic │ │ └── mcp │ ├── osal │ │ ├── core │ │ ├── coredns │ │ ├── hostsfile │ │ ├── linux │ │ │ └── templates │ │ ├── netns │ │ ├── notifier │ │ ├── osinstaller │ │ ├── rsync │ │ │ └── templates │ │ ├── screen │ │ ├── sshagent │ │ ├── startupmanager │ │ ├── systemd │ │ │ └── templates │ │ ├── tmux │ │ │ └── bin │ │ ├── traefik │ │ │ └── specs │ │ ├── tun │ │ ├── ubuntu │ │ └── ufw │ ├── schemas │ │ ├── jsonrpc │ │ │ ├── reflection │ │ │ └── testdata │ │ │ ├── testmodule │ │ │ └── testserver │ │ ├── jsonschema │ │ │ ├── codegen │ │ │ │ └── templates │ │ │ └── testdata │ │ ├── openapi │ │ │ ├── codegen │ │ │ ├── templates │ │ │ └── testdata │ │ └── openrpc │ │ ├── codegen │ │ │ ├── templates │ │ │ └── testdata │ │ ├── server │ │ └── testdata │ │ └── petstore_client │ ├── security │ │ ├── authentication │ │ │ └── templates │ │ └── jwt │ ├── threefold │ │ ├── grid3 │ │ │ ├── deploy_tosort │ │ │ ├── deployer │ │ │ ├── deployer2_sort │ │ │ ├── griddriver │ │ │ ├── gridproxy │ │ │ │ └── model │ │ │ ├── models │ │ │ ├── rmb │ │ │ ├── tfrobot │ │ │ │ └── templates │ │ │ ├── tokens │ │ │ └── zerohub │ │ ├── grid4 │ │ │ ├── datamodel │ │ │ ├── datamodelsimulator │ │ │ ├── farmingsimulator │ │ │ │ └── templates │ │ │ └── gridsimulator │ │ │ └── manual │ │ ├── incatokens │ │ │ └── templates │ │ └── models │ │ ├── business │ │ ├── core │ │ ├── finance │ │ ├── flow │ │ ├── identity │ │ ├── legal │ │ ├── library │ │ ├── location │ │ └── payment │ ├── ui │ │ ├── console │ │ ├── generic │ │ ├── logger │ │ ├── telegram │ │ │ └── client │ │ ├── template │ │ └── uimodel │ ├── vfs │ │ ├── vfs_calendar │ │ ├── vfs_contacts │ │ ├── vfs_db │ │ ├── vfs_local │ │ ├── vfs_mail │ │ └── vfs_nested │ ├── virt │ │ ├── cloudhypervisor │ │ ├── crun │ │ ├── docker │ │ ├── heropods │ │ ├── herorun │ │ ├── herorun2 │ │ ├── hetznermanager │ │ ├── lima │ │ │ ├── raw │ │ │ └── templates │ │ ├── podman │ │ └── qemu │ │ └── templates │ └── web │ ├── doctreeclient │ ├── docusaurus │ │ └── example │ ├── echarts │ ├── site │ │ └── example │ └── ui │ ├── static │ │ ├── css │ │ └── js │ └── templates │ └── admin ├── libarchive │ ├── baobab │ │ ├── actor │ │ ├── generator │ │ │ ├── _archive │ │ │ ├── templates │ │ │ └── testdata │ │ ├── osis │ │ ├── specification │ │ └── stage │ │ └── interfaces │ ├── buildah │ ├── daguserver │ │ └── templates │ ├── dify │ │ └── templates │ ├── examples │ │ └── baobab │ │ ├── generator │ │ │ ├── basic │ │ │ ├── geomind_poc │ │ │ └── openapi_e2e │ │ └── specification │ ├── installers │ │ └── web │ │ └── caddy2 │ │ └── templates │ ├── rhai │ │ ├── prompts │ │ ├── templates │ │ └── testdata │ ├── starlight │ │ └── templates │ └── zinit │ └── zinit ├── manual │ ├── best_practices │ │ ├── osal │ │ └── scripts │ ├── core │ │ └── concepts │ ├── documentation │ └── playcmds ├── research │ ├── globals │ └── openrpc ├── tests │ └── data └── vscodeplugin └── heroscrypt-syntax └── syntaxes
</file_map>
<file_contents> File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/ai_instructions.md
# HeroDB Model Creation Instructions for AI
## Overview
This document provides clear instructions for AI agents to create new HeroDB models similar to `comment.v`. These models are used to store structured data in Redis using the HeroDB system.
## Key Concepts
- Each model represents a data type stored in Redis hash sets
- Models must implement serialization/deserialization using the `encoder` module
- Models inherit from the `Base` struct which provides common fields
- The database uses a factory pattern for model access
## File Structure
Create a new file in `lib/hero/heromodels/` with the model name (e.g., `calendar.v`).
## Required Components
### 1. Model Struct Definition
Define your model struct with the following pattern:
```v
@[heap]
pub struct Calendar {
db.Base // Inherit from Base struct
pub mut:
// Add your specific fields here
title string
start_time i64
end_time i64
location string
attendees []string
}
2. Type Name Method
Implement a method to return the model's type name:
pub fn (self Calendar) type_name() string {
return 'calendar'
}
3. Serialization (dump) Method
Implement the dump method to serialize your struct's fields using the encoder:
pub fn (self Calendar) dump(mut e &encoder.Encoder) ! {
e.add_string(self.title)
e.add_i64(self.start_time)
e.add_i64(self.end_time)
e.add_string(self.location)
e.add_list_string(self.attendees)
}
4. Deserialization (load) Method
Implement the load method to deserialize your struct's fields:
fn (mut self DBCalendar) load(mut o Calendar, mut e &encoder.Decoder) ! {
o.title = e.get_string()!
o.start_time = e.get_i64()!
o.end_time = e.get_i64()!
o.location = e.get_string()!
o.attendees = e.get_list_string()!
}
5. Model Arguments Struct
Define a struct for creating new instances of your model:
@[params]
pub struct CalendarArg {
pub mut:
title string @[required]
start_time i64
end_time i64
location string
attendees []string
}
6. Database Wrapper Struct
Create a database wrapper struct for your model:
pub struct DBCalendar {
pub mut:
db &db.DB @[skip; str: skip]
}
7. Factory Integration
Add your model to the ModelsFactory struct in factory.v:
pub struct ModelsFactory {
pub mut:
comments DBCalendar
// ... other models
}
And initialize it in the new() function:
pub fn new() !ModelsFactory {
mut mydb := db.new()!
return ModelsFactory{
comments: DBCalendar{
db: &mydb
}
// ... initialize other models
}
}
Encoder Methods Reference
Use these methods for serialization/deserialization:
Encoder (Serialization)
e.add_bool(val bool)e.add_u8(val u8)e.add_u16(val u16)e.add_u32(val u32)e.add_u64(val u64)e.add_i8(val i8)e.add_i16(val i16)e.add_i32(val i32)e.add_i64(val i64)e.add_f32(val f32)e.add_f64(val f64)e.add_string(val string)e.add_list_bool(val []bool)e.add_list_u8(val []u8)e.add_list_u16(val []u16)e.add_list_u32(val []u32)e.add_list_u64(val []u64)e.add_list_i8(val []i8)e.add_list_i16(val []i16)e.add_list_i32(val []i32)e.add_list_i64(val []i64)e.add_list_f32(val []f32)e.add_list_f64(val []f64)e.add_list_string(val []string)
Decoder (Deserialization)
e.get_bool()!e.get_u8()!e.get_u16()!e.get_u32()!e.get_u64()!e.get_i8()!e.get_i16()!e.get_i32()!e.get_i64()!e.get_f32()!e.get_f64()!e.get_string()!e.get_list_bool()!e.get_list_u8()!e.get_list_u16()!e.get_list_u32()!e.get_list_u64()!e.get_list_i8()!e.get_list_i16()!e.get_list_i32()!e.get_list_i64()!e.get_list_f32()!e.get_list_f64()!e.get_list_string()!
CRUD Methods Implementation
Create New Instance
pub fn (mut self DBCalendar) new(args CalendarArg) !Calendar {
mut o := Calendar{
title: args.title
start_time: args.start_time
end_time: args.end_time
location: args.location
attendees: args.attendees
updated_at: ourtime.now().unix()
}
return o
}
Save to Database
pub fn (mut self DBCalendar) set(o Calendar) !u32 {
return self.db.set[Calendar](o)!
}
Retrieve from Database
pub fn (mut self DBCalendar) get(id u32) !Calendar {
mut o, data := self.db.get_data[Calendar](id)!
mut e_decoder := encoder.decoder_new(data)
self.load(mut o, mut e_decoder)!
return o
}
Delete from Database
pub fn (mut self DBCalendar) delete(id u32) ! {
self.db.delete[Calendar](id)!
}
Check Existence
pub fn (mut self DBCalendar) exist(id u32) !bool {
return self.db.exists[Calendar](id)!
}
List All Objects
pub fn (mut self DBCalendar) list() ![]Calendar {
return self.db.list[Calendar]()!.map(self.get(it)!)
}
Example Usage Script
Create a .vsh script in examples/hero/heromodels/ to demonstrate usage:
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
import incubaid.herolib.core.redisclient
import incubaid.herolib.hero.heromodels
mut mydb := heromodels.new()!
// Create a new object
mut o := mydb.calendar.new(
title: 'Meeting'
start_time: 1672531200
end_time: 1672534800
location: 'Conference Room'
attendees: ['john@example.com', 'jane@example.com']
)!
// Save to database
oid := mydb.calendar.set(o)!
println('Created object with ID: ${oid}')
// Retrieve from database
mut o2 := mydb.calendar.get(oid)!
println('Retrieved object: ${o2}')
// List all objects
mut objects := mydb.calendar.list()!
println('All objects: ${objects}')
Best Practices
- Always inherit from
db.Basestruct - Implement all required methods (
type_name,dump,load) - Use the encoder methods for consistent serialization
- Handle errors appropriately with
!ororblocks - Keep field ordering consistent between
dumpandloadmethods - Use snake_case for field names
- Add
@[required]attribute to mandatory fields in argument structs - Initialize timestamps using
ourtime.now().unix()
Implementation Steps Summary
- Create model struct inheriting from
db.Base - Implement
type_name()method - Implement
dump()method using encoder - Implement
load()method using decoder - Create argument struct with
@[params]attribute - Create database wrapper struct
- Add model to
ModelsFactoryinfactory.v - Implement CRUD methods
- Create example usage script
- Test the implementation with the example script
File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/core_methods.v
```v
module db
import incubaid.herolib.data.ourtime
import incubaid.herolib.data.encoder
pub fn (mut self DB) set[T](obj_ T) !u32 {
// Get the next ID
mut obj := obj_
if obj.id == 0 {
obj.id = self.new_id()!
}
mut t := ourtime.now().unix()
if obj.created_at == 0 {
obj.created_at = t
}
obj.updated_at = t
// id u32
// name string
// description string
// created_at i64
// updated_at i64
// securitypolicy u32
// tags u32 // when we set/get we always do as []string but this can then be sorted and md5ed this gies the unique id of tags
// comments []u32
mut e := encoder.new()
e.add_u8(1)
e.add_u32(obj.id)
e.add_string(obj.name)
e.add_string(obj.description)
e.add_i64(obj.created_at)
e.add_i64(obj.updated_at)
e.add_u32(obj.securitypolicy)
e.add_u32(obj.tags)
e.add_u16(u16(obj.comments.len))
for comment in obj.comments {
e.add_u32(comment)
}
// println('set: before dump, e.data.len: ${e.data.len}')
obj.dump(mut e)!
// println('set: after dump, e.data.len: ${e.data.len}')
self.redis.hset(self.db_name[T](), obj.id.str(), e.data.bytestr())!
return obj.id
}
// return the data, cannot return the object as we do not know the type
pub fn (mut self DB) get_data[T](id u32) !(T, []u8) {
data := self.redis.hget(self.db_name[T](), id.str())!
if data.len == 0 {
return error('herodb:${self.db_name[T]()} not found for ${id}')
}
// println('get_data: data.len: ${data.len}')
mut e := encoder.decoder_new(data.bytes())
version := e.get_u8()!
if version != 1 {
panic('wrong version in base load')
}
mut base := T{}
base.id = e.get_u32()!
base.name = e.get_string()!
base.description = e.get_string()!
base.created_at = e.get_i64()!
base.updated_at = e.get_i64()!
base.securitypolicy = e.get_u32()!
base.tags = e.get_u32()!
for _ in 0 .. e.get_u16()! {
base.comments << e.get_u32()!
}
return base, e.data
}
pub fn (mut self DB) exists[T](id u32) !bool {
return self.redis.hexists(self.db_name[T](), id.str())!
}
pub fn (mut self DB) delete[T](id u32) ! {
self.redis.hdel(self.db_name[T](), id.str())!
}
pub fn (mut self DB) list[T]() ![]u32 {
ids := self.redis.hkeys(self.db_name[T]())!
return ids.map(it.u32())
}
// make it easy to get a base object
pub fn (mut self DB) new_from_base[T](args BaseArgs) !Base {
return T{
Base: new_base(args)!
}
}
fn (mut self DB) db_name[T]() string {
// get the name of the type T
mut name := T.name.to_lower_ascii().split('.').last()
// println("db_name rediskey: '${name}'")
return 'db:${name}'
}
pub fn (mut self DB) new_id() !u32 {
return u32(self.redis.incr('db:id')!)
}
File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/core_models.v
module db
import crypto.md5
import incubaid.herolib.core.redisclient
import incubaid.herolib.data.ourtime
// Group represents a collection of users with roles and permissions
@[heap]
pub struct Base {
pub mut:
id u32
name string
description string
created_at i64
updated_at i64
securitypolicy u32
tags u32 // when we set/get we always do as []string but this can then be sorted and md5ed this gies the unique id of tags
comments []u32
}
@[heap]
pub struct SecurityPolicy {
pub mut:
id u32
read []u32 // links to users & groups
write []u32 // links to users & groups
delete []u32 // links to users & groups
public bool
md5 string // this sorts read, write and delete u32 + hash, then do md5 hash, this allows to go from a random read/write/delete/public config to a hash
}
@[heap]
pub struct Tags {
pub mut:
id u32
names []string // unique per id
md5 string // of sorted names, to make easy to find unique id, each name lowercased and made ascii
}
File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/helpers_comments.v
module db
import crypto.md5
@[params]
pub struct CommentArg {
pub mut:
comment string
parent u32
author u32
}
pub fn (mut self DB) comments_get(args []CommentArg) ![]u32 {
return args.map(self.comment_get(it.comment)!)
}
pub fn (mut self DB) comment_get(comment string) !u32 {
comment_fixed := comment.to_lower_ascii().trim_space()
return if comment_fixed.len > 0 {
hash := md5.hexhash(comment_fixed)
comment_found := self.redis.hget('db:comments', hash)!
if comment_found == '' {
id := self.new_id()!
self.redis.hset('db:comments', hash, id.str())!
self.redis.hset('db:comments', id.str(), comment_fixed)!
id
} else {
comment_found.u32()
}
} else {
0
}
}
File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/helpers_tags.v
module db
import crypto.md5
pub fn (mut self DB) tags_get(tags []string) !u32 {
return if tags.len > 0 {
mut tags_fixed := tags.map(it.to_lower_ascii().trim_space()).filter(it != '')
tags_fixed.sort_ignore_case()
hash := md5.hexhash(tags_fixed.join(','))
tags_found := self.redis.hget('db:tags', hash)!
return if tags_found == '' {
println('tags_get: new tags: ${tags_fixed.join(",")}')
id := self.new_id()!
self.redis.hset('db:tags', hash, id.str())!
self.redis.hset('db:tags', id.str(), tags_fixed.join(','))!
id
} else {
tags_found.u32()
}
} else {
0
}
}
File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/heromodels/calendar_event.v
module heromodels
import incubaid.herolib.data.encoder
import incubaid.herolib.data.ourtime
import incubaid.herolib.hero.db
// CalendarEvent represents a single event in a calendar
@[heap]
pub struct CalendarEvent {
db.Base
pub mut:
title string
start_time i64 // Unix timestamp
end_time i64 // Unix timestamp
location string
attendees []u32 // IDs of user groups
fs_items []u32 // IDs of linked files or dirs
calendar_id u32 // Associated calendar
status EventStatus
is_all_day bool
is_recurring bool
recurrence []RecurrenceRule // normally empty
reminder_mins []int // Minutes before event for reminders
color string // Hex color code
timezone string
}
pub struct Attendee {
pub mut:
user_id u32
status AttendanceStatus
role AttendeeRole
}
pub enum AttendanceStatus {
no_response
accepted
declined
tentative
}
pub enum AttendeeRole {
required
optional
organizer
}
pub enum EventStatus {
draft
published
cancelled
completed
}
pub struct RecurrenceRule {
pub mut:
frequency RecurrenceFreq
interval int // Every N frequencies
until i64 // End date (Unix timestamp)
count int // Number of occurrences
by_weekday []int // Days of week (0=Sunday)
by_monthday []int // Days of month
}
pub enum RecurrenceFreq {
none
daily
weekly
monthly
yearly
}
pub struct DBCalendarEvent {
pub mut:
db &db.DB @[skip; str: skip]
}
pub fn (self CalendarEvent) type_name() string {
return 'calendar_event'
}
pub fn (self CalendarEvent) dump(mut e &encoder.Encoder) ! {
e.add_string(self.title)
e.add_i64(self.start_time)
e.add_i64(self.end_time)
e.add_string(self.location)
e.add_list_u32(self.attendees)
e.add_list_u32(self.fs_items)
e.add_u32(self.calendar_id)
e.add_u8(u8(self.status))
e.add_bool(self.is_all_day)
e.add_bool(self.is_recurring)
// Encode recurrence array
e.add_u16(u16(self.recurrence.len))
for rule in self.recurrence {
e.add_u8(u8(rule.frequency))
e.add_int(rule.interval)
e.add_i64(rule.until)
e.add_int(rule.count)
e.add_list_int(rule.by_weekday)
e.add_list_int(rule.by_monthday)
}
e.add_list_int(self.reminder_mins)
e.add_string(self.color)
e.add_string(self.timezone)
}
fn (mut self DBCalendarEvent) load(mut o CalendarEvent, mut e &encoder.Decoder) ! {
o.title = e.get_string()!
o.start_time = e.get_i64()!
o.end_time = e.get_i64()!
o.location = e.get_string()!
o.attendees = e.get_list_u32()!
o.fs_items = e.get_list_u32()!
o.calendar_id = e.get_u32()!
o.status = unsafe { EventStatus(e.get_u8()!) } //TODO: is there no better way?
o.is_all_day = e.get_bool()!
o.is_recurring = e.get_bool()!
// Decode recurrence array
recurrence_len := e.get_u16()!
mut recurrence := []RecurrenceRule{}
for _ in 0 .. recurrence_len {
frequency := unsafe { RecurrenceFreq(e.get_u8()!) }
interval := e.get_int()!
until := e.get_i64()!
count := e.get_int()!
by_weekday := e.get_list_int()!
by_monthday := e.get_list_int()!
recurrence << RecurrenceRule{
frequency: frequency
interval: interval
until: until
count: count
by_weekday: by_weekday
by_monthday: by_monthday
}
}
o.recurrence = recurrence
o.reminder_mins = e.get_list_int()!
o.color = e.get_string()!
o.timezone = e.get_string()!
}
@[params]
pub struct CalendarEventArg {
pub mut:
name string
description string
title string
start_time string // use ourtime module to go from string to epoch
end_time string // use ourtime module to go from string to epoch
location string
attendees []u32 // IDs of user groups
fs_items []u32 // IDs of linked files or dirs
calendar_id u32 // Associated calendar
status EventStatus
is_all_day bool
is_recurring bool
recurrence []RecurrenceRule
reminder_mins []int // Minutes before event for reminders
color string // Hex color code
timezone string
securitypolicy u32
tags []string
comments []db.CommentArg
}
// get new calendar event, not from the DB
pub fn (mut self DBCalendarEvent) new(args CalendarEventArg) !CalendarEvent {
mut o := CalendarEvent{
title: args.title
location: args.location
attendees: args.attendees
fs_items: args.fs_items
calendar_id: args.calendar_id
status: args.status
is_all_day: args.is_all_day
is_recurring: args.is_recurring
recurrence: args.recurrence
reminder_mins: args.reminder_mins
color: args.color
timezone: args.timezone
}
// Set base fields
o.name = args.name
o.description = args.description
o.securitypolicy = args.securitypolicy
o.tags = self.db.tags_get(args.tags)!
o.comments = self.db.comments_get(args.comments)!
o.updated_at = ourtime.now().unix()
// Convert string times to Unix timestamps
mut start_time_obj := ourtime.new(args.start_time)!
o.start_time = start_time_obj.unix()
mut end_time_obj := ourtime.new(args.end_time)!
o.end_time = end_time_obj.unix()
return o
}
pub fn (mut self DBCalendarEvent) set(o CalendarEvent) !u32 {
// Use db set function which now returns the ID
return self.db.set[CalendarEvent](o)!
}
pub fn (mut self DBCalendarEvent) delete(id u32) ! {
self.db.delete[CalendarEvent](id)!
}
pub fn (mut self DBCalendarEvent) exist(id u32) !bool {
return self.db.exists[CalendarEvent](id)!
}
pub fn (mut self DBCalendarEvent) get(id u32) !CalendarEvent {
mut o, data := self.db.get_data[CalendarEvent](id)!
mut e_decoder := encoder.decoder_new(data)
self.load(mut o, mut e_decoder)!
return o
}
pub fn (mut self DBCalendarEvent) list() ![]CalendarEvent {
return self.db.list[CalendarEvent]()!.map(self.get(it)!)
}
</file_contents> <user_instructions> make the crud and example for all files in lib/hero/herofs
think about which additional hsets we need to make it efficient
check the implementation
do the implementation
</user_instructions>