Merge branch 'development_actions007' of github.com:freeflowuniverse/herolib into development_actions007

This commit is contained in:
Timur Gordon
2025-03-26 19:27:57 +01:00
375 changed files with 8342 additions and 9011 deletions

1
.gitignore vendored
View File

@@ -44,3 +44,4 @@ compile_results.log
tmp
compile_summary.log
.summary_lock
.aider*

View File

@@ -1,26 +1,29 @@
module developer
import freeflowuniverse.herolib.mcp
@[heap]
pub struct Developer {}
pub fn result_to_mcp_tool_contents[T](result T) []mcp.ToolContent {
return [result_to_mcp_tool_content(result)]
}
pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
return $if T is string {
mcp.ToolContent{
mcp.ToolContent
{
typ: 'text'
text: result.str()
}
} $else $if T is int {
mcp.ToolContent{
mcp.ToolContent
{
typ: 'number'
number: result.int()
}
} $else $if T is bool {
mcp.ToolContent{
mcp.ToolContent
{
typ: 'boolean'
boolean: result.bool()
}
@@ -29,7 +32,8 @@ pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
for item in result {
items << result_to_mcp_tool_content(item)
}
return mcp.ToolContent{
return mcp.ToolContent
{
typ: 'array'
items: items
}
@@ -38,7 +42,8 @@ pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
$for field in T.fields {
properties[field.name] = result_to_mcp_tool_content(result.$(field.name))
}
return mcp.ToolContent{
return mcp.ToolContent
{
typ: 'object'
properties: properties
}

View File

@@ -2,10 +2,17 @@ module developer
import freeflowuniverse.herolib.core.code
import freeflowuniverse.herolib.mcp
import os
// create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists.
// returns an MCP Tool code in v for attaching the function to the mcp server
pub fn (d &Developer) create_mcp_tool_code(function_name string, module_path string) !string {
println('DEBUG: Looking for function ${function_name} in module path: ${module_path}')
if !os.exists(module_path) {
println('DEBUG: Module path does not exist: ${module_path}')
return error('Module path does not exist: ${module_path}')
}
function_ := get_function_from_module(module_path, function_name)!
println('Function string found:\n${function_}')

View File

@@ -1,7 +1,7 @@
module developer
import freeflowuniverse.herolib.mcp
import x.json2 {Any}
import x.json2
pub fn result_to_mcp_tool_contents[T](result T) []mcp.ToolContent {
return [result_to_mcp_tool_content(result)]

View File

@@ -0,0 +1,205 @@
module developer
import freeflowuniverse.herolib.mcp
import json
import os
// fn test_parse_struct_fields() {
// // Test case 1: Simple struct with primitive types
// simple_struct := 'pub struct User {
// name string
// age int
// active bool
// }'
// fields := parse_struct_fields(simple_struct)
// assert fields.len == 3
// assert fields['name'] == 'string'
// assert fields['age'] == 'int'
// assert fields['active'] == 'bool'
// // Test case 2: Struct with pub: and mut: sections
// complex_struct := 'pub struct Config {
// pub:
// host string
// port int
// mut:
// connected bool
// retries int
// }'
// fields2 := parse_struct_fields(complex_struct)
// assert fields2.len == 4
// assert fields2['host'] == 'string'
// assert fields2['port'] == 'int'
// assert fields2['connected'] == 'bool'
// assert fields2['retries'] == 'int'
// // Test case 3: Struct with attributes and comments
// struct_with_attrs := 'pub struct ApiResponse {
// // User ID
// id int
// // User full name
// name string @[json: "full_name"]
// // Whether account is active
// active bool
// }'
// fields3 := parse_struct_fields(struct_with_attrs)
// assert fields3.len == 3 // All fields are included
// assert fields3['id'] == 'int'
// assert fields3['active'] == 'bool'
// // Test case 4: Empty struct
// empty_struct := 'pub struct Empty {}'
// fields4 := parse_struct_fields(empty_struct)
// assert fields4.len == 0
// println('test_parse_struct_fields passed')
// }
// fn test_create_mcp_tool_input_schema() {
// d := Developer{}
// // Test case 1: Primitive types
// string_schema := d.create_mcp_tool_input_schema('string') or { panic(err) }
// assert string_schema.typ == 'string'
// int_schema := d.create_mcp_tool_input_schema('int') or { panic(err) }
// assert int_schema.typ == 'integer'
// float_schema := d.create_mcp_tool_input_schema('float') or { panic(err) }
// assert float_schema.typ == 'number'
// bool_schema := d.create_mcp_tool_input_schema('bool') or { panic(err) }
// assert bool_schema.typ == 'boolean'
// // Test case 2: Array type
// array_schema := d.create_mcp_tool_input_schema('[]string') or { panic(err) }
// assert array_schema.typ == 'array'
// // In our implementation, arrays don't have items directly in the schema
// // Test case 3: Struct type
// struct_def := 'pub struct Person {
// name string
// age int
// }'
// struct_schema := d.create_mcp_tool_input_schema(struct_def) or { panic(err) }
// assert struct_schema.typ == 'object'
// assert struct_schema.properties.len == 2
// assert struct_schema.properties['name'].typ == 'string'
// assert struct_schema.properties['age'].typ == 'integer'
// println('test_create_mcp_tool_input_schema passed')
// }
// fn test_create_mcp_tool() {
// d := Developer{}
// // Test case 1: Simple function with primitive types
// simple_fn := '// Get user by ID
// // Returns user information
// pub fn get_user(id int, include_details bool) {
// // Implementation
// }'
// tool1 := d.create_mcp_tool(simple_fn, {}) or { panic(err) }
// assert tool1.name == 'get_user'
// expected_desc1 := 'Get user by ID\nReturns user information'
// assert tool1.description == expected_desc1
// assert tool1.input_schema.typ == 'object'
// assert tool1.input_schema.properties.len == 2
// assert tool1.input_schema.properties['id'].typ == 'integer'
// assert tool1.input_schema.properties['include_details'].typ == 'boolean'
// assert tool1.input_schema.required.len == 2
// assert 'id' in tool1.input_schema.required
// assert 'include_details' in tool1.input_schema.required
// // Test case 2: Method with receiver
// method_fn := '// Update user profile
// pub fn (u User) update_profile(name string, age int) bool {
// // Implementation
// return true
// }'
// tool2 := d.create_mcp_tool(method_fn, {}) or { panic(err) }
// assert tool2.name == 'update_profile'
// assert tool2.description == 'Update user profile'
// assert tool2.input_schema.properties.len == 2
// assert tool2.input_schema.properties['name'].typ == 'string'
// assert tool2.input_schema.properties['age'].typ == 'integer'
// // Test case 3: Function with complex types
// complex_fn := '// Create new configuration
// // Sets up system configuration
// fn create_config(name string, settings Config) !Config {
// // Implementation
// }'
// config_struct := 'pub struct Config {
// server_url string
// max_retries int
// timeout float
// }'
// tool3 := d.create_mcp_tool(complex_fn, {
// 'Config': config_struct
// }) or { panic(err) }
// assert tool3.name == 'create_config'
// expected_desc3 := 'Create new configuration\nSets up system configuration'
// assert tool3.description == expected_desc3
// assert tool3.input_schema.properties.len == 2
// assert tool3.input_schema.properties['name'].typ == 'string'
// assert tool3.input_schema.properties['settings'].typ == 'object'
// // Test case 4: Function with no parameters
// no_params_fn := '// Initialize system
// pub fn initialize() {
// // Implementation
// }'
// tool4 := d.create_mcp_tool(no_params_fn, {}) or { panic(err) }
// assert tool4.name == 'initialize'
// assert tool4.description == 'Initialize system'
// assert tool4.input_schema.properties.len == 0
// assert tool4.input_schema.required.len == 0
// println('test_create_mcp_tool passed')
// }
// fn test_create_mcp_tool_code() {
// d := Developer{}
// // Test with the complex function that has struct parameters and return type
// module_path := "${os.dir(@FILE)}/testdata/mock_module"
// function_name := 'test_function'
// code := d.create_mcp_tool_code(function_name, module_path) or {
// panic('Failed to create MCP tool code: ${err}')
// }
// // Print the code instead of panic for debugging
// println('Generated code:')
// println('----------------------------------------')
// println(code)
// println('----------------------------------------')
// // Verify the generated code contains the expected elements
// assert code.contains('test_function_tool')
// assert code.contains('TestConfig')
// assert code.contains('TestResult')
// // Test with a simple function that has primitive types
// simple_function_name := 'simple_function'
// simple_code := d.create_mcp_tool_code(simple_function_name, module_path) or {
// panic('Failed to create MCP tool code for simple function: ${err}')
// }
// // Verify the simple function code
// assert simple_code.contains('simple_function_tool')
// assert simple_code.contains('name string')
// assert simple_code.contains('count int')
// // println('test_create_mcp_tool_code passed')
// }

View File

@@ -10,21 +10,24 @@ const create_mcp_tool_code_tool = mcp.Tool{
returns an MCP Tool code in v for attaching the function to the mcp server'
input_schema: mcp.ToolInputSchema{
typ: 'object'
properties: {'function_name': mcp.ToolProperty{
properties: {
'function_name': mcp.ToolProperty{
typ: 'string'
items: mcp.ToolItems{
typ: ''
enum: []
}
enum: []
}, 'module_path': mcp.ToolProperty{
}
'module_path': mcp.ToolProperty{
typ: 'string'
items: mcp.ToolItems{
typ: ''
enum: []
}
enum: []
}}
}
}
required: ['function_name', 'module_path']
}
}
@@ -62,16 +65,13 @@ const create_mcp_tool_tool = mcp.Tool{
pub fn (d &Developer) create_mcp_tool_tool_handler(arguments map[string]Any) !mcp.ToolCallResult {
function := arguments['function'].str()
types := json.decode[map[string]string](arguments['types'].str())!
result := d.create_mcp_tool(function, types) or {
return mcp.error_tool_call_result(err)
}
result := d.create_mcp_tool(function, types) or { return mcp.error_tool_call_result(err) }
return mcp.ToolCallResult{
is_error: false
content: result_to_mcp_tool_contents[string](result.str())
}
}
// Tool definition for the create_mcp_tool_handler function
const create_mcp_tool_handler_tool = mcp.Tool{
name: 'create_mcp_tool_handler'
@@ -81,10 +81,10 @@ const create_mcp_tool_handler_tool = mcp.Tool{
properties: {
'function': mcp.ToolProperty{
typ: 'string'
},
}
'types': mcp.ToolProperty{
typ: 'object'
},
}
'result': mcp.ToolProperty{
typ: 'string'
}

31
TOSORT/developer/mcp.v Normal file
View File

@@ -0,0 +1,31 @@
module developer
import freeflowuniverse.herolib.mcp.logger
import freeflowuniverse.herolib.mcp
import freeflowuniverse.herolib.schemas.jsonrpc
// pub fn new_mcp_server(d &Developer) !&mcp.Server {
// logger.info('Creating new Developer MCP server')
// // Initialize the server with the empty handlers map
// mut server := mcp.new_server(mcp.MemoryBackend{
// tools: {
// 'create_mcp_tool': create_mcp_tool_tool
// 'create_mcp_tool_handler': create_mcp_tool_handler_tool
// 'create_mcp_tool_code': create_mcp_tool_code_tool
// }
// tool_handlers: {
// 'create_mcp_tool': d.create_mcp_tool_tool_handler
// 'create_mcp_tool_handler': d.create_mcp_tool_handler_tool_handler
// 'create_mcp_tool_code': d.create_mcp_tool_code_tool_handler
// }
// }, mcp.ServerParams{
// config: mcp.ServerConfiguration{
// server_info: mcp.ServerInfo{
// name: 'developer'
// version: '1.0.0'
// }
// }
// })!
// return server
// }

View File

@@ -5,7 +5,6 @@ import freeflowuniverse.herolib.mcp.logger
mut server := developer.new_mcp_server(&developer.Developer{})!
server.start() or {
logger.fatal('Error starting server: $err')
logger.fatal('Error starting server: ${err}')
exit(1)
}

View File

@@ -12,13 +12,13 @@ fn get_type_from_module(module_path string, type_name string) !string {
}
for v_file in v_files {
content := os.read_file(v_file) or {
return error('Failed to read file ${v_file}: ${err}')
}
content := os.read_file(v_file) or { return error('Failed to read file ${v_file}: ${err}') }
type_str := 'struct ${type_name} {'
i := content.index(type_str) or { -1 }
if i == -1 { continue }
if i == -1 {
continue
}
start_i := i + type_str.len
closing_i := find_closing_brace(content, start_i) or {
@@ -49,9 +49,7 @@ fn find_closing_brace(content string, start_i int) ?int {
// Helper function to list V files
fn list_v_files(dir string) ![]string {
files := os.ls(dir) or {
return error('Error listing directory: $err')
}
files := os.ls(dir) or { return error('Error listing directory: ${err}') }
mut v_files := []string{}
for file in files {
@@ -71,7 +69,7 @@ fn create_test_files() !(string, string, string) {
test_file_path := os.join_path(test_dir, 'test_type.v')
// Create a test file with a simple struct
test_content := "module test_module
test_content := 'module test_module
struct TestType {
name string
@@ -83,14 +81,14 @@ struct TestType {
struct OtherType {
id string
}
"
'
os.write_file(test_file_path, test_content) or {
eprintln('Failed to create test file: $err')
return error('Failed to create test file: $err')
eprintln('Failed to create test file: ${err}')
return error('Failed to create test file: ${err}')
}
// Create a test file with a nested struct
nested_test_content := "module test_module
nested_test_content := 'module test_module
struct NestedType {
config map[string]string {
@@ -101,11 +99,11 @@ struct NestedType {
value string
}
}
"
'
nested_test_file := os.join_path(test_dir, 'nested_test.v')
os.write_file(nested_test_file, nested_test_content) or {
eprintln('Failed to create nested test file: $err')
return error('Failed to create nested test file: $err')
eprintln('Failed to create nested test file: ${err}')
return error('Failed to create nested test file: ${err}')
}
return test_dir, test_file_path, nested_test_file
@@ -115,21 +113,21 @@ struct NestedType {
fn test_get_type_from_module() {
// Create test files
test_dir, test_file_path, nested_test_file := create_test_files() or {
eprintln('Failed to create test files: $err')
eprintln('Failed to create test files: ${err}')
assert false
return
}
// Test case 1: Get a simple struct
type_content := get_type_from_module(test_dir, 'TestType') or {
eprintln('Failed to get type: $err')
eprintln('Failed to get type: ${err}')
assert false
return
}
// Verify the content matches what we expect
expected := "\n\tname string\n\tage int\n\tactive bool\n}"
assert type_content == expected, 'Expected: "$expected", got: "$type_content"'
expected := '\n\tname string\n\tage int\n\tactive bool\n}'
assert type_content == expected, 'Expected: "${expected}", got: "${type_content}"'
// Test case 2: Try to get a non-existent type
non_existent := get_type_from_module(test_dir, 'NonExistentType') or {
@@ -141,13 +139,13 @@ fn test_get_type_from_module() {
// Test case 3: Test with nested braces in the struct
nested_type_content := get_type_from_module(test_dir, 'NestedType') or {
eprintln('Failed to get nested type: $err')
eprintln('Failed to get nested type: ${err}')
assert false
return
}
expected_nested := "\n\tconfig map[string]string {\n\t\trequired: true\n\t}\n\tdata []struct {\n\t\tkey string\n\t\tvalue string\n\t}\n}"
assert nested_type_content == expected_nested, 'Expected: "$expected_nested", got: "$nested_type_content"'
expected_nested := '\n\tconfig map[string]string {\n\t\trequired: true\n\t}\n\tdata []struct {\n\t\tkey string\n\t\tvalue string\n\t}\n}'
assert nested_type_content == expected_nested, 'Expected: "${expected_nested}", got: "${nested_type_content}"'
// Clean up test files
os.rm(test_file_path) or {}

View File

@@ -7,7 +7,7 @@ import log
fn get_module_dir(mod string) string {
module_parts := mod.trim_string_left('freeflowuniverse.herolib').split('.')
return '${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/${module_parts.join("/")}'
return '${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/${module_parts.join('/')}'
}
// given a module path and a type name, returns the type definition of that type within that module
@@ -20,10 +20,7 @@ fn get_type_from_module(module_path string, type_name string) !string {
for v_file in v_files {
println('Checking file: ${v_file}')
content := os.read_file(v_file) or {
return error('Failed to read file ${v_file}: ${err}')
}
content := os.read_file(v_file) or { return error('Failed to read file ${v_file}: ${err}') }
// Look for both regular and pub struct declarations
mut type_str := 'struct ${type_name} {'
@@ -38,7 +35,8 @@ fn get_type_from_module(module_path string, type_name string) !string {
}
if i == -1 {
type_import := content.split_into_lines().filter(it.contains('import') && it.contains(type_name))
type_import := content.split_into_lines().filter(it.contains('import')
&& it.contains(type_name))
if type_import.len > 0 {
log.debug('debugzoooo')
mod := type_import[0].trim_space().trim_string_left('import ').all_before(' ')
@@ -134,9 +132,6 @@ fn find_closing_brace(content string, start_i int) ?int {
return none
}
// get_function_from_file parses a V file and extracts a specific function block including its comments
// ARGS:
// file_path string - path to the V file
@@ -167,8 +162,8 @@ fn get_function_from_file(file_path string, function_name string) !string {
}
// Check if we found the function
if !in_function && (trimmed.starts_with('fn ${function_name}(') ||
trimmed.starts_with('pub fn ${function_name}(')) {
if !in_function && (trimmed.starts_with('fn ${function_name}(')
|| trimmed.starts_with('pub fn ${function_name}(')) {
in_function = true
// Add collected comments
result << comment_block
@@ -213,9 +208,7 @@ fn get_function_from_file(file_path string, function_name string) !string {
// list_v_files returns all .v files in a directory (non-recursive), excluding generated files ending with _.v
fn list_v_files(dir string) ![]string {
files := os.ls(dir) or {
return error('Error listing directory: $err')
}
files := os.ls(dir) or { return error('Error listing directory: ${err}') }
mut v_files := []string{}
for file in files {
@@ -230,12 +223,12 @@ fn list_v_files(dir string) ![]string {
// test runs v test on the specified file or directory
pub fn vtest(fullpath string) !string {
logger.info('test $fullpath')
logger.info('test ${fullpath}')
if !os.exists(fullpath) {
return error('File or directory does not exist: $fullpath')
return error('File or directory does not exist: ${fullpath}')
}
if os.is_dir(fullpath) {
mut results:=""
mut results := ''
for item in list_v_files(fullpath)! {
results += vtest(item)!
results += '\n-----------------------\n'
@@ -243,34 +236,31 @@ pub fn vtest(fullpath string) !string {
return results
} else {
cmd := 'v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test ${fullpath}'
logger.debug('Executing command: $cmd')
logger.debug('Executing command: ${cmd}')
result := os.execute(cmd)
if result.exit_code != 0 {
return error('Test failed for $fullpath with exit code ${result.exit_code}\n${result.output}')
return error('Test failed for ${fullpath} with exit code ${result.exit_code}\n${result.output}')
} else {
logger.info('Test completed for $fullpath')
logger.info('Test completed for ${fullpath}')
}
return 'Command: $cmd\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
return 'Command: ${cmd}\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
}
}
// vvet runs v vet on the specified file or directory
pub fn vvet(fullpath string) !string {
logger.info('vet $fullpath')
logger.info('vet ${fullpath}')
if !os.exists(fullpath) {
return error('File or directory does not exist: $fullpath')
return error('File or directory does not exist: ${fullpath}')
}
if os.is_dir(fullpath) {
mut results := ""
files := list_v_files(fullpath) or {
return error('Error listing V files: $err')
}
mut results := ''
files := list_v_files(fullpath) or { return error('Error listing V files: ${err}') }
for file in files {
results += vet_file(file) or {
logger.error('Failed to vet $file: $err')
return error('Failed to vet $file: $err')
logger.error('Failed to vet ${file}: ${err}')
return error('Failed to vet ${file}: ${err}')
}
results += '\n-----------------------\n'
}
@@ -283,14 +273,14 @@ pub fn vvet(fullpath string) !string {
// vet_file runs v vet on a single file
fn vet_file(file string) !string {
cmd := 'v vet -v -w ${file}'
logger.debug('Executing command: $cmd')
logger.debug('Executing command: ${cmd}')
result := os.execute(cmd)
if result.exit_code != 0 {
return error('Vet failed for $file with exit code ${result.exit_code}\n${result.output}')
return error('Vet failed for ${file} with exit code ${result.exit_code}\n${result.output}')
} else {
logger.info('Vet completed for $file')
logger.info('Vet completed for ${file}')
}
return 'Command: $cmd\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
return 'Command: ${cmd}\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
}
// cmd := 'v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc ${fullpath}'

View File

@@ -14,7 +14,7 @@ fn create_test_files() !(string, string, string) {
test_file_path := os.join_path(test_dir, 'test_type.v')
// Create a test file with a simple struct
test_content := "module test_module
test_content := 'module test_module
struct TestType {
name string
@@ -26,14 +26,14 @@ struct TestType {
struct OtherType {
id string
}
"
'
os.write_file(test_file_path, test_content) or {
eprintln('Failed to create test file: $err')
return error('Failed to create test file: $err')
eprintln('Failed to create test file: ${err}')
return error('Failed to create test file: ${err}')
}
// Create a test file with a nested struct
nested_test_content := "module test_module
nested_test_content := 'module test_module
struct NestedType {
config map[string]string {
@@ -44,11 +44,11 @@ struct NestedType {
value string
}
}
"
'
nested_test_file := os.join_path(test_dir, 'nested_test.v')
os.write_file(nested_test_file, nested_test_content) or {
eprintln('Failed to create nested test file: $err')
return error('Failed to create nested test file: $err')
eprintln('Failed to create nested test file: ${err}')
return error('Failed to create nested test file: ${err}')
}
return test_dir, test_file_path, nested_test_file
@@ -58,21 +58,21 @@ struct NestedType {
fn test_get_type_from_module() {
// Create test files
test_dir, test_file_path, nested_test_file := create_test_files() or {
eprintln('Failed to create test files: $err')
eprintln('Failed to create test files: ${err}')
assert false
return
}
// Test case 1: Get a simple struct
type_content := get_type_from_module(test_dir, 'TestType') or {
eprintln('Failed to get type: $err')
eprintln('Failed to get type: ${err}')
assert false
return
}
// Verify the content matches what we expect
expected := "\n\tname string\n\tage int\n\tactive bool\n}"
assert type_content == expected, 'Expected: "$expected", got: "$type_content"'
expected := '\n\tname string\n\tage int\n\tactive bool\n}'
assert type_content == expected, 'Expected: "${expected}", got: "${type_content}"'
// Test case 2: Try to get a non-existent type
non_existent := get_type_from_module(test_dir, 'NonExistentType') or {
@@ -84,13 +84,13 @@ fn test_get_type_from_module() {
// Test case 3: Test with nested braces in the struct
nested_type_content := get_type_from_module(test_dir, 'NestedType') or {
eprintln('Failed to get nested type: $err')
eprintln('Failed to get nested type: ${err}')
assert false
return
}
expected_nested := "\n\tconfig map[string]string {\n\t\trequired: true\n\t}\n\tdata []struct {\n\t\tkey string\n\t\tvalue string\n\t}\n}"
assert nested_type_content == expected_nested, 'Expected: "$expected_nested", got: "$nested_type_content"'
expected_nested := '\n\tconfig map[string]string {\n\t\trequired: true\n\t}\n\tdata []struct {\n\t\tkey string\n\t\tvalue string\n\t}\n}'
assert nested_type_content == expected_nested, 'Expected: "${expected_nested}", got: "${nested_type_content}"'
// Clean up test files
os.rm(test_file_path) or {}

View File

@@ -0,0 +1,34 @@
module developer
import freeflowuniverse.herolib.mcp
const get_function_from_file_tool = mcp.Tool{
name: 'get_function_from_file'
description: 'get_function_from_file parses a V file and extracts a specific function block including its comments
ARGS:
file_path string - path to the V file
function_name string - name of the function to extract
RETURNS: string - the function block including comments, or empty string if not found'
input_schema: mcp.ToolInputSchema{
typ: 'object'
properties: {
'file_path': mcp.ToolProperty{
typ: 'string'
items: mcp.ToolItems{
typ: ''
enum: []
}
enum: []
}
'function_name': mcp.ToolProperty{
typ: 'string'
items: mcp.ToolItems{
typ: ''
enum: []
}
enum: []
}
}
required: ['file_path', 'function_name']
}
}

View File

@@ -56,7 +56,7 @@ fn main() {
jina.TrainingExample{
text: 'The movie was okay.'
label: 'neutral'
}
},
]
training_result := client.train(examples, model, 'private') or {

View File

@@ -12,8 +12,7 @@ const openrpc_spec_path = os.join_path(example_dir, 'openrpc.json')
openrpc_spec := openrpc.new(path: openrpc_spec_path)!
actor_spec := specification.from_openrpc(openrpc_spec)!
actor_module := generator.generate_actor_module(
actor_spec,
actor_module := generator.generate_actor_module(actor_spec,
interfaces: [.openrpc]
)!

View File

@@ -5,7 +5,6 @@ import freeflowuniverse.herolib.baobab.specification
import freeflowuniverse.herolib.schemas.openapi
import os
const example_dir = os.dir(@FILE)
const specs = ['merchant', 'profiler', 'farmer']
@@ -13,8 +12,7 @@ for spec in specs {
openapi_spec_path := os.join_path(example_dir, '${spec}.json')
openapi_spec := openapi.new(path: openapi_spec_path, process: true)!
actor_spec := specification.from_openapi(openapi_spec)!
actor_module := generator.generate_actor_folder(
actor_spec,
actor_module := generator.generate_actor_folder(actor_spec,
interfaces: [.openapi, .http]
)!
actor_module.write(example_dir,

View File

@@ -3,7 +3,7 @@ module geomind_poc
import freeflowuniverse.crystallib.core.playbook { PlayBook }
// play_commerce processes heroscript actions for the commerce system
pub fn play_commerce(mut plbook playbook.PlayBook) ! {
pub fn play_commerce(mut plbook PlayBook) ! {
commerce_actions := plbook.find(filter: 'commerce.')!
mut c := Commerce{}
@@ -12,8 +12,8 @@ pub fn play_commerce(mut plbook playbook.PlayBook) ! {
'merchant' {
mut p := action.params
merchant := c.create_merchant(
name: p.get('name')!,
description: p.get_default('description', '')!,
name: p.get('name')!
description: p.get_default('description', '')!
contact: p.get('contact')!
)!
println('Created merchant: ${merchant.name}')
@@ -21,10 +21,10 @@ pub fn play_commerce(mut plbook playbook.PlayBook) ! {
'component' {
mut p := action.params
component := c.create_product_component_template(
name: p.get('name')!,
description: p.get_default('description', '')!,
specs: p.get_map(),
price: p.get_float('price')!,
name: p.get('name')!
description: p.get_default('description', '')!
specs: p.get_map()
price: p.get_float('price')!
currency: p.get('currency')!
)!
println('Created component: ${component.name}')
@@ -50,10 +50,10 @@ pub fn play_commerce(mut plbook playbook.PlayBook) ! {
}
template := c.create_product_template(
name: p.get('name')!,
description: p.get_default('description', '')!,
components: components,
merchant_id: p.get('merchant_id')!,
name: p.get('name')!
description: p.get_default('description', '')!
components: components
merchant_id: p.get('merchant_id')!
category: p.get_default('category', 'General')!
)!
println('Created template: ${template.name}')
@@ -61,8 +61,8 @@ pub fn play_commerce(mut plbook playbook.PlayBook) ! {
'product' {
mut p := action.params
product := c.create_product(
template_id: p.get('template_id')!,
merchant_id: p.get('merchant_id')!,
template_id: p.get('template_id')!
merchant_id: p.get('merchant_id')!
stock_quantity: p.get_int('stock_quantity')!
)!
println('Created product: ${product.name} with stock: ${product.stock_quantity}')
@@ -88,7 +88,7 @@ pub fn play_commerce(mut plbook playbook.PlayBook) ! {
}
order := c.create_order(
customer_id: p.get('customer_id')!,
customer_id: p.get('customer_id')!
items: items
)!
println('Created order: ${order.id} with ${order.items.len} items')
@@ -96,7 +96,7 @@ pub fn play_commerce(mut plbook playbook.PlayBook) ! {
'update_order' {
mut p := action.params
order := c.update_order_status(
order_id: p.get('order_id')!,
order_id: p.get('order_id')!
new_status: p.get('status')!
)!
println('Updated order ${order.id} status to: ${order.status}')

View File

@@ -1,6 +1,7 @@
module geomind_poc
import crypto.rand
import time
// Commerce represents the main e-commerce server handling all operations
pub struct Commerce {

View File

@@ -5,15 +5,16 @@ import freeflowuniverse.herolib.baobab.specification
import freeflowuniverse.herolib.schemas.openapi
import os
const example_dir = os.join_path('${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/circles/mcc', 'baobab')
const openapi_spec_path = os.join_path('${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/circles/mcc', 'openapi.json')
const example_dir = os.join_path('${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/circles/mcc',
'baobab')
const openapi_spec_path = os.join_path('${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/circles/mcc',
'openapi.json')
// the actor specification obtained from the OpenRPC Specification
openapi_spec := openapi.new(path: openapi_spec_path)!
actor_spec := specification.from_openapi(openapi_spec)!
actor_module := generator.generate_actor_module(
actor_spec,
actor_module := generator.generate_actor_module(actor_spec,
interfaces: [.openapi, .http]
)!

View File

@@ -14,8 +14,7 @@ actor_spec := specification.from_openapi(openapi_spec)!
println(actor_spec)
actor_module := generator.generate_actor_module(
actor_spec,
actor_module := generator.generate_actor_module(actor_spec,
interfaces: [.openapi, .http]
)!

View File

@@ -30,7 +30,7 @@ const actor_specification = specification.ActorSpecification{
schema: jsonschema.SchemaRef(jsonschema.Schema{
typ: 'integer'
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pet_list'
@@ -50,7 +50,7 @@ const actor_specification = specification.ActorSpecification{
schema: jsonschema.SchemaRef(jsonschema.Reference{
ref: '#/components/schemas/NewPet'
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pet'
@@ -70,7 +70,7 @@ const actor_specification = specification.ActorSpecification{
schema: jsonschema.SchemaRef(jsonschema.Schema{
typ: 'integer'
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pet'
@@ -90,7 +90,7 @@ const actor_specification = specification.ActorSpecification{
schema: jsonschema.SchemaRef(jsonschema.Schema{
typ: 'integer'
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pet'
@@ -99,7 +99,7 @@ const actor_specification = specification.ActorSpecification{
typ: 'null'
})
}
}
},
]
}

View File

@@ -32,7 +32,7 @@ const actor_specification = specification.ActorSpecification{
schema: jsonschema.SchemaRef(jsonschema.Schema{
typ: 'integer'
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pet_list'
@@ -52,7 +52,7 @@ const actor_specification = specification.ActorSpecification{
schema: jsonschema.SchemaRef(jsonschema.Reference{
ref: '#/components/schemas/NewPet'
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pet'
@@ -72,7 +72,7 @@ const actor_specification = specification.ActorSpecification{
schema: jsonschema.SchemaRef(jsonschema.Schema{
typ: 'integer'
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pet'
@@ -92,7 +92,7 @@ const actor_specification = specification.ActorSpecification{
schema: jsonschema.SchemaRef(jsonschema.Schema{
typ: 'integer'
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pet'
@@ -101,7 +101,7 @@ const actor_specification = specification.ActorSpecification{
typ: 'null'
})
}
}
},
]
}

View File

@@ -8,13 +8,10 @@ const build_path = os.join_path(os.dir(@FILE), '/docusaurus')
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
mut model := bizmodel.generate("test", playbook_path)!
mut model := bizmodel.generate('test', playbook_path)!
println(model.sheet)
println(model.sheet.export()!)
model.sheet.export(path:"~/Downloads/test.csv")!
model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!
model.sheet.export(path: '~/Downloads/test.csv')!
model.sheet.export(path: '~/code/github/freeflowuniverse/starlight_template/src/content/test.csv')!

View File

@@ -12,7 +12,7 @@ const build_path = os.join_path(os.dir(@FILE), '/docusaurus')
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
mut model := bizmodel.getset("example")!
mut model := bizmodel.getset('example')!
model.workdir = build_path
model.play(mut playbook.new(path: playbook_path)!)!
@@ -22,9 +22,6 @@ println(model.sheet.export()!)
// model.sheet.export(path:"~/Downloads/test.csv")!
// model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!
report := model.new_report(
name: 'example_report'
title: 'Example Business Model'

View File

@@ -31,7 +31,7 @@ fn main() {
openai.Message{
role: .user
content: 'What are the key differences between Groq and other AI inference providers?'
}
},
]
})!

256
examples/clients/qdrant_example.vsh Normal file → Executable file
View File

@@ -1,209 +1,85 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.clients.qdrant
import os
import flag
import freeflowuniverse.herolib.core.httpconnection
import rand
mut fp := flag.new_flag_parser(os.args)
fp.application('qdrant_example.vsh')
fp.version('v0.1.0')
fp.description('Example script demonstrating Qdrant client usage')
fp.skip_executable()
// 1. Get the qdrant client
mut qdrant_client := qdrant.get()!
help_requested := fp.bool('help', `h`, false, 'Show help message')
// 2. Generate collection name
if help_requested {
println(fp.usage())
exit(0)
}
collection_name := 'collection_' + rand.string(4)
additional_args := fp.finalize() or {
eprintln(err)
println(fp.usage())
exit(1)
}
// 2. Create a new collection
// Initialize Qdrant client
mut client := qdrant.get(name: 'default') or {
// If client doesn't exist, create a new one
mut new_client := qdrant.QdrantClient{
name: 'default'
url: 'http://localhost:6333'
}
qdrant.set(new_client) or {
eprintln('Failed to set Qdrant client: ${err}')
exit(1)
}
new_client
}
println('Connected to Qdrant at ${client.url}')
// Check if Qdrant is healthy
is_healthy := client.health_check() or {
eprintln('Failed to check Qdrant health: ${err}')
exit(1)
}
if !is_healthy {
eprintln('Qdrant is not healthy')
exit(1)
}
println('Qdrant is healthy')
// Get service info
service_info := client.get_service_info() or {
eprintln('Failed to get service info: ${err}')
exit(1)
}
println('Qdrant version: ${service_info.version}')
// Collection name for our example
collection_name := 'example_collection'
// Check if collection exists and delete it if it does
collections := client.list_collections() or {
eprintln('Failed to list collections: ${err}')
exit(1)
}
if collection_name in collections.result {
println('Collection ${collection_name} already exists, deleting it...')
client.delete_collection(collection_name: collection_name) or {
eprintln('Failed to delete collection: ${err}')
exit(1)
}
println('Collection deleted')
}
// Create a new collection
println('Creating collection ${collection_name}...')
vectors_config := qdrant.VectorsConfig{
size: 4 // Small size for example purposes
distance: .cosine
}
client.create_collection(
created_collection := qdrant_client.create_collection(
collection_name: collection_name
vectors: vectors_config
) or {
eprintln('Failed to create collection: ${err}')
exit(1)
}
size: 15
distance: 'Cosine'
)!
println('Collection created')
println('Created Collection: ${created_collection}')
// Upsert some points
println('Upserting points...')
points := [
qdrant.PointStruct{
id: '1'
vector: [f32(0.1), 0.2, 0.3, 0.4]
payload: {
'color': 'red'
'category': 'furniture'
'name': 'chair'
}
},
qdrant.PointStruct{
id: '2'
vector: [f32(0.2), 0.3, 0.4, 0.5]
payload: {
'color': 'blue'
'category': 'electronics'
'name': 'laptop'
}
},
qdrant.PointStruct{
id: '3'
vector: [f32(0.3), 0.4, 0.5, 0.6]
payload: {
'color': 'green'
'category': 'food'
'name': 'apple'
}
}
// 3. Get the created collection
get_collection := qdrant_client.get_collection(
collection_name: collection_name
)!
println('Get Collection: ${get_collection}')
// 4. Delete the created collection
// deleted_collection := qdrant_client.delete_collection(
// collection_name: collection_name
// )!
// println('Deleted Collection: ${deleted_collection}')
// 5. List all collections
list_collection := qdrant_client.list_collections()!
println('List Collection: ${list_collection}')
// 6. Check collection existence
collection_existence := qdrant_client.is_collection_exists(
collection_name: collection_name
)!
println('Collection Existence: ${collection_existence}')
// 7. Retrieve points
collection_points := qdrant_client.retrieve_points(
collection_name: collection_name
ids: [
0,
3,
100,
]
)!
client.upsert_points(
println('Collection Points: ${collection_points}')
// 8. Upsert points
upsert_points := qdrant_client.upsert_points(
collection_name: collection_name
points: points
wait: true
) or {
eprintln('Failed to upsert points: ${err}')
exit(1)
points: [
qdrant.Point{
payload: {
'key': 'value'
}
println('Points upserted')
// Get collection info to verify points were added
collection_info := client.get_collection(collection_name: collection_name) or {
eprintln('Failed to get collection info: ${err}')
exit(1)
vector: [1.0, 2.0, 3.0]
},
qdrant.Point{
payload: {
'key': 'value'
}
println('Collection has ${collection_info.vectors_count} points')
// Search for points
println('Searching for points similar to [0.1, 0.2, 0.3, 0.4]...')
search_result := client.search(
collection_name: collection_name
vector: [f32(0.1), 0.2, 0.3, 0.4]
limit: 3
) or {
eprintln('Failed to search points: ${err}')
exit(1)
}
println('Search results:')
for i, point in search_result.result {
println(' ${i+1}. ID: ${point.id}, Score: ${point.score}')
if payload := point.payload {
println(' Name: ${payload['name']}')
println(' Category: ${payload['category']}')
println(' Color: ${payload['color']}')
}
}
// Search with filter
println('\nSearching for points with category "electronics"...')
filter := qdrant.Filter{
must: [
qdrant.FieldCondition{
key: 'category'
match: 'electronics'
vector: [4.0, 5.0, 6.0]
},
qdrant.Point{
payload: {
'key': 'value'
}
vector: [7.0, 8.0, 9.0]
},
]
}
)!
filtered_search := client.search(
collection_name: collection_name
vector: [f32(0.1), 0.2, 0.3, 0.4]
filter: filter
limit: 3
) or {
eprintln('Failed to search with filter: ${err}')
exit(1)
}
println('Filtered search results:')
for i, point in filtered_search.result {
println(' ${i+1}. ID: ${point.id}, Score: ${point.score}')
if payload := point.payload {
println(' Name: ${payload['name']}')
println(' Category: ${payload['category']}')
println(' Color: ${payload['color']}')
}
}
// Clean up - delete the collection
println('\nCleaning up - deleting collection...')
client.delete_collection(collection_name: collection_name) or {
eprintln('Failed to delete collection: ${err}')
exit(1)
}
println('Collection deleted')
println('Example completed successfully')
println('Upsert Points: ${upsert_points}')

View File

@@ -14,9 +14,7 @@ struct ExampleActor {
}
fn new() !ExampleActor {
return ExampleActor{
stage.new_actor('example')
}
return ExampleActor{stage.new_actor('example')}
}
pub fn run() ! {

View File

@@ -107,35 +107,47 @@ fn (mut actor Actor) handle_method(cmd string, data string) !string {
return json.encode(response)
}
'deletePet' {
params := json.decode(map[string]int, data) or { return error('Invalid params: $err') }
actor.data_store.delete_pet(params['petId']) or { return error('Pet not found: $err') }
return json.encode({'message': 'Pet deleted'})
params := json.decode(map[string]int, data) or {
return error('Invalid params: ${err}')
}
actor.data_store.delete_pet(params['petId']) or {
return error('Pet not found: ${err}')
}
return json.encode({
'message': 'Pet deleted'
})
}
'listOrders' {
orders := actor.data_store.list_orders()
return json.encode(orders)
}
'getOrder' {
params := json.decode(map[string]int, data) or { return error('Invalid params: $err') }
params := json.decode(map[string]int, data) or {
return error('Invalid params: ${err}')
}
order := actor.data_store.get_order(params['orderId']) or {
return error('Order not found: $err')
return error('Order not found: ${err}')
}
return json.encode(order)
}
'deleteOrder' {
params := json.decode(map[string]int, data) or { return error('Invalid params: $err') }
actor.data_store.delete_order(params['orderId']) or {
return error('Order not found: $err')
params := json.decode(map[string]int, data) or {
return error('Invalid params: ${err}')
}
return json.encode({'message': 'Order deleted'})
actor.data_store.delete_order(params['orderId']) or {
return error('Order not found: ${err}')
}
return json.encode({
'message': 'Order deleted'
})
}
'createUser' {
user := json.decode(NewUser, data) or { return error('Invalid user data: $err') }
user := json.decode(NewUser, data) or { return error('Invalid user data: ${err}') }
created_user := actor.data_store.create_user(user)
return json.encode(created_user)
}
else {
return error('Unknown method: $cmd')
return error('Unknown method: ${cmd}')
}
}
}

View File

@@ -6,4 +6,3 @@ mut db := qdrant_installer.get()!
db.install()!
db.start()!

View File

@@ -5,6 +5,4 @@ import freeflowuniverse.herolib.installers.lang.python as python_module
mut python_installer := python_module.get()!
python_installer.install()!
// python_installer.destroy()!

View File

@@ -32,9 +32,7 @@ additional_args := fp.finalize() or {
}
// Create a new HeroRunner instance
mut runner := model.new() or {
panic('Failed to create HeroRunner: ${err}')
}
mut runner := model.new() or { panic('Failed to create HeroRunner: ${err}') }
println('\n---------BEGIN VFS JOBS EXAMPLE')
@@ -55,17 +53,13 @@ for i in 0..create_count {
job.status.created.time = time.now().add_days(-(cleanup_days + 1))
}
runner.jobs.set(job) or {
panic('Failed to set job: ${err}')
}
runner.jobs.set(job) or { panic('Failed to set job: ${err}') }
println('Created job with GUID: ${job.guid}')
}
// List all jobs
println('\n---------LISTING ALL JOBS')
jobs := runner.jobs.list() or {
panic('Failed to list jobs: ${err}')
}
jobs := runner.jobs.list() or { panic('Failed to list jobs: ${err}') }
println('Found ${jobs.len} jobs:')
for job in jobs {
days_ago := (time.now().unix - job.status.created.time.unix) / (60 * 60 * 24)
@@ -75,16 +69,12 @@ for job in jobs {
// Clean up old jobs
println('\n---------CLEANING UP OLD JOBS')
println('Cleaning up jobs older than ${cleanup_days} days...')
deleted_count := runner.cleanup_jobs(cleanup_days) or {
panic('Failed to clean up jobs: ${err}')
}
deleted_count := runner.cleanup_jobs(cleanup_days) or { panic('Failed to clean up jobs: ${err}') }
println('Deleted ${deleted_count} old jobs')
// List remaining jobs
println('\n---------LISTING REMAINING JOBS')
remaining_jobs := runner.jobs.list() or {
panic('Failed to list jobs: ${err}')
}
remaining_jobs := runner.jobs.list() or { panic('Failed to list jobs: ${err}') }
println('Found ${remaining_jobs.len} remaining jobs:')
for job in remaining_jobs {
days_ago := (time.now().unix - job.status.created.time.unix) / (60 * 60 * 24)

View File

@@ -3,7 +3,6 @@
// Calendar Typescript Client Generation Example
// This example demonstrates how to generate a typescript client
// from a given OpenAPI Specification using the `openapi/codegen` module.
import os
import freeflowuniverse.herolib.schemas.openapi
import freeflowuniverse.herolib.schemas.openapi.codegen
@@ -15,5 +14,3 @@ const specification = openapi.new(path: '${dir}/meeting_api.json') or {
// generate typescript client folder and write it in dir
codegen.ts_client_folder(specification)!.write(dir, overwrite: true)!

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env -S v -gc none -cc tcc -d use_openssl -enable-globals -cg run
import freeflowuniverse.herolib.threefold.grid3.deployer
const gigabyte = u64(1024 * 1024 * 1024)
// We can use any of the parameters for the corresponding Grid Proxy query
// https://gridproxy.grid.tf/swagger/index.html#/GridProxy/get_nodes
filter := deployer.FilterNodesArgs{
size: 5
randomize: true
free_mru: 8 * gigabyte
free_sru: 50 * gigabyte
farm_name: 'FreeFarm'
status: 'up'
}
nodes := deployer.filter_nodes(filter)!
println(nodes)

View File

@@ -11,18 +11,18 @@ griddriver.install()!
v := tfgrid3deployer.get()!
println('cred: ${v}')
deployment_name := 'herzner_dep'
deployment_name := 'hetzner_dep'
mut deployment := tfgrid3deployer.new_deployment(deployment_name)!
// TODO: find a way to filter hetzner nodes
deployment.add_machine(
name: 'hetzner_vm'
cpu: 1
memory: 2
cpu: 2
memory: 5
planetary: false
public_ip4: true
public_ip4: false
size: 10 // 10 gig
mycelium: tfgrid3deployer.Mycelium{}
// mycelium: tfgrid3deployer.Mycelium{}
)
deployment.deploy()!

View File

@@ -13,8 +13,8 @@ fn main() {
// mut deployment := tfgrid3deployer.get_deployment(deployment_name)!
deployment.add_machine(
name: 'my_vm1'
cpu: 1
memory: 2
cpu: 2
memory: 4
planetary: false
public_ip4: false
nodes: [167]
@@ -32,10 +32,10 @@ fn main() {
deployment.add_webname(name: 'mywebname2', backend: 'http://37.27.132.47:8000')
deployment.deploy()!
deployment.remove_machine('my_vm1')!
deployment.remove_webname('mywebname2')!
deployment.remove_zdb('my_zdb')!
deployment.deploy()!
// deployment.remove_machine('my_vm1')!
// deployment.remove_webname('mywebname2')!
// deployment.remove_zdb('my_zdb')!
// deployment.deploy()!
tfgrid3deployer.delete_deployment(deployment_name)!
// tfgrid3deployer.delete_deployment(deployment_name)!
}

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env -S v -gc none -d use_openssl -enable-globals -cg run
#!/usr/bin/env -S v -gc none -cc tcc -d use_openssl -enable-globals -cg run
//#!/usr/bin/env -S v -gc none -cc tcc -d use_openssl -enable-globals -cg run
import freeflowuniverse.herolib.threefold.grid3.gridproxy
import freeflowuniverse.herolib.threefold.grid3.deployer
import freeflowuniverse.herolib.installers.threefold.griddriver

View File

@@ -11,9 +11,10 @@ pub struct VFSDedupeDB {
}
pub fn (mut db VFSDedupeDB) set(args ourdb.OurDBSetArgs) !u32 {
return db.store(args.data,
dedupestor.Reference{owner: u16(1), id: args.id or {panic('VFS Must provide id')}}
)!
return db.store(args.data, dedupestor.Reference{
owner: u16(1)
id: args.id or { panic('VFS Must provide id') }
})!
}
pub fn (mut db VFSDedupeDB) delete(id u32) ! {
@@ -38,30 +39,18 @@ mut db_metadata := ourdb.new(
)!
// Create VFS with separate databases for data and metadata
mut vfs := vfs_db.new(mut db_data, mut db_metadata) or {
panic('Failed to create VFS: ${err}')
}
mut vfs := vfs_db.new(mut db_data, mut db_metadata) or { panic('Failed to create VFS: ${err}') }
println('\n---------BEGIN EXAMPLE')
println('---------WRITING FILES')
vfs.file_create('/some_file.txt') or {
panic('Failed to create file: ${err}')
}
vfs.file_create('/another_file.txt') or {
panic('Failed to create file: ${err}')
}
vfs.file_create('/some_file.txt') or { panic('Failed to create file: ${err}') }
vfs.file_create('/another_file.txt') or { panic('Failed to create file: ${err}') }
vfs.file_write('/some_file.txt', 'gibberish'.bytes()) or {
panic('Failed to write file: ${err}')
}
vfs.file_write('/another_file.txt', 'abcdefg'.bytes()) or {
panic('Failed to write file: ${err}')
}
vfs.file_write('/some_file.txt', 'gibberish'.bytes()) or { panic('Failed to write file: ${err}') }
vfs.file_write('/another_file.txt', 'abcdefg'.bytes()) or { panic('Failed to write file: ${err}') }
println('\n---------READING FILES')
some_file_content := vfs.file_read('/some_file.txt') or {
panic('Failed to read file: ${err}')
}
some_file_content := vfs.file_read('/some_file.txt') or { panic('Failed to read file: ${err}') }
println(some_file_content.bytestr())
another_file_content := vfs.file_read('/another_file.txt') or {
@@ -69,19 +58,15 @@ another_file_content := vfs.file_read('/another_file.txt') or {
}
println(another_file_content.bytestr())
println("\n---------WRITING DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir, 'data/0.db'))})")
vfs.file_create('/duplicate.txt') or {
panic('Failed to create file: ${err}')
}
vfs.file_write('/duplicate.txt', 'gibberish'.bytes()) or {
panic('Failed to write file: ${err}')
}
println('\n---------WRITING DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir,
'data/0.db'))})')
vfs.file_create('/duplicate.txt') or { panic('Failed to create file: ${err}') }
vfs.file_write('/duplicate.txt', 'gibberish'.bytes()) or { panic('Failed to write file: ${err}') }
println("\n---------WROTE DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir, 'data/0.db'))})")
println('\n---------WROTE DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir,
'data/0.db'))})')
println('---------READING FILES')
some_file_content3 := vfs.file_read('/some_file.txt') or {
panic('Failed to read file: ${err}')
}
some_file_content3 := vfs.file_read('/some_file.txt') or { panic('Failed to read file: ${err}') }
println(some_file_content3.bytestr())
another_file_content3 := vfs.file_read('/another_file.txt') or {
@@ -89,22 +74,21 @@ another_file_content3 := vfs.file_read('/another_file.txt') or {
}
println(another_file_content3.bytestr())
duplicate_content := vfs.file_read('/duplicate.txt') or {
panic('Failed to read file: ${err}')
}
duplicate_content := vfs.file_read('/duplicate.txt') or { panic('Failed to read file: ${err}') }
println(duplicate_content.bytestr())
println("\n---------DELETING DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir, 'data/0.db'))})")
vfs.file_delete('/duplicate.txt') or {
panic('Failed to delete file: ${err}')
}
println('\n---------DELETING DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir,
'data/0.db'))})')
vfs.file_delete('/duplicate.txt') or { panic('Failed to delete file: ${err}') }
data_path := os.join_path(example_data_dir, 'data/0.db')
db_file_path := os.join_path(data_path, '0.db')
println("---------READING FILES (DB SIZE: ${if os.exists(db_file_path) { os.file_size(db_file_path) } else { 0 }})")
some_file_content2 := vfs.file_read('/some_file.txt') or {
panic('Failed to read file: ${err}')
}
data_path2 := os.join_path(example_data_dir, 'data/0.db')
db_file_path := os.join_path(data_path2, '0.db')
println('---------READING FILES (DB SIZE: ${if os.exists(db_file_path) {
os.file_size(db_file_path)
} else {
0
}})')
some_file_content2 := vfs.file_read('/some_file.txt') or { panic('Failed to read file: ${err}') }
println(some_file_content2.bytestr())
another_file_content2 := vfs.file_read('/another_file.txt') or {

View File

@@ -26,10 +26,8 @@ mut db_metadata := ourdb.new(
)!
// Create VFS with separate databases for data and metadata
mut vfs := vfs_db.new_with_separate_dbs(
mut db_data,
mut db_metadata,
data_dir: data_dir,
mut vfs := vfs_db.new_with_separate_dbs(mut db_data, mut db_metadata,
data_dir: data_dir
metadata_dir: metadata_dir
)!

View File

@@ -26,49 +26,33 @@ mut db_metadata := ourdb.new(
)!
// Create VFS with separate databases for data and metadata
mut vfs := vfs_db.new(mut db_data, mut db_metadata) or {
panic('Failed to create VFS: ${err}')
}
mut vfs := vfs_db.new(mut db_data, mut db_metadata) or { panic('Failed to create VFS: ${err}') }
println('\n---------BEGIN DIRECTORY OPERATIONS EXAMPLE')
// Create directories with subdirectories
println('\n---------CREATING DIRECTORIES')
vfs.dir_create('/dir1') or {
panic('Failed to create directory: ${err}')
}
vfs.dir_create('/dir1') or { panic('Failed to create directory: ${err}') }
println('Created directory: /dir1')
vfs.dir_create('/dir1/subdir1') or {
panic('Failed to create directory: ${err}')
}
vfs.dir_create('/dir1/subdir1') or { panic('Failed to create directory: ${err}') }
println('Created directory: /dir1/subdir1')
vfs.dir_create('/dir1/subdir2') or {
panic('Failed to create directory: ${err}')
}
vfs.dir_create('/dir1/subdir2') or { panic('Failed to create directory: ${err}') }
println('Created directory: /dir1/subdir2')
vfs.dir_create('/dir2') or {
panic('Failed to create directory: ${err}')
}
vfs.dir_create('/dir2') or { panic('Failed to create directory: ${err}') }
println('Created directory: /dir2')
vfs.dir_create('/dir2/subdir1') or {
panic('Failed to create directory: ${err}')
}
vfs.dir_create('/dir2/subdir1') or { panic('Failed to create directory: ${err}') }
println('Created directory: /dir2/subdir1')
vfs.dir_create('/dir2/subdir1/subsubdir1') or {
panic('Failed to create directory: ${err}')
}
vfs.dir_create('/dir2/subdir1/subsubdir1') or { panic('Failed to create directory: ${err}') }
println('Created directory: /dir2/subdir1/subsubdir1')
// List directories
println('\n---------LISTING ROOT DIRECTORY')
root_entries := vfs.dir_list('/') or {
panic('Failed to list directory: ${err}')
}
root_entries := vfs.dir_list('/') or { panic('Failed to list directory: ${err}') }
println('Root directory contains:')
for entry in root_entries {
entry_type := if entry.get_metadata().file_type == .directory { 'directory' } else { 'file' }
@@ -76,9 +60,7 @@ for entry in root_entries {
}
println('\n---------LISTING /dir1 DIRECTORY')
dir1_entries := vfs.dir_list('/dir1') or {
panic('Failed to list directory: ${err}')
}
dir1_entries := vfs.dir_list('/dir1') or { panic('Failed to list directory: ${err}') }
println('/dir1 directory contains:')
for entry in dir1_entries {
entry_type := if entry.get_metadata().file_type == .directory { 'directory' } else { 'file' }
@@ -87,9 +69,7 @@ for entry in dir1_entries {
// Write a file in a subdirectory
println('\n---------WRITING FILE IN SUBDIRECTORY')
vfs.file_create('/dir1/subdir1/test_file.txt') or {
panic('Failed to create file: ${err}')
}
vfs.file_create('/dir1/subdir1/test_file.txt') or { panic('Failed to create file: ${err}') }
println('Created file: /dir1/subdir1/test_file.txt')
test_content := 'This is a test file in a subdirectory'
@@ -104,13 +84,15 @@ file_content := vfs.file_read('/dir1/subdir1/test_file.txt') or {
panic('Failed to read file: ${err}')
}
println('File content: ${file_content.bytestr()}')
println('Content verification: ${if file_content.bytestr() == test_content { 'SUCCESS' } else { 'FAILED' }}')
println('Content verification: ${if file_content.bytestr() == test_content {
'SUCCESS'
} else {
'FAILED'
}}')
// List the subdirectory to see the file
println('\n---------LISTING /dir1/subdir1 DIRECTORY')
subdir1_entries := vfs.dir_list('/dir1/subdir1') or {
panic('Failed to list directory: ${err}')
}
subdir1_entries := vfs.dir_list('/dir1/subdir1') or { panic('Failed to list directory: ${err}') }
println('/dir1/subdir1 directory contains:')
for entry in subdir1_entries {
entry_type := if entry.get_metadata().file_type == .directory { 'directory' } else { 'file' }
@@ -119,9 +101,7 @@ for entry in subdir1_entries {
// Delete the file
println('\n---------DELETING FILE')
vfs.file_delete('/dir1/subdir1/test_file.txt') or {
panic('Failed to delete file: ${err}')
}
vfs.file_delete('/dir1/subdir1/test_file.txt') or { panic('Failed to delete file: ${err}') }
println('Deleted file: /dir1/subdir1/test_file.txt')
// List the subdirectory again to verify the file is gone
@@ -158,7 +138,11 @@ deep_file_content := vfs.file_read('/dir2/subdir1/subsubdir1/deep_file.txt') or
panic('Failed to read file: ${err}')
}
println('File content: ${deep_file_content.bytestr()}')
println('Content verification: ${if deep_file_content.bytestr() == deep_content { 'SUCCESS' } else { 'FAILED' }}')
println('Content verification: ${if deep_file_content.bytestr() == deep_content {
'SUCCESS'
} else {
'FAILED'
}}')
// Clean up by deleting directories (optional)
println('\n---------CLEANING UP')

View File

@@ -6,14 +6,17 @@ import freeflowuniverse.herolib.data.ourdb
import os
import log
const database_path := os.join_path(os.dir(@FILE), 'database')
const database_path = os.join_path(os.dir(@FILE), 'database')
mut metadata_db := ourdb.new(path: os.join_path(database_path, 'metadata'))!
mut data_db := ourdb.new(path: os.join_path(database_path, 'data'))!
mut vfs := vfs_db.new(mut metadata_db, mut data_db)!
mut server := webdav.new_server(vfs: vfs, user_db: {
mut server := webdav.new_server(
vfs: vfs
user_db: {
'admin': '123'
})!
}
)!
log.set_level(.debug)

View File

@@ -91,4 +91,3 @@ println('\nFootnotes:')
for id, footnote in nav.footnotes() {
println(' [^${id}]: ${footnote.content}')
}

View File

@@ -7,8 +7,8 @@ import os
import markdown
import freeflowuniverse.herolib.data.markdownparser2
path2:="${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/links.md"
path1:="${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/test.md"
path2 := '${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/links.md'
path1 := '${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/test.md'
text := os.read_file(path1)!

View File

@@ -18,7 +18,6 @@ for project in 'projectinca, legal, why'.split(',').map(it.trim_space()) {
)!
}
tree.export(
destination: '/tmp/mdexport'
reset: true

View File

@@ -375,12 +375,30 @@ check_and_start_redis() {
fi
fi
elif [[ "${OSNAME}" == "darwin"* ]]; then
# Check if we're in GitHub Actions
if is_github_actions; then
echo "Running in GitHub Actions on macOS. Starting redis directly..."
if pgrep redis-server > /dev/null; then
echo "redis is already running."
else
echo "redis is not running. Starting it in the background..."
redis-server --daemonize yes
if pgrep redis-server > /dev/null; then
echo "redis started successfully."
else
echo "Failed to start redis. Please check logs for details."
exit 1
fi
fi
else
# For regular macOS environments, use brew services
if brew services list | grep -q "^redis.*started"; then
echo "redis is already running."
else
echo "redis is not running. Starting it..."
brew services start redis
fi
fi
elif [[ "${OSNAME}" == "alpine"* ]]; then
if rc-service "redis" status | grep -q "running"; then
echo "redis is already running."

View File

@@ -1,10 +1,10 @@
module generator
import freeflowuniverse.herolib.core.code {Folder, File}
import freeflowuniverse.herolib.core.code
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.schemas.jsonschema.codegen { schema_to_struct }
import freeflowuniverse.herolib.schemas.openrpc.codegen as openrpc_codegen { content_descriptor_to_parameter }
import freeflowuniverse.herolib.baobab.specification {ActorSpecification, ActorMethod, BaseObject}
import freeflowuniverse.herolib.schemas.jsonschema.codegen
import freeflowuniverse.herolib.schemas.openrpc.codegen as openrpc_codegen
import freeflowuniverse.herolib.baobab.specification
import net.http
// pub enum BaseObjectMethodType {
@@ -58,7 +58,7 @@ fn get_endpoint_root(root string) string {
return if root == '' {
''
} else {
"/${root.trim('/')}"
'/${root.trim('/')}'
}
}

View File

@@ -22,7 +22,7 @@ const specification = specification.ActorSpecification{
name: 'Example limit'
description: 'Example Maximum number of pets to return'
value: 10
})
}),
]
result: openrpc.ExampleRef(openrpc.Example{
name: 'Example response'
@@ -39,10 +39,10 @@ const specification = specification.ActorSpecification{
description: 'Maximum number of pets to return'
required: false
schema: jsonschema.SchemaRef(jsonschema.Schema{
...jsonschema.schema_u32,
...jsonschema.schema_u32
example: 10
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pets'
@@ -56,15 +56,18 @@ const specification = specification.ActorSpecification{
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
ref: '#/components/schemas/PetId'
}),
})
'name': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
}),
})
'tag': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
})
}
required: ['id', 'name']
required: [
'id',
'name',
]
}))
})
}
@@ -72,7 +75,7 @@ const specification = specification.ActorSpecification{
openrpc.ErrorSpec{
code: 400
message: 'Invalid request'
}
},
]
},
specification.ActorMethod{
@@ -93,7 +96,7 @@ const specification = specification.ActorSpecification{
openrpc.ErrorSpec{
code: 400
message: 'Invalid input'
}
},
]
},
specification.ActorMethod{
@@ -105,7 +108,7 @@ const specification = specification.ActorSpecification{
name: 'Example petId'
description: 'Example ID of the pet to retrieve'
value: 1
})
}),
]
result: openrpc.ExampleRef(openrpc.Example{
name: 'Example response'
@@ -119,11 +122,11 @@ const specification = specification.ActorSpecification{
description: 'ID of the pet to retrieve'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
...jsonschema.schema_u32,
...jsonschema.schema_u32
format: 'uint32'
example: 1
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'result'
@@ -137,7 +140,7 @@ const specification = specification.ActorSpecification{
openrpc.ErrorSpec{
code: 404
message: 'Pet not found'
}
},
]
},
specification.ActorMethod{
@@ -149,7 +152,7 @@ const specification = specification.ActorSpecification{
name: 'Example petId'
description: 'Example ID of the pet to delete'
value: 1
})
}),
]
}
parameters: [
@@ -159,10 +162,10 @@ const specification = specification.ActorSpecification{
description: 'ID of the pet to delete'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
...jsonschema.schema_u32,
...jsonschema.schema_u32
example: 1
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'result'
@@ -173,9 +176,9 @@ const specification = specification.ActorSpecification{
openrpc.ErrorSpec{
code: 404
message: 'Pet not found'
}
},
]
}
},
]
objects: [
specification.BaseObject{
@@ -183,17 +186,17 @@ const specification = specification.ActorSpecification{
title: 'Pet'
typ: 'object'
properties: {
'id': jsonschema.schema_u32,
'id': jsonschema.schema_u32
'name': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
}),
})
'tag': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
})
}
required: ['id', 'name']
}
}
},
]
}

View File

@@ -1,7 +1,7 @@
module generator
import freeflowuniverse.herolib.baobab.specification {BaseObject}
import freeflowuniverse.herolib.core.code { type_from_symbol, VFile, CodeItem, Function, Import, Param, Param, Struct, StructField, Type }
import freeflowuniverse.herolib.baobab.specification
import freeflowuniverse.herolib.core.code { Param, Param, type_from_symbol }
import freeflowuniverse.herolib.core.texttools
const id_param = Param{

View File

@@ -1,7 +1,7 @@
module generator
import freeflowuniverse.herolib.core.code { VFile, CustomCode, Function, Import, Struct }
import freeflowuniverse.herolib.baobab.specification {BaseObject}
import freeflowuniverse.herolib.core.code
import freeflowuniverse.herolib.baobab.specification
import rand
import freeflowuniverse.herolib.core.texttools

View File

@@ -1,8 +1,8 @@
module generator
import freeflowuniverse.herolib.core.code { Result, Object, Param, Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, Function, Import, Object, Param, Result, VFile }
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.schemas.openrpc {Example, ContentDescriptor}
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, Example }
import freeflowuniverse.herolib.schemas.jsonschema.codegen { schemaref_to_type }
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
@@ -15,9 +15,16 @@ fn generate_handle_file(spec ActorSpecification) !VFile {
return VFile{
name: 'act'
imports: [
Import{mod:'freeflowuniverse.herolib.baobab.stage' types:['Action']}
Import{mod:'freeflowuniverse.herolib.core.texttools'}
Import{mod:'x.json2 as json'}
Import{
mod: 'freeflowuniverse.herolib.baobab.stage'
types: ['Action']
},
Import{
mod: 'freeflowuniverse.herolib.core.texttools'
},
Import{
mod: 'x.json2 as json'
},
]
items: items
}
@@ -77,9 +84,18 @@ pub fn generate_method_handle(actor_name string, method ActorMethod) !Function {
return Function{
name: 'handle_${name_fixed}'
description: '// Handler for ${name_fixed}\n'
receiver: Param{name: 'actor', mutable: true, typ: Object{'${actor_name_pascal}Actor'}}
params: [Param{name: 'action', typ: Object{'Action'}}]
result: Param{typ: Result{Object{'Action'}}}
receiver: Param{
name: 'actor'
mutable: true
typ: Object{'${actor_name_pascal}Actor'}
}
params: [Param{
name: 'action'
typ: Object{'Action'}
}]
result: Param{
typ: Result{Object{'Action'}}
}
body: body
}
}
@@ -95,15 +111,26 @@ pub fn generate_example_method_handle(actor_name string, method ActorMethod) !Fu
if method.example.result is Example {
'return Action{...action, result: json.encode(\'${method.example.result.value}\')}'
} else {
"return action"
'return action'
}
} else {
'return action'
}
} else { "return action" }
return Function{
name: 'handle_${name_fixed}_example'
description: '// Handler for ${name_fixed}\n'
receiver: Param{name: 'actor', mutable: true, typ: Object{'${actor_name_pascal}Actor'}}
params: [Param{name: 'action', typ: Object{'Action'}}]
result: Param{typ: Result{Object{'Action'}}}
receiver: Param{
name: 'actor'
mutable: true
typ: Object{'${actor_name_pascal}Actor'}
}
params: [Param{
name: 'action'
typ: Object{'Action'}
}]
result: Param{
typ: Result{Object{'Action'}}
}
body: body
}
}
@@ -111,12 +138,14 @@ pub fn generate_example_method_handle(actor_name string, method ActorMethod) !Fu
fn generate_call_stmt(name string, method ActorMethod) !string {
mut call_stmt := if schemaref_to_type(method.result.schema).vgen().trim_space() != '' {
'${texttools.snake_case(method.result.name)} := '
} else {''}
} else {
''
}
method_name := texttools.snake_case(method.name)
snake_name := texttools.snake_case(name)
param_names := method.parameters.map(texttools.snake_case(it.name))
call_stmt += 'actor.${snake_name}.${method_name}(${param_names.join(", ")})!'
call_stmt += 'actor.${snake_name}.${method_name}(${param_names.join(', ')})!'
return call_stmt
}
@@ -124,7 +153,7 @@ fn generate_return_stmt(method ActorMethod) !string {
if schemaref_to_type(method.result.schema).vgen().trim_space() != '' {
return 'return Action{...action, result: json.encode(${texttools.snake_case(method.result.name)})}'
}
return "return action"
return 'return action'
}
// Helper function to generate a case block for the main router
@@ -138,12 +167,13 @@ fn generate_decode_stmt(name string, param ContentDescriptor) !string {
param_type := schemaref_to_type(param.schema)
if param_type is Object {
return 'json.decode[${schemaref_to_type(param.schema).vgen()}](${name})!'
}
else if param_type is code.Array {
} else if param_type is code.Array {
return 'json.decode[${schemaref_to_type(param.schema).vgen()}](${name})'
}
param_symbol := param_type.vgen()
return if param_symbol == 'string' {
'${name}.str()'
} else {'${name}.${param_type.vgen()}()'}
} else {
'${name}.${param_type.vgen()}()'
}
}

View File

@@ -1,9 +1,9 @@
module generator
import freeflowuniverse.herolib.core.code { Folder, IFolder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
import freeflowuniverse.herolib.core.code { File, Folder, IFile, IFolder }
import freeflowuniverse.herolib.schemas.openapi
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification, ActorInterface}
import freeflowuniverse.herolib.baobab.specification { ActorInterface, ActorSpecification }
import json
@[params]
@@ -59,7 +59,7 @@ pub fn generate_actor_folder(spec ActorSpecification, params Params) !Folder {
// create module with code files and docs folder
name_fixed := texttools.snake_case(spec.name)
return code.Folder{
return Folder{
name: '${name_fixed}'
files: files
folders: folders

View File

@@ -1,9 +1,9 @@
module generator
import freeflowuniverse.herolib.core.code { Folder, IFolder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
import freeflowuniverse.herolib.core.code { CustomCode, IFile, IFolder, Module, VFile }
import freeflowuniverse.herolib.schemas.openapi
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification, ActorInterface}
import freeflowuniverse.herolib.baobab.specification { ActorInterface, ActorSpecification }
import json
pub fn generate_module_from_openapi(openapi_path string) !string {
@@ -28,11 +28,11 @@ pub fn generate_actor_module(spec ActorSpecification, params Params) !Module {
generate_actor_test_file(spec)!,
generate_specs_file(spec.name, params.interfaces)!,
generate_handle_file(spec)!,
generate_methods_file(spec)!
generate_methods_interface_file(spec)!
generate_methods_example_file(spec)!
generate_client_file(spec)!
generate_model_file(spec)!
generate_methods_file(spec)!,
generate_methods_interface_file(spec)!,
generate_methods_example_file(spec)!,
generate_client_file(spec)!,
generate_model_file(spec)!,
]
// generate code files for supported interfaces

View File

@@ -5,7 +5,7 @@ import freeflowuniverse.herolib.baobab.specification
import freeflowuniverse.herolib.schemas.openrpc
import freeflowuniverse.herolib.schemas.jsonschema
import os
import x.json2 as json {Any}
import x.json2 as json
const actor_spec = specification.ActorSpecification{
name: 'Pet Store'
@@ -22,7 +22,7 @@ const actor_spec = specification.ActorSpecification{
name: 'Example limit'
description: 'Example Maximum number of pets to return'
value: 10
})
}),
]
result: openrpc.ExampleRef(openrpc.Example{
name: 'Example response'
@@ -39,10 +39,10 @@ const actor_spec = specification.ActorSpecification{
description: 'Maximum number of pets to return'
required: false
schema: jsonschema.SchemaRef(jsonschema.Schema{
...jsonschema.schema_u32,
...jsonschema.schema_u32
example: 10
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'pets'
@@ -56,15 +56,18 @@ const actor_spec = specification.ActorSpecification{
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
ref: '#/components/schemas/PetId'
}),
})
'name': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
}),
})
'tag': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
})
}
required: ['id', 'name']
required: [
'id',
'name',
]
}))
})
}
@@ -72,7 +75,7 @@ const actor_spec = specification.ActorSpecification{
openrpc.ErrorSpec{
code: 400
message: 'Invalid request'
}
},
]
},
specification.ActorMethod{
@@ -90,17 +93,17 @@ const actor_spec = specification.ActorSpecification{
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
ref: '#/components/schemas/PetId'
}),
})
'name': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
}),
})
'tag': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
})
}
required: ['id', 'name']
})
}
},
]
example: openrpc.ExamplePairing{
result: openrpc.ExampleRef(openrpc.Example{
@@ -114,7 +117,7 @@ const actor_spec = specification.ActorSpecification{
description: 'ID of the created pet'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
...jsonschema.schema_u32,
...jsonschema.schema_u32
example: 1
})
}
@@ -122,7 +125,7 @@ const actor_spec = specification.ActorSpecification{
openrpc.ErrorSpec{
code: 400
message: 'Invalid input'
}
},
]
},
specification.ActorMethod{
@@ -134,7 +137,7 @@ const actor_spec = specification.ActorSpecification{
name: 'Example petId'
description: 'Example ID of the pet to retrieve'
value: 1
})
}),
]
result: openrpc.ExampleRef(openrpc.Example{
name: 'Example response'
@@ -148,11 +151,11 @@ const actor_spec = specification.ActorSpecification{
description: 'ID of the pet to retrieve'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
...jsonschema.schema_u32,
...jsonschema.schema_u32
format: 'uint32'
example: 1
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'result'
@@ -166,7 +169,7 @@ const actor_spec = specification.ActorSpecification{
openrpc.ErrorSpec{
code: 404
message: 'Pet not found'
}
},
]
},
specification.ActorMethod{
@@ -178,7 +181,7 @@ const actor_spec = specification.ActorSpecification{
name: 'Example petId'
description: 'Example ID of the pet to delete'
value: 1
})
}),
]
}
parameters: [
@@ -188,10 +191,10 @@ const actor_spec = specification.ActorSpecification{
description: 'ID of the pet to delete'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
...jsonschema.schema_u32,
...jsonschema.schema_u32
example: 1
})
}
},
]
result: openrpc.ContentDescriptor{
name: 'result'
@@ -202,9 +205,9 @@ const actor_spec = specification.ActorSpecification{
openrpc.ErrorSpec{
code: 404
message: 'Pet not found'
}
},
]
}
},
]
objects: [
specification.BaseObject{
@@ -212,17 +215,17 @@ const actor_spec = specification.ActorSpecification{
title: 'Pet'
typ: 'object'
properties: {
'id': jsonschema.schema_u32,
'id': jsonschema.schema_u32
'name': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
}),
})
'tag': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
})
}
required: ['id', 'name']
}
}
},
]
}

View File

@@ -1,6 +1,6 @@
module generator
import freeflowuniverse.herolib.core.code { Param, Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode, Result }
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, Function, Import, Param, Result, VFile }
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen { schemaref_to_type }
import freeflowuniverse.herolib.schemas.openrpc.codegen { content_descriptor_to_parameter }
@@ -38,7 +38,7 @@ pub fn generate_client_file(spec ActorSpecification) !VFile {
Import{
mod: 'x.json2 as json'
types: ['Any']
}
},
]
name: 'client_actor'
items: items
@@ -51,18 +51,18 @@ pub fn generate_example_client_file(spec ActorSpecification) !VFile {
mut items := []CodeItem{}
items << CustomCode {'
items << CustomCode{"
pub struct Client {
stage.Client
}
fn new_client() !Client {
mut redis := redisclient.new(\'localhost:6379\')!
mut rpc_q := redis.rpc_get(\'actor_example_\${name}\')
mut redis := redisclient.new('localhost:6379')!
mut rpc_q := redis.rpc_get('actor_example_\${name}')
return Client{
rpc: rpc_q
}
}'}
}"}
for method in spec.methods {
items << generate_client_method(method)!
@@ -79,21 +79,21 @@ pub fn generate_example_client_file(spec ActorSpecification) !VFile {
Import{
mod: 'x.json2 as json'
types: ['Any']
}
},
]
name: 'client'
items: items
}
}
pub fn generate_client_method(method ActorMethod) !Function {
name_fixed := texttools.snake_case(method.name)
call_params := if method.parameters.len > 0 {
method.parameters.map(texttools.snake_case(it.name)).map('Any(${it}.str())').join(', ')
} else {''}
} else {
''
}
params_stmt := if method.parameters.len == 0 {
''
@@ -118,12 +118,15 @@ pub fn generate_client_method(method ActorMethod) !Function {
result_stmt := if result_type == '' {
''
} else {
"return json.decode[${result_type}](action.result)!"
'return json.decode[${result_type}](action.result)!'
}
result_param := content_descriptor_to_parameter(method.result)!
return Function{
receiver: code.new_param(v: 'mut client Client')!
result: Param{...result_param, typ: Result{result_param.typ}}
result: Param{
...result_param
typ: Result{result_param.typ}
}
name: name_fixed
body: '${params_stmt}\n${client_call_stmt}\n${result_stmt}'
summary: method.summary

View File

@@ -1,6 +1,6 @@
module generator
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, Import, VFile }
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }

View File

@@ -1,7 +1,7 @@
module generator
import freeflowuniverse.herolib.baobab.specification { ActorInterface }
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
import freeflowuniverse.herolib.core.code { CustomCode, VFile }
fn generate_openrpc_interface_files(interfaces []ActorInterface) (VFile, VFile) {
http := ActorInterface.http in interfaces

View File

@@ -1,11 +1,11 @@
module generator
import freeflowuniverse.herolib.core.code { Array, Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode, Result }
import freeflowuniverse.herolib.core.code { Array, CodeItem, Function, Import, Param, Result, Struct, VFile }
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.schemas.openrpc {ContentDescriptor}
import freeflowuniverse.herolib.schemas.openrpc
import freeflowuniverse.herolib.schemas.openrpc.codegen { content_descriptor_to_parameter, content_descriptor_to_struct }
import freeflowuniverse.herolib.schemas.jsonschema { Schema }
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schema_to_struct}
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
const crud_prefixes = ['new', 'get', 'set', 'delete', 'list']
@@ -18,45 +18,63 @@ pub fn generate_methods_file(spec ActorSpecification) !VFile {
receiver_param := Param{
mutable: true
name: name_snake[0].ascii_str() // receiver is first letter of domain
typ: code.Result{code.Object{receiver.name}}
typ: Result{code.Object{receiver.name}}
}
mut items := [CodeItem(receiver), CodeItem(generate_core_factory(receiver_param))]
for method in spec.methods {
items << generate_method_code(receiver_param, ActorMethod{...method, category: spec.method_type(method)})!
items << generate_method_code(receiver_param, ActorMethod{
...method
category: spec.method_type(method)
})!
}
return VFile{
name: 'methods'
imports: [Import{mod: 'freeflowuniverse.herolib.baobab.osis', types: ['OSIS']}]
imports: [
Import{
mod: 'freeflowuniverse.herolib.baobab.osis'
types: ['OSIS']
},
]
items: items
}
}
fn generate_methods_receiver(name string) code.Struct {
return code.Struct {
fn generate_methods_receiver(name string) Struct {
return Struct{
is_pub: true
name: '${texttools.pascal_case(name)}'
fields: [code.StructField{is_mut: true, name: 'osis', typ:code.Object{'OSIS'}}]
fields: [
code.StructField{
is_mut: true
name: 'osis'
typ: code.Object{'OSIS'}
},
]
}
}
fn generate_core_factory(receiver code.Param) code.Function {
return code.Function {
fn generate_core_factory(receiver Param) Function {
return Function{
is_pub: true
name: 'new_${receiver.typ.symbol()}'
body: "return ${receiver.typ.symbol().trim_left('!?')}{osis: osis.new()!}"
body: 'return ${receiver.typ.symbol().trim_left('!?')}{osis: osis.new()!}'
result: receiver
}
}
// returns bodyless method prototype
pub fn generate_method_code(receiver code.Param, method ActorMethod) ![]CodeItem {
pub fn generate_method_code(receiver Param, method ActorMethod) ![]CodeItem {
result_param := content_descriptor_to_parameter(method.result)!
mut method_code := []CodeItem{}
// TODO: document assumption
obj_params := method.parameters.filter(if it.schema is Schema {it.schema.typ == 'object'} else {false}).map(content_descriptor_to_struct(it))
obj_params := method.parameters.filter(if it.schema is Schema {
it.schema.typ == 'object'
} else {
false
}).map(content_descriptor_to_struct(it))
if obj_param := obj_params[0] {
method_code << obj_param
}
@@ -81,12 +99,15 @@ pub fn generate_method_code(receiver code.Param, method ActorMethod) ![]CodeItem
}
// returns bodyless method prototype
pub fn generate_method_prototype(receiver code.Param, method ActorMethod) !Function {
pub fn generate_method_prototype(receiver Param, method ActorMethod) !Function {
result_param := content_descriptor_to_parameter(method.result)!
return Function{
name: texttools.snake_case(method.name)
receiver: receiver
result: Param {...result_param, typ: Result{result_param.typ}}
result: Param{
...result_param
typ: Result{result_param.typ}
}
summary: method.summary
description: method.description
params: method.parameters.map(content_descriptor_to_parameter(it)!)

View File

@@ -1,10 +1,10 @@
module generator
import freeflowuniverse.herolib.core.code { Array, Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode, Result }
import freeflowuniverse.herolib.core.code { CodeItem, Function, Import, Param, Result, Struct, VFile }
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.schemas.openrpc { Example }
import freeflowuniverse.herolib.schemas.jsonschema {Schema}
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schema_to_struct}
import freeflowuniverse.herolib.schemas.jsonschema
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen
import freeflowuniverse.herolib.schemas.openrpc.codegen { content_descriptor_to_parameter }
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
@@ -16,43 +16,52 @@ pub fn generate_methods_example_file(spec ActorSpecification) !VFile {
receiver_param := Param{
mutable: true
name: name_snake[0].ascii_str()
typ: code.Result{code.Object{receiver.name}}
typ: Result{code.Object{receiver.name}}
}
mut items := [CodeItem(receiver), CodeItem(generate_core_example_factory(receiver_param))]
for method in spec.methods {
items << generate_method_example_code(receiver_param, ActorMethod{...method, category: spec.method_type(method)})!
items << generate_method_example_code(receiver_param, ActorMethod{
...method
category: spec.method_type(method)
})!
}
return VFile{
name: 'methods_example'
imports: [
Import{mod: 'freeflowuniverse.herolib.baobab.osis', types: ['OSIS']},
Import{mod: 'x.json2 as json'}
Import{
mod: 'freeflowuniverse.herolib.baobab.osis'
types: ['OSIS']
},
Import{
mod: 'x.json2 as json'
},
]
items: items
}
}
fn generate_core_example_factory(receiver code.Param) code.Function {
return code.Function {
fn generate_core_example_factory(receiver Param) Function {
return Function{
is_pub: true
name: 'new_${texttools.snake_case(receiver.typ.symbol())}'
body: "return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}"
body: 'return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}'
result: receiver
}
}
fn generate_example_methods_receiver(name string) code.Struct {
return code.Struct {
fn generate_example_methods_receiver(name string) Struct {
return Struct{
is_pub: true
name: '${texttools.pascal_case(name)}Example'
embeds: [code.Struct{name:'OSIS'}]
embeds: [Struct{
name: 'OSIS'
}]
}
}
// returns bodyless method prototype
pub fn generate_method_example_code(receiver code.Param, method ActorMethod) ![]CodeItem {
pub fn generate_method_example_code(receiver Param, method ActorMethod) ![]CodeItem {
result_param := content_descriptor_to_parameter(method.result)!
mut method_code := []CodeItem{}
@@ -67,12 +76,13 @@ pub fn generate_method_example_code(receiver code.Param, method ActorMethod) ![]
body := if !method_is_void(method)! {
if method.example.result is Example {
"json_str := '${method.example.result.value}'
return ${generate_decode_stmt('json_str', method.result)!}"
return ${generate_decode_stmt('json_str',
method.result)!}"
} else {
"return ${result_param.typ.empty_value()}"
'return ${result_param.typ.empty_value()}'
}
} else {
""
''
}
fn_prototype := generate_method_prototype(receiver, method)!

View File

@@ -1,15 +1,20 @@
module generator
import freeflowuniverse.herolib.core.code { Array, Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode, Result }
import freeflowuniverse.herolib.core.code { CodeItem, Import, Param, VFile }
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter}
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
import freeflowuniverse.herolib.schemas.openrpc.codegen
import freeflowuniverse.herolib.baobab.specification { ActorSpecification }
pub fn generate_methods_interface_file(spec ActorSpecification) !VFile {
return VFile{
name: 'methods_interface'
imports: [Import{mod: 'freeflowuniverse.herolib.baobab.osis', types: ['OSIS']}]
items: [code.CodeItem(generate_methods_interface_declaration(spec)!)]
imports: [
Import{
mod: 'freeflowuniverse.herolib.baobab.osis'
types: ['OSIS']
},
]
items: [CodeItem(generate_methods_interface_declaration(spec)!)]
}
}

View File

@@ -1,9 +1,9 @@
module generator
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode }
import freeflowuniverse.herolib.core.code { CodeItem, Struct, VFile }
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.schemas.jsonschema.codegen { schema_to_struct }
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
import freeflowuniverse.herolib.baobab.specification { ActorSpecification }
pub fn generate_model_file(spec ActorSpecification) !VFile {
actor_name_snake := texttools.snake_case(spec.name)
@@ -11,8 +11,8 @@ pub fn generate_model_file(spec ActorSpecification) !VFile {
return VFile{
name: 'model'
items: spec.objects.map(CodeItem(
Struct {...schema_to_struct(it.schema)
items: spec.objects.map(CodeItem(Struct{
...schema_to_struct(it.schema)
is_pub: true
}))
}

View File

@@ -1,8 +1,8 @@
module generator
import json
import freeflowuniverse.herolib.core.code { VFile, File, Folder, Function, Module, Struct }
import freeflowuniverse.herolib.schemas.openapi { Components, OpenAPI, Operation }
import freeflowuniverse.herolib.core.code { File, Folder }
import freeflowuniverse.herolib.schemas.openapi { OpenAPI, Operation }
import freeflowuniverse.herolib.schemas.openapi.codegen
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen { schema_to_type }
import net.http
@@ -28,22 +28,29 @@ pub fn generate_openapi_ts_client(specification OpenAPI) !Folder {
)!
}
fn body_generator(op openapi.Operation, path_ string, method http.Method) string {
fn body_generator(op Operation, path_ string, method http.Method) string {
path := path_.replace('{', '\${')
return match method {
.post {
if schema := op.payload_schema() {
symbol := schema_to_type(schema).typescript()
"return this.restClient.post<${symbol}>('${path}', data);"
} else {''}
} else {
''
}
}
.get {
if schema := op.response_schema() {
// if op.params.len
symbol := schema_to_type(schema).typescript()
"return this.restClient.get<${symbol}>('${path}', data);"
} else {''}
} else {''}
} else {
''
}
}
else {
''
}
}
// return if operation_is_base_object_method(op) {
// bo_method := operation_to_base_object_method(op)
@@ -58,7 +65,6 @@ fn body_generator(op openapi.Operation, path_ string, method http.Method) string
// } else {''}
}
// pub fn operation_is_base_object_method(op openapi.Operation, base_objs []string) BaseObjectMethod {
// // name := texttools.pascal_case(op.operation_id)
@@ -82,9 +88,6 @@ fn body_generator(op openapi.Operation, path_ string, method http.Method) string
// }
// }
// return if operation_is_base_object_method(op) {
// bo_method := operation_to_base_object_method(op)
// match bo_method. {
@@ -98,12 +101,11 @@ fn body_generator(op openapi.Operation, path_ string, method http.Method) string
// } else {''}
// }
fn get_endpoint(path string) string {
return if path == '' {
''
} else {
"/${path.trim('/')}"
'/${path.trim('/')}'
}
}

View File

@@ -1,8 +1,8 @@
module generator
import json
import freeflowuniverse.herolib.core.code { VFile, File, Function, Module, Struct }
import freeflowuniverse.herolib.schemas.openrpc { Components, OpenRPC }
import freeflowuniverse.herolib.core.code { File, Function, Struct, VFile }
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC }
import freeflowuniverse.herolib.schemas.openrpc.codegen { generate_client_file, generate_client_test_file }
pub fn generate_openrpc_file(spec OpenRPC) !File {
@@ -20,7 +20,7 @@ pub fn generate_openrpc_client_file(spec OpenRPC) !VFile {
// }
client_file := generate_client_file(spec, objects_map)!
return VFile{
...client_file,
...client_file
name: 'client_openrpc'
}
}
@@ -36,7 +36,7 @@ pub fn generate_openrpc_client_test_file(spec OpenRPC) !VFile {
// }
file := generate_client_test_file(spec, methods_map, objects_map)!
return VFile{
...file,
...file
name: 'client_openrpc_test'
}
}

View File

@@ -1,6 +1,6 @@
module generator
import freeflowuniverse.herolib.core.code { Folder, File }
import freeflowuniverse.herolib.core.code { File, Folder }
import freeflowuniverse.herolib.core.texttools
// generates the folder with runnable scripts of the actor

View File

@@ -105,7 +105,7 @@ fn get_table[T]() string {
// returns the lists of the indices of a root objects db table, and corresponding values
pub fn get_indices[T](object T) map[string]string {
mut indices := map[string]string
mut indices := map[string]string{}
$for field in T.fields {
if field.attrs.contains('index') {
value := object.$(field.name)

View File

@@ -1,18 +1,18 @@
module specification
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.code { Struct, Function }
import freeflowuniverse.herolib.core.code { Struct }
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
import freeflowuniverse.herolib.schemas.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec, MediaType }
import freeflowuniverse.herolib.schemas.openrpc { ExamplePairing, Example, ExampleRef, ContentDescriptor, ErrorSpec }
import freeflowuniverse.herolib.schemas.openapi { MediaType, OpenAPI, Parameter }
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, Example, ExamplePairing, ExampleRef }
// Helper function: Convert OpenAPI parameter to ContentDescriptor
fn openapi_param_to_content_descriptor(param Parameter) ContentDescriptor {
return ContentDescriptor{
name: param.name,
summary: param.description,
description: param.description,
required: param.required,
name: param.name
summary: param.description
description: param.description
required: param.required
schema: param.schema
}
}
@@ -22,8 +22,8 @@ fn openapi_param_to_example(param Parameter) ?Example {
if param.schema is Schema {
if param.schema.example.str() != '' {
return Example{
name: 'Example ${param.name}',
description: 'Example ${param.description}',
name: 'Example ${param.name}'
description: 'Example ${param.description}'
value: param.schema.example
}
}
@@ -32,21 +32,25 @@ fn openapi_param_to_example(param Parameter) ?Example {
}
// Helper function: Convert OpenAPI operation to ActorMethod
fn openapi_operation_to_actor_method(info openapi.OperationInfo) ActorMethod {
fn openapi_operation_to_actor_method(info OperationInfo) ActorMethod {
mut parameters := []ContentDescriptor{}
mut example_parameters := []Example{}
for param in info.operation.parameters {
parameters << openapi_param_to_content_descriptor(param)
example_parameters << openapi_param_to_example(param) or {
continue
}
example_parameters << openapi_param_to_example(param) or { continue }
}
if schema_ := info.operation.payload_schema() {
// TODO: document assumption
schema := Schema{...schema_, title: texttools.pascal_case(info.operation.operation_id)}
parameters << ContentDescriptor {name: 'data', schema: SchemaRef(schema)}
schema := Schema{
...schema_
title: texttools.pascal_case(info.operation.operation_id)
}
parameters << ContentDescriptor{
name: 'data'
schema: SchemaRef(schema)
}
}
mut success_responses := map[string]MediaType{}
@@ -63,48 +67,54 @@ fn openapi_operation_to_actor_method(info openapi.OperationInfo) ActorMethod {
response_success := success_responses.values()[0]
mut result := ContentDescriptor{
name: "result",
description: "The response of the operation.",
required: true,
name: 'result'
description: 'The response of the operation.'
required: true
schema: response_success.schema
}
example_result := if response_success.example.str() != '' {
Example{
name: 'Example response',
name: 'Example response'
value: response_success.example
}
} else {Example{}}
} else {
Example{}
}
pairing := if example_result != Example{} || example_parameters.len > 0 {
ExamplePairing{
params: example_parameters.map(ExampleRef(it))
result: ExampleRef(example_result)
}
} else {ExamplePairing{}}
} else {
ExamplePairing{}
}
mut errors := []ErrorSpec{}
for status, response in info.operation.responses {
if status.int() >= 400 {
error_schema := if response.content.len > 0 {
response.content.values()[0].schema
} else {Schema{}}
} else {
Schema{}
}
errors << ErrorSpec{
code: status.int(),
message: response.description,
data: error_schema, // Extend if error schema is defined
code: status.int()
message: response.description
data: error_schema // Extend if error schema is defined
}
}
}
return ActorMethod{
name: info.operation.operation_id,
description: info.operation.description,
summary: info.operation.summary,
parameters: parameters,
name: info.operation.operation_id
description: info.operation.description
summary: info.operation.summary
parameters: parameters
example: pairing
result: result,
errors: errors,
result: result
errors: errors
}
}
@@ -112,7 +122,7 @@ fn openapi_operation_to_actor_method(info openapi.OperationInfo) ActorMethod {
fn openapi_schema_to_struct(name string, schema SchemaRef) Struct {
// Assuming schema properties can be mapped to Struct fields
return Struct{
name: name,
name: name
}
}
@@ -153,16 +163,18 @@ pub fn from_openapi(spec_raw OpenAPI) !ActorSpecification {
// Extract objects from OpenAPI components.schemas
for name, schema in spec.components.schemas {
objects << BaseObject{schema: schema as Schema}
objects << BaseObject{
schema: schema as Schema
}
}
return ActorSpecification{
openapi: spec_raw
name: spec.info.title,
description: spec.info.description,
structure: Struct{}, // Assuming no top-level structure for this use case
interfaces: [.openapi], // Default to OpenAPI for input
methods: spec.get_operations().map(openapi_operation_to_actor_method(it)),
objects: objects,
name: spec.info.title
description: spec.info.description
structure: Struct{} // Assuming no top-level structure for this use case
interfaces: [.openapi] // Default to OpenAPI for input
methods: spec.get_operations().map(openapi_operation_to_actor_method(it))
objects: objects
}
}

View File

@@ -1,31 +1,31 @@
module specification
import x.json2 as json {Any}
import freeflowuniverse.herolib.core.code { Struct, Function }
import x.json2 as json
import freeflowuniverse.herolib.core.code { Struct }
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec }
import freeflowuniverse.herolib.schemas.openapi { OpenAPI, Info, ServerSpec, Components, Operation, PathItem, PathRef }
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference, SchemaRef}
import freeflowuniverse.herolib.schemas.openapi { Components, Info, OpenAPI, Operation, PathItem, ServerSpec }
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema, SchemaRef }
const openapi_spec = openapi.OpenAPI{
const openapi_spec = OpenAPI{
openapi: '3.0.3'
info: openapi.Info{
info: Info{
title: 'Pet Store API'
description: 'A sample API for a pet store'
version: '1.0.0'
}
servers: [
openapi.ServerSpec{
ServerSpec{
url: 'https://api.petstore.example.com/v1'
description: 'Production server'
},
openapi.ServerSpec{
ServerSpec{
url: 'https://staging.petstore.example.com/v1'
description: 'Staging server'
}
},
]
paths: {
'/pets': openapi.PathItem{
get: openapi.Operation{
'/pets': PathItem{
get: Operation{
summary: 'List all pets'
operation_id: 'listPets'
parameters: [
@@ -39,7 +39,7 @@ const openapi_spec = openapi.OpenAPI{
format: 'int32'
example: 10
}
}
},
]
responses: {
'200': openapi.ResponseSpec{
@@ -61,7 +61,7 @@ const openapi_spec = openapi.OpenAPI{
}
}
}
post: openapi.Operation{
post: Operation{
summary: 'Create a new pet'
operation_id: 'createPet'
request_body: openapi.RequestBody{
@@ -93,8 +93,8 @@ const openapi_spec = openapi.OpenAPI{
}
}
}
'/pets/{petId}': openapi.PathItem{
get: openapi.Operation{
'/pets/{petId}': PathItem{
get: Operation{
summary: 'Get a pet by ID'
operation_id: 'getPet'
parameters: [
@@ -108,7 +108,7 @@ const openapi_spec = openapi.OpenAPI{
format: 'int64'
example: 1
}
}
},
]
responses: {
'200': openapi.ResponseSpec{
@@ -127,7 +127,7 @@ const openapi_spec = openapi.OpenAPI{
}
}
}
delete: openapi.Operation{
delete: Operation{
summary: 'Delete a pet by ID'
operation_id: 'deletePet'
parameters: [
@@ -141,7 +141,7 @@ const openapi_spec = openapi.OpenAPI{
format: 'int64'
example: 1
}
}
},
]
responses: {
'204': openapi.ResponseSpec{
@@ -154,7 +154,7 @@ const openapi_spec = openapi.OpenAPI{
}
}
}
components: openapi.Components{
components: Components{
schemas: {
'Pet': SchemaRef(Schema{
typ: 'object'
@@ -194,13 +194,13 @@ const openapi_spec = openapi.OpenAPI{
}
}
const actor_spec = specification.ActorSpecification{
const actor_spec = ActorSpecification{
name: 'Pet Store API'
description: 'A sample API for a pet store'
structure: code.Struct{}
structure: Struct{}
interfaces: [.openapi]
methods: [
specification.ActorMethod{
ActorMethod{
name: 'listPets'
summary: 'List all pets'
example: openrpc.ExamplePairing{
@@ -209,7 +209,7 @@ const actor_spec = specification.ActorSpecification{
name: 'Example limit'
description: 'Example Maximum number of pets to return'
value: 10
})
}),
]
result: openrpc.ExampleRef(openrpc.Example{
name: 'Example response'
@@ -220,34 +220,34 @@ const actor_spec = specification.ActorSpecification{
})
}
parameters: [
openrpc.ContentDescriptor{
ContentDescriptor{
name: 'limit'
summary: 'Maximum number of pets to return'
description: 'Maximum number of pets to return'
required: false
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'integer'
format: 'int32'
example: 10
})
}
},
]
result: openrpc.ContentDescriptor{
result: ContentDescriptor{
name: 'result'
description: 'The response of the operation.'
required: true
schema: jsonschema.SchemaRef(jsonschema.Reference{
schema: SchemaRef(Reference{
ref: '#/components/schemas/Pets'
})
}
errors: [
openrpc.ErrorSpec{
ErrorSpec{
code: 400
message: 'Invalid request'
}
},
]
},
specification.ActorMethod{
ActorMethod{
name: 'createPet'
summary: 'Create a new pet'
example: openrpc.ExamplePairing{
@@ -256,19 +256,19 @@ const actor_spec = specification.ActorSpecification{
value: '[]'
})
}
result: openrpc.ContentDescriptor{
result: ContentDescriptor{
name: 'result'
description: 'The response of the operation.'
required: true
}
errors: [
openrpc.ErrorSpec{
ErrorSpec{
code: 400
message: 'Invalid input'
}
},
]
},
specification.ActorMethod{
ActorMethod{
name: 'getPet'
summary: 'Get a pet by ID'
example: openrpc.ExamplePairing{
@@ -277,7 +277,7 @@ const actor_spec = specification.ActorSpecification{
name: 'Example petId'
description: 'Example ID of the pet to retrieve'
value: 1
})
}),
]
result: openrpc.ExampleRef(openrpc.Example{
name: 'Example response'
@@ -285,34 +285,34 @@ const actor_spec = specification.ActorSpecification{
})
}
parameters: [
openrpc.ContentDescriptor{
ContentDescriptor{
name: 'petId'
summary: 'ID of the pet to retrieve'
description: 'ID of the pet to retrieve'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'integer'
format: 'int64'
example: 1
})
}
},
]
result: openrpc.ContentDescriptor{
result: ContentDescriptor{
name: 'result'
description: 'The response of the operation.'
required: true
schema: jsonschema.SchemaRef(jsonschema.Reference{
schema: SchemaRef(Reference{
ref: '#/components/schemas/Pet'
})
}
errors: [
openrpc.ErrorSpec{
ErrorSpec{
code: 404
message: 'Pet not found'
}
},
]
},
specification.ActorMethod{
ActorMethod{
name: 'deletePet'
summary: 'Delete a pet by ID'
example: openrpc.ExamplePairing{
@@ -321,76 +321,76 @@ const actor_spec = specification.ActorSpecification{
name: 'Example petId'
description: 'Example ID of the pet to delete'
value: 1
})
}),
]
}
parameters: [
openrpc.ContentDescriptor{
ContentDescriptor{
name: 'petId'
summary: 'ID of the pet to delete'
description: 'ID of the pet to delete'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'integer'
format: 'int64'
example: 1
})
}
},
]
result: openrpc.ContentDescriptor{
result: ContentDescriptor{
name: 'result'
description: 'The response of the operation.'
required: true
}
errors: [
openrpc.ErrorSpec{
ErrorSpec{
code: 404
message: 'Pet not found'
}
},
]
}
},
]
objects: [
specification.BaseObject{
schema: jsonschema.Schema{
BaseObject{
schema: Schema{
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Schema{
'id': SchemaRef(Schema{
typ: 'integer'
format: 'int64'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['id', 'name']
}
},
specification.BaseObject{
schema: jsonschema.Schema{
BaseObject{
schema: Schema{
typ: 'object'
properties: {
'name': jsonschema.SchemaRef(jsonschema.Schema{
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['name']
}
},
specification.BaseObject{
schema: jsonschema.Schema{
BaseObject{
schema: Schema{
typ: 'array'
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Reference{
items: jsonschema.Items(SchemaRef(Reference{
ref: '#/components/schemas/Pet'
}))
}
}
},
]
}

View File

@@ -1,7 +1,7 @@
module specification
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC, Method, ContentDescriptor, ErrorSpec }
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema, SchemaRef }
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, Method, OpenRPC }
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema }
import freeflowuniverse.herolib.core.texttools
// Helper function: Convert OpenRPC Method to ActorMethod
@@ -86,7 +86,8 @@ pub fn from_openrpc(spec OpenRPC) !ActorSpecification {
if schema is Schema {
if schema.typ == 'object' {
objects << BaseObject{
schema: Schema {...schema,
schema: Schema{
...schema
title: texttools.pascal_case(key)
id: texttools.snake_case(key)
}

View File

@@ -1,9 +1,9 @@
module specification
import freeflowuniverse.herolib.core.code { Struct, Function }
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec }
import freeflowuniverse.herolib.schemas.openapi { OpenAPI, Info, ServerSpec, Components, Operation, PathItem, PathRef }
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference, SchemaRef}
import freeflowuniverse.herolib.core.code { Struct }
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor }
import freeflowuniverse.herolib.schemas.openapi { Components, Info }
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema, SchemaRef }
const openrpc_spec = openrpc.OpenRPC{
openrpc: '1.0.0-rc1'
@@ -14,164 +14,182 @@ const openrpc_spec = openrpc.OpenRPC{
}
version: '1.0.0'
}
servers: [openrpc.Server{
servers: [
openrpc.Server{
name: 'localhost'
url: openrpc.RuntimeExpression('http://localhost:8080')
}]
},
]
methods: [
openrpc.Method{
name: 'list_pets'
summary: 'List all pets'
params: [openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
params: [
openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'limit'
description: 'How many items to return at one time (max 100)'
required: false
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'integer'
minimum: 1
})
})]
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
}),
]
result: openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'pets'
description: 'A paged array of pets'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'array'
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Reference{
items: jsonschema.Items(SchemaRef(Reference{
ref: '#/components/schemas/Pet'
}))
})
})
examples: [openrpc.ExamplePairing{
examples: [
openrpc.ExamplePairing{
name: 'listPetExample'
description: 'List pet example'
}]
},
]
},
openrpc.Method{
name: 'create_pet'
summary: 'Create a pet'
params: [
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'newPetName'
description: 'Name of pet to create'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}),
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'newPetTag'
description: 'Pet tag to create'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
})
}),
]
result: openrpc.ContentDescriptorRef(jsonschema.Reference{
result: openrpc.ContentDescriptorRef(Reference{
ref: '#/components/contentDescriptors/PetId'
})
examples: [openrpc.ExamplePairing{
examples: [
openrpc.ExamplePairing{
name: 'createPetExample'
description: 'Create pet example'
}]
},
]
},
openrpc.Method{
name: 'get_pet'
summary: 'Info for a specific pet'
params: [openrpc.ContentDescriptorRef(jsonschema.Reference{
params: [
openrpc.ContentDescriptorRef(Reference{
ref: '#/components/contentDescriptors/PetId'
})]
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
}),
]
result: openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'pet'
description: 'Expected response to a valid request'
schema: jsonschema.SchemaRef(jsonschema.Reference{
schema: SchemaRef(Reference{
ref: '#/components/schemas/Pet'
})
})
examples: [openrpc.ExamplePairing{
examples: [
openrpc.ExamplePairing{
name: 'getPetExample'
description: 'Get pet example'
}]
},
]
},
openrpc.Method{
name: 'update_pet'
summary: 'Update a pet'
params: [
openrpc.ContentDescriptorRef(jsonschema.Reference{
openrpc.ContentDescriptorRef(Reference{
ref: '#/components/contentDescriptors/PetId'
}),
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'updatedPetName'
description: 'New name for the pet'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}),
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'updatedPetTag'
description: 'New tag for the pet'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
})
}),
]
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
result: openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'pet'
description: 'Updated pet object'
schema: jsonschema.SchemaRef(jsonschema.Reference{
schema: SchemaRef(Reference{
ref: '#/components/schemas/Pet'
})
})
examples: [openrpc.ExamplePairing{
examples: [
openrpc.ExamplePairing{
name: 'updatePetExample'
description: 'Update pet example'
}]
},
]
},
openrpc.Method{
name: 'delete_pet'
summary: 'Delete a pet'
params: [openrpc.ContentDescriptorRef(jsonschema.Reference{
params: [
openrpc.ContentDescriptorRef(Reference{
ref: '#/components/contentDescriptors/PetId'
})]
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
}),
]
result: openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'success'
description: 'Boolean indicating success'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'boolean'
})
})
examples: [openrpc.ExamplePairing{
examples: [
openrpc.ExamplePairing{
name: 'deletePetExample'
description: 'Delete pet example'
}]
}
},
]
},
]
components: openrpc.Components{
content_descriptors: {
'PetId': openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
'PetId': openrpc.ContentDescriptorRef(ContentDescriptor{
name: 'petId'
description: 'The ID of the pet'
required: true
schema: jsonschema.SchemaRef(jsonschema.Reference{
schema: SchemaRef(Reference{
ref: '#/components/schemas/PetId'
})
})
}
schemas: {
'PetId': jsonschema.SchemaRef(jsonschema.Schema{
'PetId': SchemaRef(Schema{
typ: 'integer'
minimum: 0
}),
'Pet': jsonschema.SchemaRef(jsonschema.Schema{
})
'Pet': SchemaRef(Schema{
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
'id': SchemaRef(Reference{
ref: '#/components/schemas/PetId'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
@@ -181,152 +199,176 @@ const openrpc_spec = openrpc.OpenRPC{
}
}
const actor_spec = specification.ActorSpecification{
const actor_spec = ActorSpecification{
name: 'Petstore'
structure: code.Struct{
structure: Struct{
is_pub: false
}
interfaces: [.openrpc]
methods: [specification.ActorMethod{
methods: [
ActorMethod{
name: 'list_pets'
summary: 'List all pets'
parameters: [openrpc.ContentDescriptor{
parameters: [
ContentDescriptor{
name: 'limit'
description: 'How many items to return at one time (max 100)'
required: false
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'integer'
minimum: 1
})
}]
result: openrpc.ContentDescriptor{
},
]
result: ContentDescriptor{
name: 'pets'
description: 'A paged array of pets'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'array'
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
items: jsonschema.Items(SchemaRef(Schema{
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
'id': SchemaRef(Reference{
ref: '#/components/schemas/PetId'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['id', 'name']
required: [
'id',
'name',
]
}))
})
}
}, specification.ActorMethod{
},
ActorMethod{
name: 'create_pet'
summary: 'Create a pet'
parameters: [openrpc.ContentDescriptor{
parameters: [
ContentDescriptor{
name: 'newPetName'
description: 'Name of pet to create'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}, openrpc.ContentDescriptor{
},
ContentDescriptor{
name: 'newPetTag'
description: 'Pet tag to create'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}]
}, specification.ActorMethod{
},
]
},
ActorMethod{
name: 'get_pet'
summary: 'Info for a specific pet'
result: openrpc.ContentDescriptor{
result: ContentDescriptor{
name: 'pet'
description: 'Expected response to a valid request'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
'id': SchemaRef(Reference{
ref: '#/components/schemas/PetId'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['id', 'name']
required: [
'id',
'name',
]
})
}
}, specification.ActorMethod{
},
ActorMethod{
name: 'update_pet'
summary: 'Update a pet'
parameters: [openrpc.ContentDescriptor{
parameters: [
ContentDescriptor{
name: 'updatedPetName'
description: 'New name for the pet'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}, openrpc.ContentDescriptor{
},
ContentDescriptor{
name: 'updatedPetTag'
description: 'New tag for the pet'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}]
result: openrpc.ContentDescriptor{
},
]
result: ContentDescriptor{
name: 'pet'
description: 'Updated pet object'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
'id': SchemaRef(Reference{
ref: '#/components/schemas/PetId'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['id', 'name']
required: [
'id',
'name',
]
})
}
}, specification.ActorMethod{
},
ActorMethod{
name: 'delete_pet'
summary: 'Delete a pet'
result: openrpc.ContentDescriptor{
result: ContentDescriptor{
name: 'success'
description: 'Boolean indicating success'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'boolean'
})
}
}]
objects: [specification.BaseObject{
schema: jsonschema.Schema{
},
]
objects: [
BaseObject{
schema: Schema{
id: 'pet'
title: 'Pet'
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
'id': SchemaRef(Reference{
ref: '#/components/schemas/PetId'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['id', 'name']
}
}]
},
]
}
pub fn test_from_openrpc() ! {
@@ -334,7 +376,6 @@ pub fn test_from_openrpc() ! {
assert actor_spec_.methods.len == actor_spec.methods.len
assert_methods_match(actor_spec_.methods[0], actor_spec.methods[0])
// assert from_openrpc(openrpc_spec)! == actor_spec
}
@@ -360,7 +401,7 @@ fn assert_methods_match(a ActorMethod, b ActorMethod) {
assert_params_match(a.result, b.result, a.name)
}
fn assert_params_match(a openrpc.ContentDescriptor, b openrpc.ContentDescriptor, method_name string) {
fn assert_params_match(a ContentDescriptor, b ContentDescriptor, method_name string) {
// Compare parameter names
assert a.name == b.name, 'Parameter names do not match in method ${method_name}: ${a.name} != ${b.name}'

View File

@@ -1,9 +1,9 @@
module specification
import freeflowuniverse.herolib.core.code { Struct, Function }
import freeflowuniverse.herolib.core.code { Struct }
import freeflowuniverse.herolib.schemas.openapi
import freeflowuniverse.herolib.schemas.openrpc {ExamplePairing, ContentDescriptor, ErrorSpec}
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference}
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, ExamplePairing }
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema }
pub struct ActorSpecification {
pub mut:
@@ -107,7 +107,7 @@ pub fn (s ActorSpecification) validate() ActorSpecification {
}
}
return ActorSpecification{
...s,
...s
objects: validated_objects
}
}
@@ -150,35 +150,38 @@ fn (s ActorSpecification) is_base_object_method(method ActorMethod) bool {
fn (m ActorMethod) is_new_method() bool {
return m.name.starts_with('new')
}
fn (m ActorMethod) is_get_method() bool {
return m.name.starts_with('get')
}
fn (m ActorMethod) is_set_method() bool {
return m.name.starts_with('set')
}
fn (m ActorMethod) is_delete_method() bool {
return m.name.starts_with('delete')
}
fn (m ActorMethod) is_list_method() bool {
return m.name.starts_with('list')
}
fn (m ActorMethod) is_filter_method() bool {
return m.name.starts_with('filter')
}
fn (m ActorMethod) is_crudlf_method() bool {
return m.is_new_method() ||
m.is_get_method() ||
m.is_set_method() ||
m.is_delete_method() ||
m.is_list_method() ||
m.is_filter_method()
return m.is_new_method() || m.is_get_method() || m.is_set_method() || m.is_delete_method()
|| m.is_list_method() || m.is_filter_method()
}
pub fn (o BaseObject) name() string {
return if o.schema.id.trim_space() != '' {
o.schema.id.trim_space()
} else {o.schema.title.trim_space()}
} else {
o.schema.title.trim_space()
}
}
fn (s ActorSpecification) is_base_object_new_method(method ActorMethod) bool {

View File

@@ -1,7 +1,7 @@
module specification
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
import freeflowuniverse.herolib.schemas.openapi { MediaType, ResponseSpec, Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec }
import freeflowuniverse.herolib.schemas.openapi { Components, Info, MediaType, OpenAPI, Operation, Parameter, PathItem, ResponseSpec, ServerSpec }
import net.http
// Converts ActorSpecification to OpenAPI
@@ -15,8 +15,14 @@ pub fn (s ActorSpecification) to_openapi() OpenAPI {
for method in s.methods {
op := method.to_openapi_operation()
paths['${method.http_path()}'] = match method.http_method() {
.get { PathItem {get: op} }
else { panic('unsupported http method') }
.get {
PathItem{
get: op
}
}
else {
panic('unsupported http method')
}
}
// Assign operation to corresponding HTTP method
// TODO: what about other verbs
@@ -28,23 +34,23 @@ pub fn (s ActorSpecification) to_openapi() OpenAPI {
}
return OpenAPI{
openapi: '3.0.0',
openapi: '3.0.0'
info: Info{
title: s.name,
summary: s.description,
description: s.description,
version: '1.0.0',
},
title: s.name
summary: s.description
description: s.description
version: '1.0.0'
}
servers: [
ServerSpec{
url: 'http://localhost:8080',
description: 'Default server',
url: 'http://localhost:8080'
description: 'Default server'
},
],
paths: paths,
]
paths: paths
components: Components{
schemas: schemas
},
}
}
}
@@ -62,19 +68,19 @@ fn (m ActorMethod) http_method() http.Method {
fn (method ActorMethod) to_openapi_operation() Operation {
mut op := Operation{
summary: method.summary,
description: method.description,
operation_id: method.name,
summary: method.summary
description: method.description
operation_id: method.name
}
// Convert parameters to OpenAPI format
for param in method.parameters {
op.parameters << Parameter{
name: param.name,
in_: 'query', // Default to query parameters; adjust based on function context
description: param.description,
required: param.required,
schema: param.schema,
name: param.name
in_: 'query' // Default to query parameters; adjust based on function context
description: param.description
required: param.required
schema: param.schema
}
}

View File

@@ -2,155 +2,179 @@ module specification
import freeflowuniverse.herolib.core.code
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
import freeflowuniverse.herolib.schemas.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec }
import freeflowuniverse.herolib.schemas.openapi
import freeflowuniverse.herolib.schemas.openrpc
const actor_spec = specification.ActorSpecification{
const actor_spec = ActorSpecification{
name: 'Petstore'
structure: code.Struct{
is_pub: false
}
interfaces: [.openrpc]
methods: [specification.ActorMethod{
methods: [
ActorMethod{
name: 'list_pets'
summary: 'List all pets'
parameters: [openrpc.ContentDescriptor{
parameters: [
openrpc.ContentDescriptor{
name: 'limit'
description: 'How many items to return at one time (max 100)'
required: false
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'integer'
minimum: 1
})
}]
},
]
result: openrpc.ContentDescriptor{
name: 'pets'
description: 'A paged array of pets'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'array'
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
items: jsonschema.Items(SchemaRef(Schema{
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
'id': SchemaRef(jsonschema.Reference{
ref: '#/components/schemas/PetId'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['id', 'name']
required: [
'id',
'name',
]
}))
})
}
}, specification.ActorMethod{
},
ActorMethod{
name: 'create_pet'
summary: 'Create a pet'
parameters: [openrpc.ContentDescriptor{
parameters: [
openrpc.ContentDescriptor{
name: 'newPetName'
description: 'Name of pet to create'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}, openrpc.ContentDescriptor{
},
openrpc.ContentDescriptor{
name: 'newPetTag'
description: 'Pet tag to create'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}]
}, specification.ActorMethod{
},
]
},
ActorMethod{
name: 'get_pet'
summary: 'Info for a specific pet'
result: openrpc.ContentDescriptor{
name: 'pet'
description: 'Expected response to a valid request'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
'id': SchemaRef(jsonschema.Reference{
ref: '#/components/schemas/PetId'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['id', 'name']
required: [
'id',
'name',
]
})
}
}, specification.ActorMethod{
},
ActorMethod{
name: 'update_pet'
summary: 'Update a pet'
parameters: [openrpc.ContentDescriptor{
parameters: [
openrpc.ContentDescriptor{
name: 'updatedPetName'
description: 'New name for the pet'
required: true
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}, openrpc.ContentDescriptor{
},
openrpc.ContentDescriptor{
name: 'updatedPetTag'
description: 'New tag for the pet'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'string'
})
}]
},
]
result: openrpc.ContentDescriptor{
name: 'pet'
description: 'Updated pet object'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
'id': SchemaRef(jsonschema.Reference{
ref: '#/components/schemas/PetId'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['id', 'name']
required: [
'id',
'name',
]
})
}
}, specification.ActorMethod{
},
ActorMethod{
name: 'delete_pet'
summary: 'Delete a pet'
result: openrpc.ContentDescriptor{
name: 'success'
description: 'Boolean indicating success'
schema: jsonschema.SchemaRef(jsonschema.Schema{
schema: SchemaRef(Schema{
typ: 'boolean'
})
}
}]
objects: [specification.BaseObject{
schema: jsonschema.Schema{
},
]
objects: [
BaseObject{
schema: Schema{
id: 'pet'
title: 'Pet'
typ: 'object'
properties: {
'id': jsonschema.SchemaRef(jsonschema.Reference{
'id': SchemaRef(jsonschema.Reference{
ref: '#/components/schemas/PetId'
}),
'name': jsonschema.SchemaRef(jsonschema.Schema{
})
'name': SchemaRef(Schema{
typ: 'string'
}),
'tag': jsonschema.SchemaRef(jsonschema.Schema{
})
'tag': SchemaRef(Schema{
typ: 'string'
})
}
required: ['id', 'name']
}
}]
},
]
}
// Converts ActorSpecification to OpenAPI

View File

@@ -1,8 +1,8 @@
module specification
import freeflowuniverse.herolib.schemas.openrpc {OpenRPC, Components}
import freeflowuniverse.herolib.schemas.openrpc { Components, OpenRPC }
import freeflowuniverse.herolib.schemas.jsonschema { SchemaRef }
import freeflowuniverse.herolib.schemas.jsonschema.codegen { struct_to_schema }
import freeflowuniverse.herolib.schemas.jsonschema.codegen
// pub fn from_openrpc(spec openrpc.OpenRPC) !ActorSpecification {
// // Extract Actor metadata from OpenRPC info
@@ -39,7 +39,6 @@ import freeflowuniverse.herolib.schemas.jsonschema.codegen { struct_to_schema }
// }
// }
pub fn (specification ActorSpecification) to_openrpc() OpenRPC {
mut schemas := map[string]SchemaRef{}
for obj in specification.objects {

View File

@@ -45,13 +45,15 @@ pub fn (a ActorConfig) get_redis_rpc() !redisclient.RedisRpc {
}
pub fn (a ActorConfig) version(v string) ActorConfig {
return ActorConfig {...a,
return ActorConfig{
...a
version: v
}
}
pub fn (a ActorConfig) example() ActorConfig {
return ActorConfig {...a,
return ActorConfig{
...a
version: 'example'
}
}

View File

@@ -1,2 +1 @@
module stage

View File

@@ -18,9 +18,7 @@ pub fn new_openapi_interface(client Client) &OpenAPIInterface {
pub fn (mut i OpenAPIInterface) handle(request openapi.Request) !openapi.Response {
// Convert incoming OpenAPI request to a procedure call
action := action_from_openapi_request(request)
response := i.client.call_to_action(action) or {
return err
}
response := i.client.call_to_action(action) or { return err }
return action_to_openapi_response(response)
}

View File

@@ -1,7 +1,7 @@
module interfaces
import freeflowuniverse.herolib.schemas.openapi { OpenAPI }
import freeflowuniverse.herolib.baobab.stage {Client, ClientConfig}
import freeflowuniverse.herolib.baobab.stage { ClientConfig }
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC }
import veb

View File

@@ -3,11 +3,11 @@ module bizmodel
import os
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core.playbook { PlayBook, Action }
import freeflowuniverse.herolib.core.playbook { Action }
import freeflowuniverse.herolib.ui.console
// import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.data.paramsparser { Params }
import freeflowuniverse.herolib.biz.spreadsheet {RowGetArgs, UnitType, PeriodType}
import freeflowuniverse.herolib.biz.spreadsheet { PeriodType, RowGetArgs, UnitType }
pub fn (mut m BizModel) act(action Action) !Action {
return match texttools.snake_case(action.name) {
@@ -58,23 +58,28 @@ fn (mut m BizModel) export_sheet_action(action Action) !Action {
}
fn (mut m BizModel) export_graph_title_action(action Action) !Action {
return m.export_action(m.sheet.wiki_title_chart(row_args_from_params(action.params)!)!, action)
return m.export_action(m.sheet.wiki_title_chart(row_args_from_params(action.params)!)!,
action)
}
fn (mut m BizModel) export_graph_line_action(action Action) !Action {
return m.export_action(m.sheet.wiki_line_chart(row_args_from_params(action.params)!)!, action)
return m.export_action(m.sheet.wiki_line_chart(row_args_from_params(action.params)!)!,
action)
}
fn (mut m BizModel) export_graph_bar_action(action Action) !Action {
return m.export_action(m.sheet.wiki_bar_chart(row_args_from_params(action.params)!)!, action)
return m.export_action(m.sheet.wiki_bar_chart(row_args_from_params(action.params)!)!,
action)
}
pub fn (mut m BizModel) export_graph_pie_action(action Action) !Action {
return m.export_action(m.sheet.wiki_pie_chart(row_args_from_params(action.params)!)!, action)
return m.export_action(m.sheet.wiki_pie_chart(row_args_from_params(action.params)!)!,
action)
}
pub fn (mut m BizModel) export_overview_action(action Action) !Action {
return m.export_action(m.sheet.wiki_row_overview(row_args_from_params(action.params)!)!, action)
return m.export_action(m.sheet.wiki_row_overview(row_args_from_params(action.params)!)!,
action)
}
fn (mut m BizModel) new_report_action(action Action) !Action {
@@ -133,8 +138,12 @@ pub fn row_args_from_params(p Params) !RowGetArgs {
// creates the name for a file being exported given the params of the export action
fn (m BizModel) export_action(content string, action Action) !Action {
// determine name of file being exported
name := if action.params.exists('name') { action.params.get('name')! } else {
if action.params.exists('title') { action.params.get('title')! } else {
name := if action.params.exists('name') {
action.params.get('name')!
} else {
if action.params.exists('title') {
action.params.get('title')!
} else {
// if no name or title, name is ex: revenue_total_graph_bar_row
rowname := action.params.get_default('rowname', '')!
'${rowname}_${action.name}'

View File

@@ -34,7 +34,7 @@ pub fn (b BizModel) new_report(report Report) !Report {
b.write_cost_structure(path.path)!
return Report{
...report,
...report
name: name
path: path.path
}
@@ -73,18 +73,18 @@ pub fn (r Report) export(export Export) ! {
init: true
config: docusaurus.Config{
navbar: docusaurus.Navbar{
title: "Business Model",
title: 'Business Model'
items: [
docusaurus.NavbarItem{
"href": "https://threefold.info/kristof/",
"label": "ThreeFold Technology",
"position": "right"
href: 'https://threefold.info/kristof/'
label: 'ThreeFold Technology'
position: 'right'
},
docusaurus.NavbarItem{
"href": "https://threefold.io",
"label": "Operational Plan",
"position": "left"
}
href: 'https://threefold.io'
label: 'Operational Plan'
position: 'left'
},
]
}
main: docusaurus.Main{
@@ -94,7 +94,9 @@ pub fn (r Report) export(export Export) ! {
)!
site.generate()!
}
.mdbook {panic('MDBook export not fully implemented')}
.mdbook {
panic('MDBook export not fully implemented')
}
}
}
@@ -114,14 +116,21 @@ pub fn (model BizModel) write_operational_plan(path string) ! {
hr_page.template_write($tmpl('./templates/human_resources.md'), true)!
for key, employee in model.employees {
mut employee_page := pathlib.get_file(path: '${hr_dir.path}/${texttools.snake_case(employee.name)}.md')!
employee_cost_chart := model.sheet.line_chart(rowname:'hr_cost_${employee.name}', unit: .million)!.mdx()
mut employee_page := pathlib.get_file(
path: '${hr_dir.path}/${texttools.snake_case(employee.name)}.md'
)!
employee_cost_chart := model.sheet.line_chart(
rowname: 'hr_cost_${employee.name}'
unit: .million
)!.mdx()
employee_page.template_write($tmpl('./templates/employee.md'), true)!
}
mut depts_dir := pathlib.get_dir(path: '${dir.path}/departments')!
for key, department in model.departments {
mut dept_page := pathlib.get_file(path: '${depts_dir.path}/${texttools.snake_case(department.name)}.md')!
mut dept_page := pathlib.get_file(
path: '${depts_dir.path}/${texttools.snake_case(department.name)}.md'
)!
// dept_cost_chart := model.sheet.line_chart(rowname:'hr_cost_${employee.name}', unit: .million)!.mdx()
// println(employee_cost_chart)
dept_page.template_write($tmpl('./templates/department.md'), true)!
@@ -139,7 +148,9 @@ pub fn (model BizModel) write_revenue_model(path string) ! {
name1 := 'example'
for key, product in model.products {
mut product_page := pathlib.get_file(path: '${products_dir.path}/${texttools.snake_case(product.name)}.md')!
mut product_page := pathlib.get_file(
path: '${products_dir.path}/${texttools.snake_case(product.name)}.md'
)!
product_page.template_write($tmpl('./templates/product.md'), true)!
}
}

View File

@@ -32,13 +32,17 @@ pub fn (model BizModel) export_csv(args ExportCSVArgs) ! {
employee.role,
employee.department,
employee.cost.str(),
if start_date := employee.start_date { start_date.str() } else { '' }
if start_date := employee.start_date { start_date.str() } else { '' },
].map(fn [args] (s string) string {
return format_csv_value(s, args.separator)
}).join(args.separator)
employees_data << row
}
mut emp_file := pathlib.get_file(path: os.join_path(dir.path, 'employees.csv'), create: true, delete: true)!
mut emp_file := pathlib.get_file(
path: os.join_path(dir.path, 'employees.csv')
create: true
delete: true
)!
emp_file.write(employees_data.join('\n'))!
// Export products data
@@ -57,7 +61,11 @@ pub fn (model BizModel) export_csv(args ExportCSVArgs) ! {
}).join(args.separator)
products_data << row
}
mut prod_file := pathlib.get_file(path: os.join_path(dir.path, 'products.csv'), create: true, delete: true)!
mut prod_file := pathlib.get_file(
path: os.join_path(dir.path, 'products.csv')
create: true
delete: true
)!
prod_file.write(products_data.join('\n'))!
// Export departments data
@@ -70,13 +78,17 @@ pub fn (model BizModel) export_csv(args ExportCSVArgs) ! {
for _, department in model.departments {
row := [
department.name,
department.description
department.description,
].map(fn [args] (s string) string {
return format_csv_value(s, args.separator)
}).join(args.separator)
departments_data << row
}
mut dept_file := pathlib.get_file(path: os.join_path(dir.path, 'departments.csv'), create: true, delete: true)!
mut dept_file := pathlib.get_file(
path: os.join_path(dir.path, 'departments.csv')
create: true
delete: true
)!
dept_file.write(departments_data.join('\n'))!
}

View File

@@ -10,5 +10,7 @@ pub fn test_export_report() ! {
model := getset(bizmodel_name)!
model.export_report(Report{
title: 'My Business Model'
}, path: export_path)!
},
path: export_path
)!
}

View File

@@ -33,7 +33,6 @@ pub fn getset(name string) !&BizModel {
panic('bug')
}
pub fn generate(name string, path string) !&BizModel {
mut model := getset(name)!
mut pb := playbook.new(path: path)!
@@ -41,8 +40,6 @@ pub fn generate(name string, path string) !&BizModel {
return model
}
pub fn set(bizmodel BizModel) {
lock bizmodels {
bizmodels[bizmodel.name] = &bizmodel

View File

@@ -2,7 +2,7 @@ module bizmodel
import arrays
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.playbook { PlayBook, Action }
import freeflowuniverse.herolib.core.playbook { Action, PlayBook }
import freeflowuniverse.herolib.ui.console
// import freeflowuniverse.herolib.core.texttools
// import freeflowuniverse.herolib.ui.console
@@ -16,12 +16,10 @@ const action_priorities = {
pub fn play(mut plbook PlayBook) ! {
// group actions by which bizmodel they belong to
actions_by_biz := arrays.group_by[string, &Action](
plbook.actions_find(actor: 'bizmodel')!,
actions_by_biz := arrays.group_by[string, &Action](plbook.actions_find(actor: 'bizmodel')!,
fn (a &Action) string {
return a.params.get('bizname') or { 'default' }
}
)
})
// play actions for each biz in playbook
for biz, actions in actions_by_biz {
@@ -33,7 +31,6 @@ pub fn play(mut plbook PlayBook) ! {
pub fn (mut m BizModel) play(mut plbook PlayBook) ! {
mut actions := plbook.actions_find(actor: 'bizmodel')!
for action in actions.filter(it.name in action_priorities[0]) {
console.print_debug(action)
m.act(*action)!

View File

@@ -173,7 +173,6 @@ fn (mut m BizModel) revenue_action(action Action) !Action {
aggregatetype: .avg
)!
mut nr_sold := m.sheet.row_new(
name: '${name}_nr_sold'
growth: action.params.get_default('nr_sold', '0')!
@@ -254,9 +253,6 @@ fn (mut m BizModel) revenue_action(action Action) !Action {
name: '${name}_cogs_monthly_from_perc'
)!
// mut cogs_from_perc:=cogs_perc.action(action:.multiply,rows:[revenue],name:"cogs_from_perc")!
// DEAL WITH MAINTENANCE
@@ -322,7 +318,8 @@ fn (mut m BizModel) revenue_action(action Action) !Action {
revenue_total = revenue_total.action(
action: .add
rows: [revenue, revenue_items, revenue_growth, revenue_item_total, revenue_monthly_total, revenue_setup_total, maintenance_month]
rows: [revenue, revenue_items, revenue_growth, revenue_item_total, revenue_monthly_total,
revenue_setup_total, maintenance_month]
)!
if revenue_total.max() > 0 {

View File

@@ -1,4 +1,5 @@
module spreadsheet
import os
import freeflowuniverse.herolib.core.pathlib
@@ -41,7 +42,11 @@ pub fn (mut s Sheet) export(args ExportArgs) !string {
}
if args.path.len > 0 {
mut p:=pathlib.get_file(path:args.path.replace("~",os.home_dir()), create:true, delete:true)!
mut p := pathlib.get_file(
path: args.path.replace('~', os.home_dir())
create: true
delete: true
)!
p.write(result.join('\n'))!
}

View File

@@ -58,7 +58,7 @@ pub fn (mut s Sheet) export_csv(args ExportCSVArgs) !string {
format_csv_value(row.description, separator),
format_csv_value(row.aggregatetype.str(), separator),
format_csv_value(row.tags, separator),
format_csv_value(row.subgroup, separator)
format_csv_value(row.subgroup, separator),
]
for cell in row.cells {
@@ -73,7 +73,11 @@ pub fn (mut s Sheet) export_csv(args ExportCSVArgs) !string {
}
if args.path.len > 0 {
mut p := pathlib.get_file(path: args.path.replace('~', os.home_dir()), create: true, delete: true)!
mut p := pathlib.get_file(
path: args.path.replace('~', os.home_dir())
create: true
delete: true
)!
p.write(result.join('\n'))!
}

View File

@@ -6,14 +6,14 @@ fn test_sheet_export_csv() {
// Add some test rows with data
mut row1 := sheet.row_new(
name: 'row1',
descr: 'First test row',
name: 'row1'
descr: 'First test row'
tags: 'test,row,first'
)!
mut row2 := sheet.row_new(
name: 'row2',
descr: 'Second test row with | pipe character',
name: 'row2'
descr: 'Second test row with | pipe character'
tags: 'test,row,second'
)!
@@ -55,8 +55,8 @@ fn test_sheet_export_csv() {
// Test with custom separator and include_empty option
csv_output2 := sheet.export_csv(
path: '',
separator: ',',
path: ''
separator: ','
include_empty: true
)!
lines2 := csv_output2.split('\n')
@@ -70,7 +70,7 @@ fn test_sheet_export_csv() {
// Test with different separator
csv_output3 := sheet.export_csv(
path: '',
path: ''
separator: ';'
)!
lines3 := csv_output3.split('\n')

View File

@@ -1,6 +1,5 @@
module actionprocessor
import freeflowuniverse.herolib.circles.core.db as core_db
import freeflowuniverse.herolib.circles.mcc.db as mcc_db
import freeflowuniverse.herolib.circles.actions.db as actions_db
@@ -12,7 +11,6 @@ __global (
circle_default string
)
// HeroRunner is the main factory for managing jobs, agents, services, circles and names
@[heap]
pub struct CircleCoordinator {
@@ -27,11 +25,10 @@ pub mut:
session_state SessionState
}
@[params]
pub struct CircleCoordinatorArgs {
pub mut:
name string = "local"
name string = 'local'
pubkey string // pubkey of user who called this
addr string // mycelium address
path string
@@ -43,7 +40,7 @@ pub fn new(args_ CircleCoordinatorArgs) !&CircleCoordinator {
args.name = texttools.name_fix(args.name)
if args.name in circle_global {
mut c:=circle_global[args.name] or {panic("bug")}
mut c := circle_global[args.name] or { panic('bug') }
return c
}
@@ -61,14 +58,25 @@ pub fn new(args_ CircleCoordinatorArgs) !&CircleCoordinator {
// os.mkdir_all(os.join_path(mypath, 'meta_core'))!
// os.mkdir_all(os.join_path(mypath, 'meta_mcc'))! //message, contacts, calendar
// Initialize the db handlers with proper ourdb instances
mut agent_db := core_db.new_agentdb(session_state) or { return error('Failed to initialize agent_db: ${err}') }
mut circle_db := core_db.new_circledb(session_state) or { return error('Failed to initialize circle_db: ${err}') }
mut name_db := core_db.new_namedb(session_state) or { return error('Failed to initialize name_db: ${err}') }
mut mail_db := mcc_db.new_maildb(session_state) or { return error('Failed to initialize mail_db: ${err}') }
mut calendar_db := mcc_db.new_calendardb(session_state) or { return error('Failed to initialize calendar_db: ${err}') }
mut job_db := actions_db.new_jobdb(session_state) or { return error('Failed to initialize job_db: ${err}') }
mut agent_db := core_db.new_agentdb(session_state) or {
return error('Failed to initialize agent_db: ${err}')
}
mut circle_db := core_db.new_circledb(session_state) or {
return error('Failed to initialize circle_db: ${err}')
}
mut name_db := core_db.new_namedb(session_state) or {
return error('Failed to initialize name_db: ${err}')
}
mut mail_db := mcc_db.new_maildb(session_state) or {
return error('Failed to initialize mail_db: ${err}')
}
mut calendar_db := mcc_db.new_calendardb(session_state) or {
return error('Failed to initialize calendar_db: ${err}')
}
mut job_db := actions_db.new_jobdb(session_state) or {
return error('Failed to initialize job_db: ${err}')
}
mut cm := &CircleCoordinator{
agents: &agent_db

View File

@@ -1,7 +1,7 @@
module db
import freeflowuniverse.herolib.circles.base { DBHandler, SessionState, new_dbhandler }
import freeflowuniverse.herolib.circles.actions.models { Job, job_loads, JobStatus }
import freeflowuniverse.herolib.circles.actions.models { Job, JobStatus }
@[heap]
pub struct JobDB {

View File

@@ -3,7 +3,7 @@ module db
import os
import rand
import freeflowuniverse.herolib.circles.actionprocessor
import freeflowuniverse.herolib.circles.actions.models { Status, JobStatus }
import freeflowuniverse.herolib.circles.actions.models { JobStatus, Status }
import freeflowuniverse.herolib.data.ourtime
fn test_job_db() {
@@ -192,7 +192,8 @@ fn test_job_db() {
// Verify no jobs remain
jobs_after_all_deleted := runner.jobs.getall() or {
// This is expected to fail with 'No jobs found' error
assert err.msg().contains('No index keys defined for this type') || err.msg().contains('No jobs found')
assert err.msg().contains('No index keys defined for this type')
|| err.msg().contains('No jobs found')
[]models.Job{cap: 0}
}
assert jobs_after_all_deleted.len == 0, 'Expected 0 jobs after all deletions, got ${jobs_after_all_deleted.len}'

View File

@@ -1,7 +1,7 @@
module play
import freeflowuniverse.herolib.data.ourtime
import freeflowuniverse.herolib.circles.actions.models { Job, JobStatus, Status }
import freeflowuniverse.herolib.circles.actions.models { JobStatus, Status }
import freeflowuniverse.herolib.data.paramsparser
import crypto.rand
import encoding.hex

View File

@@ -28,7 +28,7 @@ pub fn (mut p Player) list(params paramsparser.Params) ! {
// Return result based on format
match p.return_format {
.heroscript {
println('!!job.list_result count:${ids.len} ids:\'${ids.map(it.str()).join(",")}\'')
println('!!job.list_result count:${ids.len} ids:\'${ids.map(it.str()).join(',')}\'')
}
.json {
println('{"action": "job.list_result", "count": ${ids.len}, "ids": ${json.encode(ids)}}')

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.circles.actions.play { Player, ReturnFormat }
import freeflowuniverse.herolib.circles.actions.play { ReturnFormat }
import os
import flag

View File

@@ -1,7 +1,7 @@
module play
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.circles.base { Databases, SessionState, new_session }
import freeflowuniverse.herolib.circles.base { SessionState, new_session }
import freeflowuniverse.herolib.circles.actions.db { JobDB, new_jobdb }
import os

View File

@@ -13,12 +13,24 @@ pub fn (mut p Player) update_status(params paramsparser.Params) ! {
// Convert status string to Status enum
mut new_status := Status.created
match status_str {
'created' { new_status = Status.created }
'scheduled' { new_status = Status.scheduled }
'planned' { new_status = Status.planned }
'running' { new_status = Status.running }
'error' { new_status = Status.error }
'ok' { new_status = Status.ok }
'created' {
new_status = Status.created
}
'scheduled' {
new_status = Status.scheduled
}
'planned' {
new_status = Status.planned
}
'running' {
new_status = Status.running
}
'error' {
new_status = Status.error
}
'ok' {
new_status = Status.ok
}
else {
return error('Invalid status value: ${status_str}')
}

Some files were not shown because too many files have changed in this diff Show More