feat: improve build and serialization logic

- Update v fmt exit code handling
- Support dynamic organization for symlinks
- Add f32 and list f64 serialization/deserialization
- Improve JSON decoding for bid requirements/pricing
- Add basic tests for Bid and Node creation
This commit is contained in:
Mahmoud-Emad
2025-10-12 13:27:10 +03:00
parent de8f390f4b
commit a65cbd606b
8 changed files with 108 additions and 30 deletions

1
.gitignore vendored
View File

@@ -57,3 +57,4 @@ MCP_HTTP_REST_IMPLEMENTATION_PLAN.md
tmux_logger
release
install_herolib
doc

15
doc.vsh
View File

@@ -6,15 +6,22 @@ abs_dir_of_script := dir(@FILE)
// Format code
println('Formatting code...')
if os.system('v fmt -w ${abs_dir_of_script}/examples') != 0 {
eprintln('Warning: Failed to format examples')
// v fmt returns:
// - 0: all files already formatted (no changes)
// - 5: files were formatted (changes made) - this is SUCCESS
// - other: actual errors (syntax errors, file access issues, etc.)
fmt_examples_result := os.system('v fmt -w ${abs_dir_of_script}/examples')
if fmt_examples_result != 0 && fmt_examples_result != 5 {
eprintln('Error: Failed to format examples (exit code: ${fmt_examples_result})')
exit(1)
}
if os.system('v fmt -w ${abs_dir_of_script}/lib') != 0 {
eprintln('Warning: Failed to format herolib')
fmt_lib_result := os.system('v fmt -w ${abs_dir_of_script}/lib')
if fmt_lib_result != 0 && fmt_lib_result != 5 {
eprintln('Error: Failed to format herolib (exit code: ${fmt_lib_result})')
exit(1)
}
println(' Code formatting completed')
// Clean existing docs
println('Cleaning existing documentation...')

View File

@@ -47,7 +47,7 @@ abs_dir_of_script := dir(@FILE)
println('Script directory: ${abs_dir_of_script}')
// Determine the organization name from the current path
// This makes the script work with any organization (incubaid, freeflowuniverse, etc.)
// This makes the script work with any organization (incubaid, etc.)
path_parts := abs_dir_of_script.split('/')
mut org_name := 'incubaid' // default fallback
for i, part in path_parts {
@@ -60,32 +60,35 @@ for i, part in path_parts {
println('Detected organization: ${org_name}')
println('Will create symlink: ${os.home_dir()}/.vmodules/${org_name}/herolib -> ${abs_dir_of_script}/lib')
// Reset symlinks for both possible organizations (cleanup)
// Reset symlinks (cleanup)
println('Resetting all symlinks...')
os.rm('${os.home_dir()}/.vmodules/freeflowuniverse/herolib') or {}
os.rm('${os.home_dir()}/.vmodules/incubaid/herolib') or {}
os.rm('${os.home_dir()}/.vmodules/${org_name}/herolib') or {}
// Create necessary directories
os.mkdir_all('${os.home_dir()}/.vmodules/freeflowuniverse') or {
panic('Failed to create directory ~/.vmodules/freeflowuniverse: ${err}')
os.mkdir_all('${os.home_dir()}/.vmodules/${org_name}') or {
panic('Failed to create directory ~/.vmodules/${org_name}: ${err}')
}
// Create new symlinks
os.symlink('${abs_dir_of_script}/lib', '${os.home_dir()}/.vmodules/freeflowuniverse/herolib') or {
panic('Failed to create herolib symlink: ${err}')
}
symlink_target := '${abs_dir_of_script}/lib'
symlink_path := '${os.home_dir()}/.vmodules/${org_name}/herolib'
os.symlink(symlink_target, symlink_path) or { panic('Failed to create herolib symlink: ${err}') }
// Verify the symlink was created
symlink_path := '${os.home_dir()}/.vmodules/${org_name}/herolib'
if os.exists(symlink_path) {
// Note: os.exists() may return false for broken symlinks, so we check if it's a link first
if os.is_link(symlink_path) {
println(' Symlink created successfully: ${symlink_path}')
// Verify it points to the right location
if os.is_link(symlink_path) {
println(' Confirmed: ${symlink_path} is a symbolic link')
println(' Points to: ${symlink_target}')
// Verify the target exists
if os.exists(symlink_target) {
println(' Target directory exists and is accessible')
} else {
eprintln(' Warning: Symlink target does not exist: ${symlink_target}')
}
} else {
panic('Failed to verify herolib symlink at ${symlink_path}')
panic('Failed to create herolib symlink at ${symlink_path}')
}
println('Herolib installation completed successfully!')

View File

@@ -136,6 +136,14 @@ pub fn (mut d Decoder) get_f64() !f64 {
return f
}
pub fn (mut d Decoder) get_f32() !f32 {
// Get the u32 bits first and then convert back to f32
bits := d.get_u32()!
// Use unsafe to convert bits to f32
f := unsafe { *(&f32(&bits)) }
return f
}
pub fn (mut d Decoder) get_time() !time.Time {
secs_ := d.get_u32()!
secs := i64(secs_)
@@ -229,6 +237,15 @@ pub fn (mut d Decoder) get_list_u64() ![]u64 {
return v
}
pub fn (mut d Decoder) get_list_f64() ![]f64 {
n := d.get_u16()!
mut v := []f64{len: int(n)}
for i in 0 .. n {
v[i] = d.get_f64()!
}
return v
}
pub fn (mut d Decoder) get_map_string() !map[string]string {
n := d.get_u16()!
mut v := map[string]string{}

View File

@@ -115,6 +115,13 @@ pub fn (mut b Encoder) add_f64(data f64) {
b.add_u64(bits)
}
// adds a float32 value
pub fn (mut b Encoder) add_f32(data f32) {
// Convert f32 to bits first, then store as u32
bits := unsafe { *(&u32(&data)) }
b.add_u32(bits)
}
// adds gid as a string
pub fn (mut b Encoder) add_gid(data gid.GID) {
b.add_string(data.str())
@@ -185,6 +192,16 @@ pub fn (mut b Encoder) add_list_u64(data []u64) {
}
}
pub fn (mut b Encoder) add_list_f64(data []f64) {
if data.len > 64 * kb {
panic('list cannot have more than 64kb items.')
}
b.add_u16(u16(data.len)) // how many items in list
for item in data {
b.add_f64(item)
}
}
// when complicated hash e.g. map of other object need to serialize each sub object
pub fn (mut b Encoder) add_map_string(data map[string]string) {
if data.len > 64 * kb {

View File

@@ -3,7 +3,7 @@ module models_tfgrid
import incubaid.herolib.data.encoder
import incubaid.herolib.data.ourtime
import incubaid.herolib.hero.db
import incubaid.herolib.data.json
import json
// Bid - ROOT OBJECT
@[heap]
@@ -106,8 +106,10 @@ pub fn (self Bid) dump(mut e encoder.Encoder) ! {
fn (mut self DBBid) load(mut o Bid, mut e encoder.Decoder) ! {
o.customer_id = e.get_u32()!
o.requirements = json.decode[map[string]string](e.get_string()!)!
o.pricing = json.decode[map[string]string](e.get_string()!)!
requirements_str := e.get_string()!
o.requirements = json.decode(map[string]string, requirements_str)!
pricing_str := e.get_string()!
o.pricing = json.decode(map[string]string, pricing_str)!
o.status = unsafe { BidStatus(e.get_int()!) }
o.obligation = e.get_bool()!
o.start_date = e.get_u32()!

View File

@@ -1,8 +0,0 @@
module models_tfgrid
// Re-export all the root objects and their supporting structures
pub use node
pub use nodegroup
pub use bid
pub use contract
pub use reputation

View File

@@ -0,0 +1,39 @@
module models_tfgrid
fn test_bid_creation() {
// Just a simple test to verify the module compiles
bid := Bid{
customer_id: 123
requirements: {
'compute_nodes': '10'
}
pricing: {
'compute_nodes': '10.2'
}
status: .pending
obligation: true
start_date: 1234567890
end_date: 1234567890
signature_user: 'test_signature'
billing_period: .monthly
}
assert bid.customer_id == 123
assert bid.status == .pending
}
fn test_node_creation() {
// Just a simple test to verify the module compiles
node := Node{
nodegroupid: 456
country: 'US'
uptime: 99
pubkey: 'test_pubkey'
signature_node: 'test_sig_node'
signature_farmer: 'test_sig_farmer'
}
assert node.nodegroupid == 456
assert node.country == 'US'
assert node.uptime == 99
}