This commit is contained in:
2025-10-13 05:36:06 +04:00
parent 44c12281d3
commit 10d1dc943c
41 changed files with 0 additions and 0 deletions

View File

@@ -1,2 +0,0 @@
main

View File

@@ -1,16 +0,0 @@
#!/bin/bash
set -ex
export name="mcp_rhai"
# Change to the directory containing this script
cd "$(dirname "$0")"
# Compile the V program
v -n -w -gc none -cc tcc -d use_openssl -enable-globals main.v
# Ensure the binary is executable
chmod +x main
mv main ~/hero/bin/${name}
echo "Compilation successful. Binary '${name}' is ready."

View File

@@ -1,17 +0,0 @@
module main
import incubaid.herolib.ai.mcp.rhai.mcp
fn main() {
// Create a new MCP server
mut server := mcp.new_mcp_server() or {
// Note: Removed log.error() as it interferes with STDIO transport JSON-RPC communication
return
}
// Start the server
server.start() or {
// Note: Removed log.error() as it interferes with STDIO transport JSON-RPC communication
return
}
}

View File

@@ -1,532 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import incubaid.herolib.ai.mcp.aitools.escalayer
import os
fn main() {
// Get the current directory
current_dir := os.dir(@FILE)
// Check if a source code path was provided as an argument
if os.args.len < 2 {
println('Please provide the path to the source code directory as an argument')
println('Example: ./example.vsh /path/to/source/code/directory')
return
}
// Get the source code path from the command line arguments
source_code_path := os.args[1]
// Check if the path exists and is a directory
if !os.exists(source_code_path) {
println('Source code path does not exist: ${source_code_path}')
return
}
if !os.is_dir(source_code_path) {
println('Source code path is not a directory: ${source_code_path}')
return
}
// Get all Rust files in the directory
files := os.ls(source_code_path) or {
println('Failed to list files in directory: ${err}')
return
}
// Combine all Rust files into a single source code string
mut source_code := ''
for file in files {
file_path := os.join_path(source_code_path, file)
// Skip directories and non-Rust files
if os.is_dir(file_path) || !file.ends_with('.rs') {
continue
}
// Read the file content
file_content := os.read_file(file_path) or {
println('Failed to read file ${file_path}: ${err}')
continue
}
// Add file content to the combined source code
source_code += '// File: ${file}\n${file_content}\n\n'
}
if source_code == '' {
println('No Rust files found in directory: ${source_code_path}')
return
}
// Read the rhaiwrapping.md file
rhai_wrapping_md := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping.md') or {
println('Failed to read rhaiwrapping.md: ${err}')
return
}
// Determine the crate path from the source code path
// Extract the path relative to the src directory
src_index := source_code_path.index('src/') or {
println('Could not determine crate path: src/ not found in path')
return
}
mut path_parts := source_code_path[src_index + 4..].split('/')
// Remove the last part (the file name)
if path_parts.len > 0 {
path_parts.delete_last()
}
rel_path := path_parts.join('::')
crate_path := 'sal::${rel_path}'
// Create a new task
mut task := escalayer.new_task(
name: 'rhai_wrapper_creator.escalayer'
description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file'
)
// Create model configs
sonnet_model := escalayer.ModelConfig{
name: 'anthropic/claude-3.7-sonnet'
provider: 'anthropic'
temperature: 0.7
max_tokens: 25000
}
gpt4_model := escalayer.ModelConfig{
name: 'gpt-4'
provider: 'openai'
temperature: 0.7
max_tokens: 25000
}
// Extract the module name from the directory path (last component)
dir_parts := source_code_path.split('/')
name := dir_parts[dir_parts.len - 1]
// Create the prompt with source code, wrapper example, and rhai_wrapping_md
prompt_content := create_rhai_wrappers(name, source_code, os.read_file('${current_dir}/prompts/example_script.md') or {
''
}, os.read_file('${current_dir}/prompts/wrapper.md') or { '' }, os.read_file('${current_dir}/prompts/errors.md') or {
''
}, crate_path)
// Create a prompt function that returns the prepared content
prompt_function := fn [prompt_content] (input string) string {
return prompt_content
}
gen := RhaiGen{
name: name
dir: source_code_path
}
// Define a single unit task that handles everything
task.new_unit_task(
name: 'create_rhai_wrappers'
prompt_function: prompt_function
callback_function: gen.process_rhai_wrappers
base_model: sonnet_model
retry_model: gpt4_model
retry_count: 1
)
// Initiate the task
result := task.initiate('') or {
println('Task failed: ${err}')
return
}
println('Task completed successfully')
println('The wrapper files have been generated and compiled in the target directory.')
println('Check /Users/timurgordon/code/git.threefold.info/herocode/sal/src/rhai for the compiled output.')
}
// Define the prompt functions
fn separate_functions(input string) string {
return 'Read the following Rust code and separate it into functions. Identify all the methods in the Container implementation and their purposes.\n\n${input}'
}
fn create_wrappers(input string) string {
return 'Create Rhai wrappers for the Rust functions identified in the previous step. The wrappers should follow the builder pattern and provide a clean API for use in Rhai scripts. Include error handling and type conversion.\n\n${input}'
}
fn create_example(input string) string {
return 'Create a Rhai example script that demonstrates how to use the wrapper functions. The example should be based on the provided example.rs file but adapted for Rhai syntax. Create a web server example that uses the container functions.\n\n${input}'
}
// Define a Rhai wrapper generator function for Container functions
fn create_rhai_wrappers(name string, source_code string, example_rhai string, wrapper_md string, errors_md string, crate_path string) string {
guides := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping_classicai.md') or {
panic('Failed to read guides')
}
engine := $tmpl('./prompts/engine.md')
vector_vs_array := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_array_vs_vector.md') or {
panic('Failed to read guides')
}
rhai_integration_fixes := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_integration_fixes.md') or {
panic('Failed to read guides')
}
rhai_syntax_guide := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_syntax_guide.md') or {
panic('Failed to read guides')
}
generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs')
return 'You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files.
${guides}
${vector_vs_array}
${example_rhai}
${wrapper_md}
## Common Errors to Avoid
${errors_md}
${rhai_integration_fixes}
${rhai_syntax_guide}
## Your Task
Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers:
## Rust Code to Wrap
```rust
${source_code}
```
IMPORTANT NOTES:
1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use
2. The following dependencies are available in Cargo.toml:
- rhai = "1.21.0"
- serde = { version = "1.0", features = ["derive"] }
- serde_json = "1.0"
- sal = { path = "../../../" }
3. For the wrapper: `use sal::${name};` this way you can access the module functions and objects with ${name}::
4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there.
```rust
${generic_wrapper_rs}
```
5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary.
- For example, return `Result<String, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a string
- Use `Result<bool, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a boolean
- Use `Result<Vec<String>, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a list of strings
6. Your code should include public functions that can be called from Rhai scripts
7. Make sure to implement all necessary helper functions for type conversion
8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine
9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings
10. When returning path references, convert them to owned strings (e.g., path().to_string())
11. For error handling, use proper Result types with Box<EvalAltResult> for the error type:
```rust
// INCORRECT:
pub fn some_function(arg: &str) -> Dynamic {
match some_operation(arg) {
Ok(result) => Dynamic::from(result),
Err(err) => Dynamic::from(format!("Error: {}", err))
}
}
// CORRECT:
pub fn some_function(arg: &str) -> Result<String, Box<EvalAltResult>> {
some_operation(arg).map_err(|err| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Error: {}", err).into(),
rhai::Position::NONE
))
})
}
```
12. IMPORTANT: Format your response with the code between triple backticks as follows:
```rust
// wrapper.rs
// Your wrapper implementation here
```
```rust
// engine.rs
// Your engine.rs implementation here
```
```rhai
// example.rhai
// Your example Rhai script here
```
13. The example.rhai script should demonstrate the use of all the wrapper functions you create
14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example:
${engine}
MOST IMPORTANT:
import package being wrapped as `use sal::<name>`
your engine create function is called `create_rhai_engine`
```
'
}
@[params]
pub struct WrapperModule {
pub:
lib_rs string
example_rs string
engine_rs string
cargo_toml string
example_rhai string
generic_wrapper_rs string
wrapper_rs string
}
// functions is a list of function names that AI should extract and pass in
fn create_wrapper_module(wrapper WrapperModule, functions []string, name_ string, base_dir string) !string {
// Define project directory paths
name := name_
project_dir := '${base_dir}/rhai'
// Create the project using cargo new --lib
if os.exists(project_dir) {
os.rmdir_all(project_dir) or {
return error('Failed to clean existing project directory: ${err}')
}
}
// Run cargo new --lib to create the project
os.chdir(base_dir) or { return error('Failed to change directory to base directory: ${err}') }
cargo_new_result := os.execute('cargo new --lib rhai')
if cargo_new_result.exit_code != 0 {
return error('Failed to create new library project: ${cargo_new_result.output}')
}
// Create examples directory
examples_dir := '${project_dir}/examples'
os.mkdir_all(examples_dir) or { return error('Failed to create examples directory: ${err}') }
// Write the lib.rs file
if wrapper.lib_rs != '' {
os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or {
return error('Failed to write lib.rs: ${err}')
}
}
// Write the wrapper.rs file
if wrapper.wrapper_rs != '' {
os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or {
return error('Failed to write wrapper.rs: ${err}')
}
}
// Write the generic wrapper.rs file
if wrapper.generic_wrapper_rs != '' {
os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or {
return error('Failed to write generic wrapper.rs: ${err}')
}
}
// Write the example.rs file
if wrapper.example_rs != '' {
os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or {
return error('Failed to write example.rs: ${err}')
}
}
// Write the engine.rs file if provided
if wrapper.engine_rs != '' {
os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or {
return error('Failed to write engine.rs: ${err}')
}
}
// Write the Cargo.toml file
if wrapper.cargo_toml != '' {
os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or {
return error('Failed to write Cargo.toml: ${err}')
}
}
// Write the example.rhai file if provided
if wrapper.example_rhai != '' {
os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or {
return error('Failed to write example.rhai: ${err}')
}
}
return project_dir
}
// Helper function to extract code blocks from the response
fn extract_code_block(response string, identifier string, language string) string {
// Find the start marker for the code block
mut start_marker := '```${language}\n// ${identifier}'
if language == '' {
start_marker = '```\n// ${identifier}'
}
start_index := response.index(start_marker) or {
// Try alternative format
mut alt_marker := '```${language}\n${identifier}'
if language == '' {
alt_marker = '```\n${identifier}'
}
response.index(alt_marker) or { return '' }
}
// Find the end marker
end_marker := '```'
end_index := response.index_after(end_marker, start_index + start_marker.len) or { return '' }
// Extract the content between the markers
content_start := start_index + start_marker.len
content := response[content_start..end_index].trim_space()
return content
}
// Extract module name from wrapper code
fn extract_module_name(code string) string {
lines := code.split('\n')
for line in lines {
// Look for pub mod or mod declarations
if line.contains('pub mod ') || line.contains('mod ') {
// Extract module name
mut parts := []string{}
if line.contains('pub mod ') {
parts = line.split('pub mod ')
} else {
parts = line.split('mod ')
}
if parts.len > 1 {
// Extract the module name and remove any trailing characters
mut name := parts[1].trim_space()
// Remove any trailing { or ; or whitespace
name = name.trim_right('{').trim_right(';').trim_space()
if name != '' {
return name
}
}
}
}
return ''
}
struct RhaiGen {
name string
dir string
}
// Define the callback function that processes the response and compiles the code
fn (gen RhaiGen) process_rhai_wrappers(response string) !string {
// Extract wrapper.rs content
wrapper_rs_content := extract_code_block(response, 'wrapper.rs', 'rust')
if wrapper_rs_content == '' {
return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```')
}
// Extract engine.rs content
mut engine_rs_content := extract_code_block(response, 'engine.rs', 'rust')
if engine_rs_content == '' {
// Try to extract from the response without explicit language marker
engine_rs_content = extract_code_block(response, 'engine.rs', '')
// if engine_rs_content == '' {
// // Use the template engine.rs
// engine_rs_content = $tmpl('./templates/engine.rs')
// }
}
// Extract example.rhai content
mut example_rhai_content := extract_code_block(response, 'example.rhai', 'rhai')
if example_rhai_content == '' {
// Try to extract from the response without explicit language marker
example_rhai_content = extract_code_block(response, 'example.rhai', '')
if example_rhai_content == '' {
// Use the example from the template
example_script_md := os.read_file('${os.dir(@FILE)}/prompts/example_script.md') or {
return error('Failed to read example.rhai template: ${err}')
}
// Extract the code block from the markdown file
example_rhai_content = extract_code_block(example_script_md, 'example.rhai',
'rhai')
if example_rhai_content == '' {
return error('Failed to extract example.rhai from template file')
}
}
}
// Extract function names from the wrapper.rs content
functions := extract_functions_from_code(wrapper_rs_content)
println('Using module name: ${gen.name}_rhai')
println('Extracted functions: ${functions.join(', ')}')
name := gen.name
// Create a WrapperModule struct with the extracted content
wrapper := WrapperModule{
lib_rs: $tmpl('./templates/lib.rs')
wrapper_rs: wrapper_rs_content
example_rs: $tmpl('./templates/example.rs')
engine_rs: engine_rs_content
generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs')
cargo_toml: $tmpl('./templates/cargo.toml')
example_rhai: example_rhai_content
}
// Create the wrapper module
base_target_dir := gen.dir
project_dir := create_wrapper_module(wrapper, functions, gen.name, base_target_dir) or {
return error('Failed to create wrapper module: ${err}')
}
// Run the example
os.chdir(project_dir) or { return error('Failed to change directory to project: ${err}') }
// Run cargo build first
build_result := os.execute('cargo build')
if build_result.exit_code != 0 {
return error('Compilation failed. Please fix the following errors and ensure your code is compatible with the existing codebase:\n\n${build_result.output}')
}
// Run the example
run_result := os.execute('cargo run --example example')
return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_result.output}\n\nRun output:\n${run_result.output}'
}
// Extract function names from wrapper code
fn extract_functions_from_code(code string) []string {
mut functions := []string{}
lines := code.split('\n')
for line in lines {
if line.contains('pub fn ') && !line.contains('//') {
// Extract function name
parts := line.split('pub fn ')
if parts.len > 1 {
name_parts := parts[1].split('(')
if name_parts.len > 0 {
fn_name := name_parts[0].trim_space()
if fn_name != '' {
functions << fn_name
}
}
}
}
}
return functions
}

View File

@@ -1,596 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import incubaid.herolib.ai.mcp.aitools.escalayer
import os
fn main() {
// Get the current directory where this script is located
current_dir := os.dir(@FILE)
// Validate command line arguments
source_code_path := validate_command_args() or {
println(err)
return
}
// Read and combine all Rust files in the source directory
source_code := read_source_code(source_code_path) or {
println(err)
return
}
// Determine the crate path from the source code path
crate_path := determine_crate_path(source_code_path) or {
println(err)
return
}
// Extract the module name from the directory path (last component)
name := extract_module_name_from_path(source_code_path)
// Create the prompt content for the AI
prompt_content := create_rhai_wrappers(name, source_code, read_file_safely('${current_dir}/prompts/example_script.md'),
read_file_safely('${current_dir}/prompts/wrapper.md'), read_file_safely('${current_dir}/prompts/errors.md'),
crate_path)
// Create the generator instance
gen := RhaiGen{
name: name
dir: source_code_path
}
// Run the task to generate Rhai wrappers
run_wrapper_generation_task(prompt_content, gen) or {
println('Task failed: ${err}')
return
}
println('Task completed successfully')
println('The wrapper files have been generated and compiled in the target directory.')
println('Check /Users/timurgordon/code/git.threefold.info/herocode/sal/src/rhai for the compiled output.')
}
// Validates command line arguments and returns the source code path
fn validate_command_args() !string {
if os.args.len < 2 {
return error('Please provide the path to the source code directory as an argument\nExample: ./example.vsh /path/to/source/code/directory')
}
source_code_path := os.args[1]
if !os.exists(source_code_path) {
return error('Source code path does not exist: ${source_code_path}')
}
if !os.is_dir(source_code_path) {
return error('Source code path is not a directory: ${source_code_path}')
}
return source_code_path
}
// Reads and combines all Rust files in the given directory
fn read_source_code(source_code_path string) !string {
// Get all files in the directory
files := os.ls(source_code_path) or {
return error('Failed to list files in directory: ${err}')
}
// Combine all Rust files into a single source code string
mut source_code := ''
for file in files {
file_path := os.join_path(source_code_path, file)
// Skip directories and non-Rust files
if os.is_dir(file_path) || !file.ends_with('.rs') {
continue
}
// Read the file content
file_content := os.read_file(file_path) or {
println('Failed to read file ${file_path}: ${err}')
continue
}
// Add file content to the combined source code
source_code += '// File: ${file}\n${file_content}\n\n'
}
if source_code == '' {
return error('No Rust files found in directory: ${source_code_path}')
}
return source_code
}
// Determines the crate path from the source code path
fn determine_crate_path(source_code_path string) !string {
// Extract the path relative to the src directory
src_index := source_code_path.index('src/') or {
return error('Could not determine crate path: src/ not found in path')
}
mut path_parts := source_code_path[src_index + 4..].split('/')
// Remove the last part (the file name)
if path_parts.len > 0 {
path_parts.delete_last()
}
rel_path := path_parts.join('::')
return 'sal::${rel_path}'
}
// Extracts the module name from a directory path
fn extract_module_name_from_path(path string) string {
dir_parts := path.split('/')
return dir_parts[dir_parts.len - 1]
}
// Helper function to read a file or return empty string if file doesn't exist
fn read_file_safely(file_path string) string {
return os.read_file(file_path) or { '' }
}
// Runs the task to generate Rhai wrappers
fn run_wrapper_generation_task(prompt_content string, gen RhaiGen) !string {
// Create a new task
mut task := escalayer.new_task(
name: 'rhai_wrapper_creator.escalayer'
description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file'
)
// Create model configs
sonnet_model := escalayer.ModelConfig{
name: 'anthropic/claude-3.7-sonnet'
provider: 'anthropic'
temperature: 0.7
max_tokens: 25000
}
gpt4_model := escalayer.ModelConfig{
name: 'gpt-4'
provider: 'openai'
temperature: 0.7
max_tokens: 25000
}
// Create a prompt function that returns the prepared content
prompt_function := fn [prompt_content] (input string) string {
return prompt_content
}
// Define a single unit task that handles everything
task.new_unit_task(
name: 'create_rhai_wrappers'
prompt_function: prompt_function
callback_function: gen.process_rhai_wrappers
base_model: sonnet_model
retry_model: gpt4_model
retry_count: 1
)
// Initiate the task
return task.initiate('')
}
// Define a Rhai wrapper generator function for Container functions
fn create_rhai_wrappers(name string, source_code string, example_rhai string, wrapper_md string, errors_md string, crate_path string) string {
// Load all required template and guide files
guides := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping_classicai.md')
engine := $tmpl('./prompts/engine.md')
vector_vs_array := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_array_vs_vector.md')
rhai_integration_fixes := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_integration_fixes.md')
rhai_syntax_guide := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_syntax_guide.md')
generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs')
// Build the prompt content
return build_prompt_content(name, source_code, example_rhai, wrapper_md, errors_md,
guides, vector_vs_array, rhai_integration_fixes, rhai_syntax_guide, generic_wrapper_rs,
engine)
}
// Helper function to load guide files with error handling
fn load_guide_file(path string) string {
return os.read_file(path) or {
eprintln('Warning: Failed to read guide file: ${path}')
return ''
}
}
// Builds the prompt content for the AI
fn build_prompt_content(name string, source_code string, example_rhai string, wrapper_md string,
errors_md string, guides string, vector_vs_array string,
rhai_integration_fixes string, rhai_syntax_guide string,
generic_wrapper_rs string, engine string) string {
return 'You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files.
${guides}
${vector_vs_array}
${example_rhai}
${wrapper_md}
## Common Errors to Avoid
${errors_md}
${rhai_integration_fixes}
${rhai_syntax_guide}
## Your Task
Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers:
## Rust Code to Wrap
```rust
${source_code}
```
IMPORTANT NOTES:
1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use
2. The following dependencies are available in Cargo.toml:
- rhai = "1.21.0"
- serde = { version = "1.0", features = ["derive"] }
- serde_json = "1.0"
- sal = { path = "../../../" }
3. For the wrapper: `use sal::${name};` this way you can access the module functions and objects with ${name}::
4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there.
```rust
${generic_wrapper_rs}
```
5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary.
- For example, return `Result<String, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a string
- Use `Result<bool, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a boolean
- Use `Result<Vec<String>, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a list of strings
6. Your code should include public functions that can be called from Rhai scripts
7. Make sure to implement all necessary helper functions for type conversion
8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine
9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings
10. When returning path references, convert them to owned strings (e.g., path().to_string())
11. For error handling, use proper Result types with Box<EvalAltResult> for the error type:
```rust
// INCORRECT:
pub fn some_function(arg: &str) -> Dynamic {
match some_operation(arg) {
Ok(result) => Dynamic::from(result),
Err(err) => Dynamic::from(format!("Error: {}", err))
}
}
// CORRECT:
pub fn some_function(arg: &str) -> Result<String, Box<EvalAltResult>> {
some_operation(arg).map_err(|err| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Error: {}", err).into(),
rhai::Position::NONE
))
})
}
```
12. IMPORTANT: Format your response with the code between triple backticks as follows:
```rust
// wrapper.rs
// Your wrapper implementation here
```
```rust
// engine.rs
// Your engine.rs implementation here
```
```rhai
// example.rhai
// Your example Rhai script here
```
13. The example.rhai script should demonstrate the use of all the wrapper functions you create
14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example:
${engine}
MOST IMPORTANT:
import package being wrapped as `use sal::<n>`
your engine create function is called `create_rhai_engine`
```
'
}
@[params]
pub struct WrapperModule {
pub:
lib_rs string
example_rs string
engine_rs string
cargo_toml string
example_rhai string
generic_wrapper_rs string
wrapper_rs string
}
// functions is a list of function names that AI should extract and pass in
fn create_wrapper_module(wrapper WrapperModule, functions []string, name_ string, base_dir string) !string {
// Define project directory paths
name := name_
project_dir := '${base_dir}/rhai'
// Create the project using cargo new --lib
if os.exists(project_dir) {
os.rmdir_all(project_dir) or {
return error('Failed to clean existing project directory: ${err}')
}
}
// Run cargo new --lib to create the project
os.chdir(base_dir) or { return error('Failed to change directory to base directory: ${err}') }
cargo_new_result := os.execute('cargo new --lib rhai')
if cargo_new_result.exit_code != 0 {
return error('Failed to create new library project: ${cargo_new_result.output}')
}
// Create examples directory
examples_dir := '${project_dir}/examples'
os.mkdir_all(examples_dir) or { return error('Failed to create examples directory: ${err}') }
// Write the lib.rs file
if wrapper.lib_rs != '' {
os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or {
return error('Failed to write lib.rs: ${err}')
}
}
// Write the wrapper.rs file
if wrapper.wrapper_rs != '' {
os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or {
return error('Failed to write wrapper.rs: ${err}')
}
}
// Write the generic wrapper.rs file
if wrapper.generic_wrapper_rs != '' {
os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or {
return error('Failed to write generic wrapper.rs: ${err}')
}
}
// Write the example.rs file
if wrapper.example_rs != '' {
os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or {
return error('Failed to write example.rs: ${err}')
}
}
// Write the engine.rs file if provided
if wrapper.engine_rs != '' {
os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or {
return error('Failed to write engine.rs: ${err}')
}
}
// Write the Cargo.toml file
if wrapper.cargo_toml != '' {
os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or {
return error('Failed to write Cargo.toml: ${err}')
}
}
// Write the example.rhai file if provided
if wrapper.example_rhai != '' {
os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or {
return error('Failed to write example.rhai: ${err}')
}
}
return project_dir
}
// Helper function to extract code blocks from the response
fn extract_code_block(response string, identifier string, language string) string {
// Find the start marker for the code block
mut start_marker := '```${language}\n// ${identifier}'
if language == '' {
start_marker = '```\n// ${identifier}'
}
start_index := response.index(start_marker) or {
// Try alternative format
mut alt_marker := '```${language}\n${identifier}'
if language == '' {
alt_marker = '```\n${identifier}'
}
response.index(alt_marker) or { return '' }
}
// Find the end marker
end_marker := '```'
end_index := response.index_after(end_marker, start_index + start_marker.len) or { return '' }
// Extract the content between the markers
content_start := start_index + start_marker.len
content := response[content_start..end_index].trim_space()
return content
}
// Extract module name from wrapper code
fn extract_module_name(code string) string {
lines := code.split('\n')
for line in lines {
// Look for pub mod or mod declarations
if line.contains('pub mod ') || line.contains('mod ') {
// Extract module name
mut parts := []string{}
if line.contains('pub mod ') {
parts = line.split('pub mod ')
} else {
parts = line.split('mod ')
}
if parts.len > 1 {
// Extract the module name and remove any trailing characters
mut name := parts[1].trim_space()
// Remove any trailing { or ; or whitespace
name = name.trim_right('{').trim_right(';').trim_space()
if name != '' {
return name
}
}
}
}
return ''
}
// RhaiGen struct for generating Rhai wrappers
struct RhaiGen {
name string
dir string
}
// Process the AI response and compile the generated code
fn (gen RhaiGen) process_rhai_wrappers(response string) !string {
// Extract code blocks from the response
code_blocks := extract_code_blocks(response) or { return err }
// Extract function names from the wrapper.rs content
functions := extract_functions_from_code(code_blocks.wrapper_rs)
println('Using module name: ${gen.name}_rhai')
println('Extracted functions: ${functions.join(', ')}')
name := gen.name
// Create a WrapperModule struct with the extracted content
wrapper := WrapperModule{
lib_rs: $tmpl('./templates/lib.rs')
wrapper_rs: code_blocks.wrapper_rs
example_rs: $tmpl('./templates/example.rs')
engine_rs: code_blocks.engine_rs
generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs')
cargo_toml: $tmpl('./templates/cargo.toml')
example_rhai: code_blocks.example_rhai
}
// Create the wrapper module
project_dir := create_wrapper_module(wrapper, functions, gen.name, gen.dir) or {
return error('Failed to create wrapper module: ${err}')
}
// Build and run the project
build_output, run_output := build_and_run_project(project_dir) or { return err }
return format_success_message(project_dir, build_output, run_output)
}
// CodeBlocks struct to hold extracted code blocks
struct CodeBlocks {
wrapper_rs string
engine_rs string
example_rhai string
}
// Extract code blocks from the AI response
fn extract_code_blocks(response string) !CodeBlocks {
// Extract wrapper.rs content
wrapper_rs_content := extract_code_block(response, 'wrapper.rs', 'rust')
if wrapper_rs_content == '' {
return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```')
}
// Extract engine.rs content
mut engine_rs_content := extract_code_block(response, 'engine.rs', 'rust')
if engine_rs_content == '' {
// Try to extract from the response without explicit language marker
engine_rs_content = extract_code_block(response, 'engine.rs', '')
}
// Extract example.rhai content
mut example_rhai_content := extract_code_block(response, 'example.rhai', 'rhai')
if example_rhai_content == '' {
// Try to extract from the response without explicit language marker
example_rhai_content = extract_code_block(response, 'example.rhai', '')
if example_rhai_content == '' {
// Use the example from the template
example_rhai_content = load_example_from_template() or { return err }
}
}
return CodeBlocks{
wrapper_rs: wrapper_rs_content
engine_rs: engine_rs_content
example_rhai: example_rhai_content
}
}
// Load example.rhai from template file
fn load_example_from_template() !string {
example_script_md := os.read_file('${os.dir(@FILE)}/prompts/example_script.md') or {
return error('Failed to read example.rhai template: ${err}')
}
// Extract the code block from the markdown file
example_rhai_content := extract_code_block(example_script_md, 'example.rhai', 'rhai')
if example_rhai_content == '' {
return error('Failed to extract example.rhai from template file')
}
return example_rhai_content
}
// Build and run the project
fn build_and_run_project(project_dir string) !(string, string) {
// Change to the project directory
os.chdir(project_dir) or { return error('Failed to change directory to project: ${err}') }
// Run cargo build first
build_result := os.execute('cargo build')
if build_result.exit_code != 0 {
return error('Compilation failed. Please fix the following errors and ensure your code is compatible with the existing codebase:\n\n${build_result.output}')
}
// Run the example
run_result := os.execute('cargo run --example example')
return build_result.output, run_result.output
}
// Format success message
fn format_success_message(project_dir string, build_output string, run_output string) string {
return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_output}\n\nRun output:\n${run_output}'
}
// Extract function names from wrapper code
fn extract_functions_from_code(code string) []string {
mut functions := []string{}
lines := code.split('\n')
for line in lines {
if line.contains('pub fn ') && !line.contains('//') {
// Extract function name
parts := line.split('pub fn ')
if parts.len > 1 {
name_parts := parts[1].split('(')
if name_parts.len > 0 {
fn_name := name_parts[0].trim_space()
if fn_name != '' {
functions << fn_name
}
}
}
}
}
return functions
}

View File

@@ -1,284 +0,0 @@
module logic
import incubaid.herolib.ai.escalayer
import incubaid.herolib.lang.rust
import incubaid.herolib.develop.codetools.utils as ai_utils
import os
pub fn generate_rhai_wrapper(name string, source_path string) !string {
// Detect source package and module information
source_pkg_info := rust.detect_source_package(source_path)!
source_code := rust.read_source_code(source_path)!
prompt := rhai_wrapper_generation_prompt(name, source_code, source_pkg_info)!
return run_wrapper_generation_task(prompt, RhaiGen{
name: name
dir: source_path
source_pkg_info: source_pkg_info
})!
}
// Runs the task to generate Rhai wrappers
pub fn run_wrapper_generation_task(prompt_content string, gen RhaiGen) !string {
// Create a new task
mut task := escalayer.new_task(
name: 'rhai_wrapper_creator.escalayer'
description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file'
)
// Create model configs
sonnet_model := escalayer.ModelConfig{
name: 'anthropic/claude-3.7-sonnet'
provider: 'anthropic'
temperature: 0.7
max_tokens: 25000
}
gpt4_model := escalayer.ModelConfig{
name: 'gpt-4'
provider: 'openai'
temperature: 0.7
max_tokens: 25000
}
// Create a prompt function that returns the prepared content
prompt_function := fn [prompt_content] (input string) string {
return prompt_content
}
// Define a single unit task that handles everything
task.new_unit_task(
name: 'create_rhai_wrappers'
prompt_function: prompt_function
callback_function: gen.process_rhai_wrappers
base_model: sonnet_model
retry_model: gpt4_model
retry_count: 1
)
// Initiate the task
return task.initiate('')
}
// Define a Rhai wrapper generator function for Container functions
pub fn rhai_wrapper_generation_prompt(name string, source_code string, source_pkg_info rust.SourcePackageInfo) !string {
current_dir := os.dir(@FILE)
example_rhai := os.read_file('${current_dir}/prompts/example_script.md')!
wrapper_md := os.read_file('${current_dir}/prompts/wrapper.md')!
errors_md := os.read_file('${current_dir}/prompts/errors.md')!
// Load all required template and guide files
guides := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping_classicai.md')!
engine := $tmpl('./prompts/engine.md')
vector_vs_array := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_array_vs_vector.md')!
rhai_integration_fixes := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_integration_fixes.md')!
rhai_syntax_guide := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_syntax_guide.md')!
generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs')
prompt := $tmpl('./prompts/main.md')
return prompt
}
@[params]
pub struct WrapperModule {
pub:
lib_rs string
example_rs string
engine_rs string
cargo_toml string
example_rhai string
generic_wrapper_rs string
wrapper_rs string
}
// functions is a list of function names that AI should extract and pass in
pub fn write_rhai_wrapper_module(wrapper WrapperModule, name string, path string) !string {
// Define project directory paths
project_dir := '${path}/rhai'
// Create the project using cargo new --lib
if os.exists(project_dir) {
os.rmdir_all(project_dir) or {
return error('Failed to clean existing project directory: ${err}')
}
}
// Run cargo new --lib to create the project
os.chdir(path) or { return error('Failed to change directory to base directory: ${err}') }
cargo_new_result := os.execute('cargo new --lib rhai')
if cargo_new_result.exit_code != 0 {
return error('Failed to create new library project: ${cargo_new_result.output}')
}
// Create examples directory
examples_dir := '${project_dir}/examples'
os.mkdir_all(examples_dir) or { return error('Failed to create examples directory: ${err}') }
// Write the lib.rs file
if wrapper.lib_rs != '' {
os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or {
return error('Failed to write lib.rs: ${err}')
}
} else {
// Use default lib.rs template if none provided
lib_rs_content := $tmpl('./templates/lib.rs')
os.write_file('${project_dir}/src/lib.rs', lib_rs_content) or {
return error('Failed to write lib.rs: ${err}')
}
}
// Write the wrapper.rs file
if wrapper.wrapper_rs != '' {
os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or {
return error('Failed to write wrapper.rs: ${err}')
}
}
// Write the generic wrapper.rs file
if wrapper.generic_wrapper_rs != '' {
os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or {
return error('Failed to write generic wrapper.rs: ${err}')
}
}
// Write the example.rs file
if wrapper.example_rs != '' {
os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or {
return error('Failed to write example.rs: ${err}')
}
} else {
// Use default example.rs template if none provided
example_rs_content := $tmpl('./templates/example.rs')
os.write_file('${examples_dir}/example.rs', example_rs_content) or {
return error('Failed to write example.rs: ${err}')
}
}
// Write the engine.rs file if provided
if wrapper.engine_rs != '' {
os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or {
return error('Failed to write engine.rs: ${err}')
}
}
// Write the Cargo.toml file
os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or {
return error('Failed to write Cargo.toml: ${err}')
}
// Write the example.rhai file
os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or {
return error('Failed to write example.rhai: ${err}')
}
return project_dir
}
// Extract module name from wrapper code
fn extract_module_name(code string) string {
lines := code.split('\n')
for line in lines {
// Look for pub mod or mod declarations
if line.contains('pub mod ') || line.contains('mod ') {
// Extract module name
mut parts := []string{}
if line.contains('pub mod ') {
parts = line.split('pub mod ')
} else {
parts = line.split('mod ')
}
if parts.len > 1 {
// Extract the module name and remove any trailing characters
mut name := parts[1].trim_space()
// Remove any trailing { or ; or whitespace
name = name.trim_right('{').trim_right(';').trim_space()
if name != '' {
return name
}
}
}
}
return ''
}
// RhaiGen struct for generating Rhai wrappers
struct RhaiGen {
name string
dir string
source_pkg_info rust.SourcePackageInfo
}
// Process the AI response and compile the generated code
pub fn (gen RhaiGen) process_rhai_wrappers(input string) !string {
blocks := extract_code_blocks(input)!
source_pkg_info := gen.source_pkg_info
// Create the module structure
mod := WrapperModule{
lib_rs: blocks.lib_rs
engine_rs: blocks.engine_rs
example_rhai: blocks.example_rhai
generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs')
wrapper_rs: blocks.wrapper_rs
}
// Write the module files
project_dir := write_rhai_wrapper_module(mod, gen.name, gen.dir)!
return project_dir
}
// CodeBlocks struct to hold extracted code blocks
struct CodeBlocks {
wrapper_rs string
engine_rs string
example_rhai string
lib_rs string
}
// Extract code blocks from the AI response
fn extract_code_blocks(response string) !CodeBlocks {
// Extract wrapper.rs content
wrapper_rs_content := ai_utils.extract_code_block(response, 'wrapper.rs', 'rust')
if wrapper_rs_content == '' {
return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```')
}
// Extract engine.rs content
mut engine_rs_content := ai_utils.extract_code_block(response, 'engine.rs', 'rust')
if engine_rs_content == '' {
// Try to extract from the response without explicit language marker
engine_rs_content = ai_utils.extract_code_block(response, 'engine.rs', '')
}
// Extract example.rhai content
mut example_rhai_content := ai_utils.extract_code_block(response, 'example.rhai',
'rhai')
if example_rhai_content == '' {
// Try to extract from the response without explicit language marker
example_rhai_content = ai_utils.extract_code_block(response, 'example.rhai', '')
if example_rhai_content == '' {
return error('Failed to extract example.rhai content from response. Please ensure your code is properly formatted inside a code block that starts with ```rhai\n// example.rhai and ends with ```')
}
}
// Extract lib.rs content
lib_rs_content := ai_utils.extract_code_block(response, 'lib.rs', 'rust')
if lib_rs_content == '' {
return error('Failed to extract lib.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// lib.rs and ends with ```')
}
return CodeBlocks{
wrapper_rs: wrapper_rs_content
engine_rs: engine_rs_content
example_rhai: example_rhai_content
lib_rs: lib_rs_content
}
}
// Format success message
fn format_success_message(project_dir string, build_output string, run_output string) string {
return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_output}\n\nRun output:\n${run_output}'
}

View File

@@ -1,258 +0,0 @@
module logic
import incubaid.herolib.ai.escalayer
import incubaid.herolib.lang.rust
import incubaid.herolib.develop.codetools.utils as ai_utils
import os
// pub fn generate_rhai_wrapper_sampling(name string, source_path string) !string {
// prompt := rhai_wrapper_generation_prompt(name, source_path) or {panic(err)}
// return run_wrapper_generation_task_sampling(prompt, RhaiGen{
// name: name
// dir: source_path
// }) or {panic(err)}
// }
// // Runs the task to generate Rhai wrappers
// pub fn run_wrapper_generation_task_sampling(prompt_content string, gen RhaiGen) !string {
// // Create a new task
// mut task := escalayer.new_task(
// name: 'rhai_wrapper_creator.escalayer'
// description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file'
// )
// // Create model configs
// sonnet_model := escalayer.ModelConfig{
// name: 'anthropic/claude-3.7-sonnet'
// provider: 'anthropic'
// temperature: 0.7
// max_tokens: 25000
// }
// gpt4_model := escalayer.ModelConfig{
// name: 'gpt-4'
// provider: 'openai'
// temperature: 0.7
// max_tokens: 25000
// }
// // Create a prompt function that returns the prepared content
// prompt_function := fn [prompt_content] (input string) string {
// return prompt_content
// }
// // Define a single unit task that handles everything
// task.new_unit_task(
// name: 'create_rhai_wrappers'
// prompt_function: prompt_function
// callback_function: gen.process_rhai_wrappers
// base_model: sonnet_model
// retry_model: gpt4_model
// retry_count: 1
// )
// // Initiate the task
// return task.initiate('')
// }
// @[params]
// pub struct WrapperModule {
// pub:
// lib_rs string
// example_rs string
// engine_rs string
// cargo_toml string
// example_rhai string
// generic_wrapper_rs string
// wrapper_rs string
// }
// // functions is a list of function names that AI should extract and pass in
// pub fn write_rhai_wrapper_module(wrapper WrapperModule, name string, path string)! string {
// // Define project directory paths
// project_dir := '${path}/rhai'
// // Create the project using cargo new --lib
// if os.exists(project_dir) {
// os.rmdir_all(project_dir) or {
// return error('Failed to clean existing project directory: ${err}')
// }
// }
// // Run cargo new --lib to create the project
// os.chdir(path) or {
// return error('Failed to change directory to base directory: ${err}')
// }
// cargo_new_result := os.execute('cargo new --lib rhai')
// if cargo_new_result.exit_code != 0 {
// return error('Failed to create new library project: ${cargo_new_result.output}')
// }
// // Create examples directory
// examples_dir := '${project_dir}/examples'
// os.mkdir_all(examples_dir) or {
// return error('Failed to create examples directory: ${err}')
// }
// // Write the lib.rs file
// if wrapper.lib_rs != '' {
// os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or {
// return error('Failed to write lib.rs: ${err}')
// }
// }
// // Write the wrapper.rs file
// if wrapper.wrapper_rs != '' {
// os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or {
// return error('Failed to write wrapper.rs: ${err}')
// }
// }
// // Write the generic wrapper.rs file
// if wrapper.generic_wrapper_rs != '' {
// os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or {
// return error('Failed to write generic wrapper.rs: ${err}')
// }
// }
// // Write the example.rs file
// if wrapper.example_rs != '' {
// os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or {
// return error('Failed to write example.rs: ${err}')
// }
// }
// // Write the engine.rs file if provided
// if wrapper.engine_rs != '' {
// os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or {
// return error('Failed to write engine.rs: ${err}')
// }
// }
// // Write the Cargo.toml file
// os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or {
// return error('Failed to write Cargo.toml: ${err}')
// }
// // Write the example.rhai file
// os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or {
// return error('Failed to write example.rhai: ${err}')
// }
// return project_dir
// }
// // Extract module name from wrapper code
// fn extract_module_name(code string) string {
// lines := code.split('\n')
// for line in lines {
// // Look for pub mod or mod declarations
// if line.contains('pub mod ') || line.contains('mod ') {
// // Extract module name
// mut parts := []string{}
// if line.contains('pub mod ') {
// parts = line.split('pub mod ')
// } else {
// parts = line.split('mod ')
// }
// if parts.len > 1 {
// // Extract the module name and remove any trailing characters
// mut name := parts[1].trim_space()
// // Remove any trailing { or ; or whitespace
// name = name.trim_right('{').trim_right(';').trim_space()
// if name != '' {
// return name
// }
// }
// }
// }
// return ''
// }
// // RhaiGen struct for generating Rhai wrappers
// struct RhaiGen {
// name string
// dir string
// }
// // Process the AI response and compile the generated code
// fn (gen RhaiGen) process_rhai_wrappers(response string)! string {
// // Extract code blocks from the response
// code_blocks := extract_code_blocks(response) or {
// return err
// }
// name := gen.name
// // Create a WrapperModule struct with the extracted content
// wrapper := WrapperModule{
// lib_rs: $tmpl('./templates/lib.rs')
// wrapper_rs: code_blocks.wrapper_rs
// example_rs: $tmpl('./templates/example.rs')
// engine_rs: code_blocks.engine_rs
// generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs')
// cargo_toml: $tmpl('./templates/cargo.toml')
// example_rhai: code_blocks.example_rhai
// }
// // Create the wrapper module
// project_dir := write_rhai_wrapper_module(wrapper, gen.name, gen.dir) or {
// return error('Failed to create wrapper module: ${err}')
// }
// // Build and run the project
// build_output, run_output := rust.run_example(project_dir, 'example') or {
// return err
// }
// return format_success_message(project_dir, build_output, run_output)
// }
// // CodeBlocks struct to hold extracted code blocks
// struct CodeBlocks {
// wrapper_rs string
// engine_rs string
// example_rhai string
// }
// // Extract code blocks from the AI response
// fn extract_code_blocks(response string)! CodeBlocks {
// // Extract wrapper.rs content
// wrapper_rs_content := ai_utils.extract_code_block(response, 'wrapper.rs', 'rust')
// if wrapper_rs_content == '' {
// return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```')
// }
// // Extract engine.rs content
// mut engine_rs_content := ai_utils.extract_code_block(response, 'engine.rs', 'rust')
// if engine_rs_content == '' {
// // Try to extract from the response without explicit language marker
// engine_rs_content = ai_utils.extract_code_block(response, 'engine.rs', '')
// }
// // Extract example.rhai content
// mut example_rhai_content := ai_utils.extract_code_block(response, 'example.rhai', 'rhai')
// if example_rhai_content == '' {
// // Try to extract from the response without explicit language marker
// example_rhai_content = ai_utils.extract_code_block(response, 'example.rhai', '')
// if example_rhai_content == '' {
// return error('Failed to extract example.rhai content from response. Please ensure your code is properly formatted inside a code block that starts with ```rhai\n// example.rhai and ends with ```')
// }
// }
// return CodeBlocks{
// wrapper_rs: wrapper_rs_content
// engine_rs: engine_rs_content
// example_rhai: example_rhai_content
// }
// }
// // Format success message
// fn format_success_message(project_dir string, build_output string, run_output string) string {
// return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_output}\n\nRun output:\n${run_output}'
// }

View File

@@ -1,125 +0,0 @@
# Engine
Here is an example of a well-implemented Rhai engine for the Git module:
## Example engine
```rust
// engine.rs
/// Register Nerdctl module functions with the Rhai engine
pub fn create_rhai_engine() -> Engine {
let mut engine = Engine::new();
register_nerdctl_module(&mut engine)?;
register_nerdctl_types(&mut engine)?;
engine
}
pub fn register_nerdctl_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register Container constructor
engine.register_fn("nerdctl_container_new", container_new);
engine.register_fn("nerdctl_container_from_image", container_from_image);
// Register Container instance methods
engine.register_fn("reset", container_reset);
engine.register_fn("with_port", container_with_port);
engine.register_fn("with_volume", container_with_volume);
engine.register_fn("with_env", container_with_env);
engine.register_fn("with_network", container_with_network);
engine.register_fn("with_network_alias", container_with_network_alias);
engine.register_fn("with_cpu_limit", container_with_cpu_limit);
engine.register_fn("with_memory_limit", container_with_memory_limit);
engine.register_fn("with_restart_policy", container_with_restart_policy);
engine.register_fn("with_health_check", container_with_health_check);
engine.register_fn("with_ports", container_with_ports);
engine.register_fn("with_volumes", container_with_volumes);
engine.register_fn("with_envs", container_with_envs);
engine.register_fn("with_network_aliases", container_with_network_aliases);
engine.register_fn("with_memory_swap_limit", container_with_memory_swap_limit);
engine.register_fn("with_cpu_shares", container_with_cpu_shares);
engine.register_fn("with_health_check_options", container_with_health_check_options);
engine.register_fn("with_snapshotter", container_with_snapshotter);
engine.register_fn("with_detach", container_with_detach);
engine.register_fn("build", container_build);
engine.register_fn("start", container_start);
engine.register_fn("stop", container_stop);
engine.register_fn("remove", container_remove);
engine.register_fn("exec", container_exec);
engine.register_fn("logs", container_logs);
engine.register_fn("copy", container_copy);
// Register legacy container functions (for backward compatibility)
engine.register_fn("nerdctl_run", nerdctl_run);
engine.register_fn("nerdctl_run_with_name", nerdctl_run_with_name);
engine.register_fn("nerdctl_run_with_port", nerdctl_run_with_port);
engine.register_fn("new_run_options", new_run_options);
engine.register_fn("nerdctl_exec", nerdctl_exec);
engine.register_fn("nerdctl_copy", nerdctl_copy);
engine.register_fn("nerdctl_stop", nerdctl_stop);
engine.register_fn("nerdctl_remove", nerdctl_remove);
engine.register_fn("nerdctl_list", nerdctl_list);
engine.register_fn("nerdctl_logs", nerdctl_logs);
// Register image functions
engine.register_fn("nerdctl_images", nerdctl_images);
engine.register_fn("nerdctl_image_remove", nerdctl_image_remove);
engine.register_fn("nerdctl_image_push", nerdctl_image_push);
engine.register_fn("nerdctl_image_tag", nerdctl_image_tag);
engine.register_fn("nerdctl_image_pull", nerdctl_image_pull);
engine.register_fn("nerdctl_image_commit", nerdctl_image_commit);
engine.register_fn("nerdctl_image_build", nerdctl_image_build);
Ok(())
}
/// Register Nerdctl module types with the Rhai engine
fn register_nerdctl_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register Container type
engine.register_type_with_name::<Container>("NerdctlContainer");
// Register getters for Container properties
engine.register_get("name", |container: &mut Container| container.name.clone());
engine.register_get("container_id", |container: &mut Container| {
match &container.container_id {
Some(id) => id.clone(),
None => "".to_string(),
}
});
engine.register_get("image", |container: &mut Container| {
match &container.image {
Some(img) => img.clone(),
None => "".to_string(),
}
});
engine.register_get("ports", |container: &mut Container| {
let mut array = Array::new();
for port in &container.ports {
array.push(Dynamic::from(port.clone()));
}
array
});
engine.register_get("volumes", |container: &mut Container| {
let mut array = Array::new();
for volume in &container.volumes {
array.push(Dynamic::from(volume.clone()));
}
array
});
engine.register_get("detach", |container: &mut Container| container.detach);
// Register Image type and methods
engine.register_type_with_name::<Image>("NerdctlImage");
// Register getters for Image properties
engine.register_get("id", |img: &mut Image| img.id.clone());
engine.register_get("repository", |img: &mut Image| img.repository.clone());
engine.register_get("tag", |img: &mut Image| img.tag.clone());
engine.register_get("size", |img: &mut Image| img.size.clone());
engine.register_get("created", |img: &mut Image| img.created.clone());
Ok(())
}
```

View File

@@ -1,186 +0,0 @@
# Common Errors in Rhai Wrappers and How to Fix Them
When creating Rhai wrappers for Rust functions, you might encounter several common errors. Here's how to address them:
## 1. `rhai_fn` Attribute Errors
```
error: cannot find attribute `rhai_fn` in this scope
```
**Solution**: Do not use the `#[rhai_fn]` attribute. Instead, register functions directly in the engine:
```rust
// INCORRECT:
#[rhai_fn(name = "pull_repository")]
pub fn pull_repository(repo: &mut GitRepo) -> Dynamic { ... }
// CORRECT:
pub fn pull_repository(repo: &mut GitRepo) -> Dynamic { ... }
// Then register in engine.rs:
engine.register_fn("pull_repository", pull_repository);
```
## 2. Function Visibility Errors
```
error[E0603]: function `create_rhai_engine` is private
```
**Solution**: Make sure to declare functions as `pub` when they need to be accessed from other modules:
```rust
// INCORRECT:
fn create_rhai_engine() -> Engine { ... }
// CORRECT:
pub fn create_rhai_engine() -> Engine { ... }
```
## 3. Type Errors with String vs &str
```
error[E0308]: `match` arms have incompatible types
```
**Solution**: Ensure consistent return types in match arms. When one arm returns a string literal (`&str`) and another returns a `String`, convert them to be consistent:
```rust
// INCORRECT:
match r.pull() {
Ok(_) => "Successfully pulled changes",
Err(err) => {
let error_msg = format!("Error pulling changes: {}", err);
error_msg // This is a String, not matching the &str above
}
}
// CORRECT - Option 1: Convert &str to String
match r.pull() {
Ok(_) => String::from("Successfully pulled changes"),
Err(err) => format!("Error pulling changes: {}", err)
}
// CORRECT - Option 2: Use String::from for all string literals
match r.pull() {
Ok(_) => String::from("Successfully pulled changes"),
Err(err) => {
let error_msg = format!("Error pulling changes: {}", err);
error_msg
}
}
```
## 4. Lifetime Errors
```
error: lifetime may not live long enough
```
**Solution**: When returning references from closures, you need to ensure the lifetime is valid. For path operations, convert to owned strings:
```rust
// INCORRECT:
repo_clone.wrap(|r| r.path())
// CORRECT:
repo_clone.wrap(|r| r.path().to_string())
```
## 5. Sized Trait Errors
```
error[E0277]: the size for values of type `Self` cannot be known at compilation time
```
**Solution**: Add a `Sized` bound to the `Self` type in trait definitions:
```rust
// INCORRECT:
trait RhaiWrapper {
fn wrap<F, R>(&self, f: F) -> Dynamic
where
F: FnOnce(Self) -> R,
R: ToRhai;
}
// CORRECT:
trait RhaiWrapper {
fn wrap<F, R>(&self, f: F) -> Dynamic
where
F: FnOnce(Self) -> R,
R: ToRhai,
Self: Sized;
}
```
## 6. Unused Imports
```
warning: unused imports: `Engine`, `EvalAltResult`, `FLOAT`, `INT`, and `plugin::*`
```
**Solution**: Remove unused imports to clean up your code:
```rust
// INCORRECT:
use rhai::{Engine, EvalAltResult, plugin::*, FLOAT, INT, Dynamic, Map, Array};
// CORRECT - only keep what you use:
use rhai::{Dynamic, Array};
```
## 7. Overuse of Dynamic Types
```
error[E0277]: the trait bound `Vec<Dynamic>: generic_wrapper::ToRhai` is not satisfied
```
**Solution**: Use proper static typing instead of Dynamic types whenever possible. This improves type safety and makes the code more maintainable:
```rust
// INCORRECT: Returning Dynamic for everything
pub fn list_repositories(tree: &mut GitTree) -> Dynamic {
let tree_clone = tree.clone();
tree_clone.wrap(|t| {
match t.list() {
Ok(repos) => repos,
Err(err) => vec![format!("Error listing repositories: {}", err)]
}
})
}
// CORRECT: Using proper Result types
pub fn list_repositories(tree: &mut GitTree) -> Result<Vec<String>, Box<EvalAltResult>> {
let tree_clone = tree.clone();
tree_clone.list().map_err(|err| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Error listing repositories: {}", err).into(),
rhai::Position::NONE
))
})
}
```
## 8. Improper Error Handling
```
error[E0277]: the trait bound `for<'a> fn(&'a mut Engine) -> Result<(), Box<EvalAltResult>> {wrapper::register_git_module}: RhaiNativeFunc<_, _, _, _, _>` is not satisfied
```
**Solution**: When registering functions that return Result types, make sure they are properly handled:
```rust
// INCORRECT: Trying to register a function that returns Result<(), Box<EvalAltResult>>
engine.register_fn("register_git_module", wrapper::register_git_module);
// CORRECT: Wrap the function to handle the Result
engine.register_fn("register_git_module", |engine: &mut Engine| {
match wrapper::register_git_module(engine) {
Ok(_) => Dynamic::from(true),
Err(err) => Dynamic::from(format!("Error: {}", err))
}
});
```
Remember to adapt these solutions to your specific code context. The key is to maintain type consistency, proper visibility, correct lifetime management, and appropriate static typing.

View File

@@ -1,40 +0,0 @@
## Example Rhai Script
Now, given the source code you wrapped using Rhai executable functions, write an example Rhai script that uses those functions.
### Example example rhai script
```rhai
// example.rhai
// Create a new GitTree instance
let git_tree = new_git_tree("/Users/timurgordon/code");
print("\nCreated GitTree for: /Users/timurgordon/code");
// List repositories in the tree
let repos = list_repositories(git_tree);
print("Found " + repos.len() + " repositories");
if repos.len() > 0 {
print("First repository: " + repos[0]);
// Get the repository
let repo_array = get_repositories(git_tree, repos[0]);
if repo_array.len() > 0 {
let repo = repo_array[0];
print("\nRepository path: " + path(repo));
// Check if the repository has changes
let has_changes = has_changes(repo);
print("Has changes: " + has_changes);
// Try to pull the repository
print("\nTrying to pull repository...");
let pull_result = pull_repository(repo);
print("Pull result: " + pull_result);
}
}
print("\nResult: Git operations completed successfully");
42 // Return value
```

View File

@@ -1,99 +0,0 @@
You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files.
@{guides}
@{vector_vs_array}
@{example_rhai}
@{wrapper_md}
## Common Errors to Avoid
@{errors_md}
@{rhai_integration_fixes}
@{rhai_syntax_guide}
## Your Task
Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers:
## Rust Code to Wrap
```rust
@{source_code}
```
IMPORTANT NOTES:
1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use
2. The following dependencies are available in Cargo.toml:
- rhai = "1.21.0"
- serde = { version = "1.0", features = ["derive"] }
- serde_json = "1.0"
- @{source_pkg_info.name} = { path = "@{source_pkg_info.path}" }
3. For the wrapper: `use @{source_pkg_info.name}::@{source_pkg_info.module};` this way you can access the module functions and objects with @{source_pkg_info.module}::
4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there.
```rust
@{generic_wrapper_rs}
```
5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary.
- For example, return `Result<String, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a string
- Use `Result<bool, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a boolean
- Use `Result<Vec<String>, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a list of strings
6. Your code should include public functions that can be called from Rhai scripts
7. Make sure to implement all necessary helper functions for type conversion
8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine
9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings
10. When returning path references, convert them to owned strings (e.g., path().to_string())
11. For error handling, use proper Result types with Box<EvalAltResult> for the error type:
```rust
// INCORRECT:
pub fn some_function(arg: &str) -> Dynamic {
match some_operation(arg) {
Ok(result) => Dynamic::from(result),
Err(err) => Dynamic::from(format!("Error: {}", err))
}
}
// CORRECT:
pub fn some_function(arg: &str) -> Result<String, Box<EvalAltResult>> {
some_operation(arg).map_err(|err| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Error: {}", err).into(),
rhai::Position::NONE
))
})
}
```
12. IMPORTANT: Format your response with the code between triple backticks as follows:
```rust
// wrapper.rs
// Your wrapper implementation here
```
```rust
// engine.rs
// Your engine.rs implementation here
```
```rhai
// example.rhai
// Your example Rhai script here
```
13. The example.rhai script should demonstrate the use of all the wrapper functions you create
14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example:
@{engine}
MOST IMPORTANT:
import package being wrapped as `use @{source_pkg_info.name}::@{source_pkg_info.module}`
your engine create function is called `create_rhai_engine`

View File

@@ -1,473 +0,0 @@
# Wrapper
Here is an example of a well-implemented Rhai wrapper for the Git module:
## Example wrapper
```rust
// wrapper.rs
//! Rhai wrappers for Nerdctl module functions
//!
//! This module provides Rhai wrappers for the functions in the Nerdctl module.
use rhai::{Engine, EvalAltResult, Array, Dynamic, Map};
use crate::virt::nerdctl::{self, NerdctlError, Image, Container};
use crate::process::CommandResult;
// Helper functions for error conversion with improved context
fn nerdctl_error_to_rhai_error<T>(result: Result<T, NerdctlError>) -> Result<T, Box<EvalAltResult>> {
result.map_err(|e| {
// Create a more detailed error message based on the error type
let error_message = match &e {
NerdctlError::CommandExecutionFailed(io_err) => {
format!("Failed to execute nerdctl command: {}. This may indicate nerdctl is not installed or not in PATH.", io_err)
},
NerdctlError::CommandFailed(msg) => {
format!("Nerdctl command failed: {}. Check container status and logs for more details.", msg)
},
NerdctlError::JsonParseError(msg) => {
format!("Failed to parse nerdctl JSON output: {}. This may indicate an incompatible nerdctl version.", msg)
},
NerdctlError::ConversionError(msg) => {
format!("Data conversion error: {}. This may indicate unexpected output format from nerdctl.", msg)
},
NerdctlError::Other(msg) => {
format!("Nerdctl error: {}. This is an unexpected error.", msg)
},
};
Box::new(EvalAltResult::ErrorRuntime(
error_message.into(),
rhai::Position::NONE
))
})
}
//
// Container Builder Pattern Implementation
//
/// Create a new Container
pub fn container_new(name: &str) -> Result<Container, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(Container::new(name))
}
/// Create a Container from an image
pub fn container_from_image(name: &str, image: &str) -> Result<Container, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(Container::from_image(name, image))
}
/// Reset the container configuration to defaults while keeping the name and image
pub fn container_reset(container: Container) -> Container {
container.reset()
}
/// Add a port mapping to a Container
pub fn container_with_port(container: Container, port: &str) -> Container {
container.with_port(port)
}
/// Add a volume mount to a Container
pub fn container_with_volume(container: Container, volume: &str) -> Container {
container.with_volume(volume)
}
/// Add an environment variable to a Container
pub fn container_with_env(container: Container, key: &str, value: &str) -> Container {
container.with_env(key, value)
}
/// Set the network for a Container
pub fn container_with_network(container: Container, network: &str) -> Container {
container.with_network(network)
}
/// Add a network alias to a Container
pub fn container_with_network_alias(container: Container, alias: &str) -> Container {
container.with_network_alias(alias)
}
/// Set CPU limit for a Container
pub fn container_with_cpu_limit(container: Container, cpus: &str) -> Container {
container.with_cpu_limit(cpus)
}
/// Set memory limit for a Container
pub fn container_with_memory_limit(container: Container, memory: &str) -> Container {
container.with_memory_limit(memory)
}
/// Set restart policy for a Container
pub fn container_with_restart_policy(container: Container, policy: &str) -> Container {
container.with_restart_policy(policy)
}
/// Set health check for a Container
pub fn container_with_health_check(container: Container, cmd: &str) -> Container {
container.with_health_check(cmd)
}
/// Add multiple port mappings to a Container
pub fn container_with_ports(mut container: Container, ports: Array) -> Container {
for port in ports.iter() {
if port.is_string() {
let port_str = port.clone().cast::<String>();
container = container.with_port(&port_str);
}
}
container
}
/// Add multiple volume mounts to a Container
pub fn container_with_volumes(mut container: Container, volumes: Array) -> Container {
for volume in volumes.iter() {
if volume.is_string() {
let volume_str = volume.clone().cast::<String>();
container = container.with_volume(&volume_str);
}
}
container
}
/// Add multiple environment variables to a Container
pub fn container_with_envs(mut container: Container, env_map: Map) -> Container {
for (key, value) in env_map.iter() {
if value.is_string() {
let value_str = value.clone().cast::<String>();
container = container.with_env(&key, &value_str);
}
}
container
}
/// Add multiple network aliases to a Container
pub fn container_with_network_aliases(mut container: Container, aliases: Array) -> Container {
for alias in aliases.iter() {
if alias.is_string() {
let alias_str = alias.clone().cast::<String>();
container = container.with_network_alias(&alias_str);
}
}
container
}
/// Set memory swap limit for a Container
pub fn container_with_memory_swap_limit(container: Container, memory_swap: &str) -> Container {
container.with_memory_swap_limit(memory_swap)
}
/// Set CPU shares for a Container
pub fn container_with_cpu_shares(container: Container, shares: &str) -> Container {
container.with_cpu_shares(shares)
}
/// Set health check with options for a Container
pub fn container_with_health_check_options(
container: Container,
cmd: &str,
interval: Option<&str>,
timeout: Option<&str>,
retries: Option<i64>,
start_period: Option<&str>
) -> Container {
// Convert i64 to u32 for retries
let retries_u32 = retries.map(|r| r as u32);
container.with_health_check_options(cmd, interval, timeout, retries_u32, start_period)
}
/// Set snapshotter for a Container
pub fn container_with_snapshotter(container: Container, snapshotter: &str) -> Container {
container.with_snapshotter(snapshotter)
}
/// Set detach mode for a Container
pub fn container_with_detach(container: Container, detach: bool) -> Container {
container.with_detach(detach)
}
/// Build and run the Container
///
/// This function builds and runs the container using the configured options.
/// It provides detailed error information if the build fails.
pub fn container_build(container: Container) -> Result<Container, Box<EvalAltResult>> {
// Get container details for better error reporting
let container_name = container.name.clone();
let image = container.image.clone().unwrap_or_else(|| "none".to_string());
let ports = container.ports.clone();
let volumes = container.volumes.clone();
let env_vars = container.env_vars.clone();
// Try to build the container
let build_result = container.build();
// Handle the result with improved error context
match build_result {
Ok(built_container) => {
// Container built successfully
Ok(built_container)
},
Err(err) => {
// Add more context to the error
let enhanced_error = match err {
NerdctlError::CommandFailed(msg) => {
// Provide more detailed error information
let mut enhanced_msg = format!("Failed to build container '{}' from image '{}': {}",
container_name, image, msg);
// Add information about configured options that might be relevant
if !ports.is_empty() {
enhanced_msg.push_str(&format!("\nConfigured ports: {:?}", ports));
}
if !volumes.is_empty() {
enhanced_msg.push_str(&format!("\nConfigured volumes: {:?}", volumes));
}
if !env_vars.is_empty() {
enhanced_msg.push_str(&format!("\nConfigured environment variables: {:?}", env_vars));
}
// Add suggestions for common issues
if msg.contains("not found") || msg.contains("no such image") {
enhanced_msg.push_str("\nSuggestion: The specified image may not exist or may not be pulled yet. Try pulling the image first with nerdctl_image_pull().");
} else if msg.contains("port is already allocated") {
enhanced_msg.push_str("\nSuggestion: One of the specified ports is already in use. Try using a different port or stopping the container using that port.");
} else if msg.contains("permission denied") {
enhanced_msg.push_str("\nSuggestion: Permission issues detected. Check if you have the necessary permissions to create containers or access the specified volumes.");
}
NerdctlError::CommandFailed(enhanced_msg)
},
_ => err
};
nerdctl_error_to_rhai_error(Err(enhanced_error))
}
}
}
/// Start the Container and verify it's running
///
/// This function starts the container and verifies that it's actually running.
/// It returns detailed error information if the container fails to start or
/// if it starts but stops immediately.
pub fn container_start(container: &mut Container) -> Result<CommandResult, Box<EvalAltResult>> {
// Get container details for better error reporting
let container_name = container.name.clone();
let container_id = container.container_id.clone().unwrap_or_else(|| "unknown".to_string());
// Try to start the container
let start_result = container.start();
// Handle the result with improved error context
match start_result {
Ok(result) => {
// Container started successfully
Ok(result)
},
Err(err) => {
// Add more context to the error
let enhanced_error = match err {
NerdctlError::CommandFailed(msg) => {
// Check if this is a "container already running" error, which is not really an error
if msg.contains("already running") {
return Ok(CommandResult {
stdout: format!("Container {} is already running", container_name),
stderr: "".to_string(),
success: true,
code: 0,
});
}
// Try to get more information about why the container might have failed to start
let mut enhanced_msg = format!("Failed to start container '{}' (ID: {}): {}",
container_name, container_id, msg);
// Try to check if the image exists
if let Some(image) = &container.image {
enhanced_msg.push_str(&format!("\nContainer was using image: {}", image));
}
NerdctlError::CommandFailed(enhanced_msg)
},
_ => err
};
nerdctl_error_to_rhai_error(Err(enhanced_error))
}
}
}
/// Stop the Container
pub fn container_stop(container: &mut Container) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(container.stop())
}
/// Remove the Container
pub fn container_remove(container: &mut Container) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(container.remove())
}
/// Execute a command in the Container
pub fn container_exec(container: &mut Container, command: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(container.exec(command))
}
/// Get container logs
pub fn container_logs(container: &mut Container) -> Result<CommandResult, Box<EvalAltResult>> {
// Get container details for better error reporting
let container_name = container.name.clone();
let container_id = container.container_id.clone().unwrap_or_else(|| "unknown".to_string());
// Use the nerdctl::logs function
let logs_result = nerdctl::logs(&container_id);
match logs_result {
Ok(result) => {
Ok(result)
},
Err(err) => {
// Add more context to the error
let enhanced_error = NerdctlError::CommandFailed(
format!("Failed to get logs for container '{}' (ID: {}): {}",
container_name, container_id, err)
);
nerdctl_error_to_rhai_error(Err(enhanced_error))
}
}
}
/// Copy files between the Container and local filesystem
pub fn container_copy(container: &mut Container, source: &str, dest: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(container.copy(source, dest))
}
/// Create a new Map with default run options
pub fn new_run_options() -> Map {
let mut map = Map::new();
map.insert("name".into(), Dynamic::UNIT);
map.insert("detach".into(), Dynamic::from(true));
map.insert("ports".into(), Dynamic::from(Array::new()));
map.insert("snapshotter".into(), Dynamic::from("native"));
map
}
//
// Container Function Wrappers
//
/// Wrapper for nerdctl::run
///
/// Run a container from an image.
pub fn nerdctl_run(image: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::run(image, None, true, None, None))
}
/// Run a container with a name
pub fn nerdctl_run_with_name(image: &str, name: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, None, None))
}
/// Run a container with a port mapping
pub fn nerdctl_run_with_port(image: &str, name: &str, port: &str) -> Result<CommandResult, Box<EvalAltResult>> {
let ports = vec![port];
nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, Some(&ports), None))
}
/// Wrapper for nerdctl::exec
///
/// Execute a command in a container.
pub fn nerdctl_exec(container: &str, command: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::exec(container, command))
}
/// Wrapper for nerdctl::copy
///
/// Copy files between container and local filesystem.
pub fn nerdctl_copy(source: &str, dest: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::copy(source, dest))
}
/// Wrapper for nerdctl::stop
///
/// Stop a container.
pub fn nerdctl_stop(container: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::stop(container))
}
/// Wrapper for nerdctl::remove
///
/// Remove a container.
pub fn nerdctl_remove(container: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::remove(container))
}
/// Wrapper for nerdctl::list
///
/// List containers.
pub fn nerdctl_list(all: bool) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::list(all))
}
/// Wrapper for nerdctl::logs
///
/// Get container logs.
pub fn nerdctl_logs(container: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::logs(container))
}
//
// Image Function Wrappers
//
/// Wrapper for nerdctl::images
///
/// List images in local storage.
pub fn nerdctl_images() -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::images())
}
/// Wrapper for nerdctl::image_remove
///
/// Remove one or more images.
pub fn nerdctl_image_remove(image: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_remove(image))
}
/// Wrapper for nerdctl::image_push
///
/// Push an image to a registry.
pub fn nerdctl_image_push(image: &str, destination: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_push(image, destination))
}
/// Wrapper for nerdctl::image_tag
///
/// Add an additional name to a local image.
pub fn nerdctl_image_tag(image: &str, new_name: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_tag(image, new_name))
}
/// Wrapper for nerdctl::image_pull
///
/// Pull an image from a registry.
pub fn nerdctl_image_pull(image: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_pull(image))
}
/// Wrapper for nerdctl::image_commit
///
/// Commit a container to an image.
pub fn nerdctl_image_commit(container: &str, image_name: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_commit(container, image_name))
}
/// Wrapper for nerdctl::image_build
///
/// Build an image using a Dockerfile.
pub fn nerdctl_image_build(tag: &str, context_path: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_build(tag, context_path))
}
```

View File

@@ -1,10 +0,0 @@
[package]
name = "@{name}_rhai"
version = "0.1.0"
edition = "2021"
[dependencies]
rhai = "1.21.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
@{source_pkg_info.name} = { path = "@{source_pkg_info.path}" }

View File

@@ -1,12 +0,0 @@
use rhai::{Engine, EvalAltResult, Map, Dynamic};
use crate::wrapper;
pub fn create_rhai_engine() -> Engine {
let mut engine = Engine::new();
@for function in functions
engine.register_fn("@{function}", wrapper::@{function});
@end
engine
}

View File

@@ -1,40 +0,0 @@
use std::{fs, path::Path};
use @{name}_rhai::create_rhai_engine;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Rhai Wrapper Example ===");
// Create a Rhai engine with functionality
let mut engine = create_rhai_engine();
println!("Successfully created Rhai engine");
// Get the path to the example.rhai script
let script_path = get_script_path()?;
println!("Loading script from: {}", script_path.display());
// Load the script content
let script = fs::read_to_string(&script_path)
.map_err(|e| format!("Failed to read script file: {}", e))?;
// Run the script
println!("\n=== Running Rhai script ===");
let result = engine.eval::<i64>(&script)
.map_err(|e| format!("Script execution error: {}", e))?;
println!("\nScript returned: {}", result);
println!("\nExample completed successfully!");
Ok(())
}
fn get_script_path() -> Result<std::path::PathBuf, String> {
// When running with cargo run --example, the script will be in the examples directory
let script_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("examples")
.join("example.rhai");
if script_path.exists() {
Ok(script_path)
} else {
Err(format!("Could not find example.rhai script at {}", script_path.display()))
}
}

View File

@@ -1,510 +0,0 @@
// File: /root/code/git.threefold.info/herocode/sal/src/virt/nerdctl/container_builder.rs
use std::collections::HashMap;
use crate::virt::nerdctl::{execute_nerdctl_command, NerdctlError};
use super::container_types::{Container, HealthCheck};
use super::health_check_script::prepare_health_check_command;
impl Container {
/// Reset the container configuration to defaults while keeping the name and image
/// If the container exists, it will be stopped and removed.
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn reset(self) -> Self {
let name = self.name;
let image = self.image.clone();
// If container exists, stop and remove it
if let Some(container_id) = &self.container_id {
println!("Container exists. Stopping and removing container '{}'...", name);
// Try to stop the container
let _ = execute_nerdctl_command(&["stop", container_id]);
// Try to remove the container
let _ = execute_nerdctl_command(&["rm", container_id]);
}
// Create a new container with just the name and image, but no container_id
Self {
name,
container_id: None, // Reset container_id to None since we removed the container
image,
config: std::collections::HashMap::new(),
ports: Vec::new(),
volumes: Vec::new(),
env_vars: std::collections::HashMap::new(),
network: None,
network_aliases: Vec::new(),
cpu_limit: None,
memory_limit: None,
memory_swap_limit: None,
cpu_shares: None,
restart_policy: None,
health_check: None,
detach: false,
snapshotter: None,
}
}
/// Add a port mapping
///
/// # Arguments
///
/// * `port` - Port mapping (e.g., "8080:80")
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_port(mut self, port: &str) -> Self {
self.ports.push(port.to_string());
self
}
/// Add multiple port mappings
///
/// # Arguments
///
/// * `ports` - Array of port mappings (e.g., ["8080:80", "8443:443"])
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_ports(mut self, ports: &[&str]) -> Self {
for port in ports {
self.ports.push(port.to_string());
}
self
}
/// Add a volume mount
///
/// # Arguments
///
/// * `volume` - Volume mount (e.g., "/host/path:/container/path")
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_volume(mut self, volume: &str) -> Self {
self.volumes.push(volume.to_string());
self
}
/// Add multiple volume mounts
///
/// # Arguments
///
/// * `volumes` - Array of volume mounts (e.g., ["/host/path1:/container/path1", "/host/path2:/container/path2"])
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_volumes(mut self, volumes: &[&str]) -> Self {
for volume in volumes {
self.volumes.push(volume.to_string());
}
self
}
/// Add an environment variable
///
/// # Arguments
///
/// * `key` - Environment variable name
/// * `value` - Environment variable value
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_env(mut self, key: &str, value: &str) -> Self {
self.env_vars.insert(key.to_string(), value.to_string());
self
}
/// Add multiple environment variables
///
/// # Arguments
///
/// * `env_map` - Map of environment variable names to values
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_envs(mut self, env_map: &HashMap<&str, &str>) -> Self {
for (key, value) in env_map {
self.env_vars.insert(key.to_string(), value.to_string());
}
self
}
/// Set the network for the container
///
/// # Arguments
///
/// * `network` - Network name
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_network(mut self, network: &str) -> Self {
self.network = Some(network.to_string());
self
}
/// Add a network alias for the container
///
/// # Arguments
///
/// * `alias` - Network alias
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_network_alias(mut self, alias: &str) -> Self {
self.network_aliases.push(alias.to_string());
self
}
/// Add multiple network aliases for the container
///
/// # Arguments
///
/// * `aliases` - Array of network aliases
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_network_aliases(mut self, aliases: &[&str]) -> Self {
for alias in aliases {
self.network_aliases.push(alias.to_string());
}
self
}
/// Set CPU limit for the container
///
/// # Arguments
///
/// * `cpus` - CPU limit (e.g., "0.5" for half a CPU, "2" for 2 CPUs)
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_cpu_limit(mut self, cpus: &str) -> Self {
self.cpu_limit = Some(cpus.to_string());
self
}
/// Set memory limit for the container
///
/// # Arguments
///
/// * `memory` - Memory limit (e.g., "512m" for 512MB, "1g" for 1GB)
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_memory_limit(mut self, memory: &str) -> Self {
self.memory_limit = Some(memory.to_string());
self
}
/// Set memory swap limit for the container
///
/// # Arguments
///
/// * `memory_swap` - Memory swap limit (e.g., "1g" for 1GB)
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_memory_swap_limit(mut self, memory_swap: &str) -> Self {
self.memory_swap_limit = Some(memory_swap.to_string());
self
}
/// Set CPU shares for the container (relative weight)
///
/// # Arguments
///
/// * `shares` - CPU shares (e.g., "1024" for default, "512" for half)
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_cpu_shares(mut self, shares: &str) -> Self {
self.cpu_shares = Some(shares.to_string());
self
}
/// Set restart policy for the container
///
/// # Arguments
///
/// * `policy` - Restart policy (e.g., "no", "always", "on-failure", "unless-stopped")
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_restart_policy(mut self, policy: &str) -> Self {
self.restart_policy = Some(policy.to_string());
self
}
/// Set a simple health check for the container
///
/// # Arguments
///
/// * `cmd` - Command to run for health check (e.g., "curl -f http://localhost/ || exit 1")
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_health_check(mut self, cmd: &str) -> Self {
// Use the health check script module to prepare the command
let prepared_cmd = prepare_health_check_command(cmd, &self.name);
self.health_check = Some(HealthCheck {
cmd: prepared_cmd,
interval: None,
timeout: None,
retries: None,
start_period: None,
});
self
}
/// Set a health check with custom options for the container
///
/// # Arguments
///
/// * `cmd` - Command to run for health check
/// * `interval` - Optional time between running the check (e.g., "30s", "1m")
/// * `timeout` - Optional maximum time to wait for a check to complete (e.g., "30s", "1m")
/// * `retries` - Optional number of consecutive failures needed to consider unhealthy
/// * `start_period` - Optional start period for the container to initialize before counting retries (e.g., "30s", "1m")
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_health_check_options(
mut self,
cmd: &str,
interval: Option<&str>,
timeout: Option<&str>,
retries: Option<u32>,
start_period: Option<&str>,
) -> Self {
// Use the health check script module to prepare the command
let prepared_cmd = prepare_health_check_command(cmd, &self.name);
let mut health_check = HealthCheck {
cmd: prepared_cmd,
interval: None,
timeout: None,
retries: None,
start_period: None,
};
if let Some(interval_value) = interval {
health_check.interval = Some(interval_value.to_string());
}
if let Some(timeout_value) = timeout {
health_check.timeout = Some(timeout_value.to_string());
}
if let Some(retries_value) = retries {
health_check.retries = Some(retries_value);
}
if let Some(start_period_value) = start_period {
health_check.start_period = Some(start_period_value.to_string());
}
self.health_check = Some(health_check);
self
}
/// Set the snapshotter
///
/// # Arguments
///
/// * `snapshotter` - Snapshotter to use
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_snapshotter(mut self, snapshotter: &str) -> Self {
self.snapshotter = Some(snapshotter.to_string());
self
}
/// Set whether to run in detached mode
///
/// # Arguments
///
/// * `detach` - Whether to run in detached mode
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn with_detach(mut self, detach: bool) -> Self {
self.detach = detach;
self
}
/// Build the container
///
/// # Returns
///
/// * `Result<Self, NerdctlError>` - Container instance or error
pub fn build(self) -> Result<Self, NerdctlError> {
// If container already exists, return it
if self.container_id.is_some() {
return Ok(self);
}
// If no image is specified, return an error
let image = match &self.image {
Some(img) => img,
None => return Err(NerdctlError::Other("No image specified for container creation".to_string())),
};
// Build the command arguments as strings
let mut args_strings = Vec::new();
args_strings.push("run".to_string());
if self.detach {
args_strings.push("-d".to_string());
}
args_strings.push("--name".to_string());
args_strings.push(self.name.clone());
// Add port mappings
for port in &self.ports {
args_strings.push("-p".to_string());
args_strings.push(port.clone());
}
// Add volume mounts
for volume in &self.volumes {
args_strings.push("-v".to_string());
args_strings.push(volume.clone());
}
// Add environment variables
for (key, value) in &self.env_vars {
args_strings.push("-e".to_string());
args_strings.push(format!("{}={}", key, value));
}
// Add network configuration
if let Some(network) = &self.network {
args_strings.push("--network".to_string());
args_strings.push(network.clone());
}
// Add network aliases
for alias in &self.network_aliases {
args_strings.push("--network-alias".to_string());
args_strings.push(alias.clone());
}
// Add resource limits
if let Some(cpu_limit) = &self.cpu_limit {
args_strings.push("--cpus".to_string());
args_strings.push(cpu_limit.clone());
}
if let Some(memory_limit) = &self.memory_limit {
args_strings.push("--memory".to_string());
args_strings.push(memory_limit.clone());
}
if let Some(memory_swap_limit) = &self.memory_swap_limit {
args_strings.push("--memory-swap".to_string());
args_strings.push(memory_swap_limit.clone());
}
if let Some(cpu_shares) = &self.cpu_shares {
args_strings.push("--cpu-shares".to_string());
args_strings.push(cpu_shares.clone());
}
// Add restart policy
if let Some(restart_policy) = &self.restart_policy {
args_strings.push("--restart".to_string());
args_strings.push(restart_policy.clone());
}
// Add health check
if let Some(health_check) = &self.health_check {
args_strings.push("--health-cmd".to_string());
args_strings.push(health_check.cmd.clone());
if let Some(interval) = &health_check.interval {
args_strings.push("--health-interval".to_string());
args_strings.push(interval.clone());
}
if let Some(timeout) = &health_check.timeout {
args_strings.push("--health-timeout".to_string());
args_strings.push(timeout.clone());
}
if let Some(retries) = &health_check.retries {
args_strings.push("--health-retries".to_string());
args_strings.push(retries.to_string());
}
if let Some(start_period) = &health_check.start_period {
args_strings.push("--health-start-period".to_string());
args_strings.push(start_period.clone());
}
}
if let Some(snapshotter_value) = &self.snapshotter {
args_strings.push("--snapshotter".to_string());
args_strings.push(snapshotter_value.clone());
}
// Add flags to avoid BPF issues
args_strings.push("--cgroup-manager=cgroupfs".to_string());
args_strings.push(image.clone());
// Convert to string slices for the command
let args: Vec<&str> = args_strings.iter().map(|s| s.as_str()).collect();
// Execute the command
let result = execute_nerdctl_command(&args)?;
// Get the container ID from the output
let container_id = result.stdout.trim().to_string();
Ok(Self {
name: self.name,
container_id: Some(container_id),
image: self.image,
config: self.config,
ports: self.ports,
volumes: self.volumes,
env_vars: self.env_vars,
network: self.network,
network_aliases: self.network_aliases,
cpu_limit: self.cpu_limit,
memory_limit: self.memory_limit,
memory_swap_limit: self.memory_swap_limit,
cpu_shares: self.cpu_shares,
restart_policy: self.restart_policy,
health_check: self.health_check,
detach: self.detach,
snapshotter: self.snapshotter,
})
}
}

View File

@@ -1,132 +0,0 @@
use std::collections::HashMap;
use rhai::{Dynamic, Map, Array};
/// Local wrapper trait for sal::rhai::ToRhai to avoid orphan rule violations
pub trait ToRhai {
/// Convert to a Rhai Dynamic value
fn to_rhai(&self) -> Dynamic;
}
// Implementation of ToRhai for Dynamic
impl ToRhai for Dynamic {
fn to_rhai(&self) -> Dynamic {
self.clone()
}
}
/// Generic trait for wrapping Rust functions to be used with Rhai
pub trait RhaiWrapper {
/// Wrap a function that takes ownership of self
fn wrap_consuming<F, R>(self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(Self) -> R,
R: ToRhai;
/// Wrap a function that takes a mutable reference to self
fn wrap_mut<F, R>(&mut self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(&mut Self) -> R,
R: ToRhai;
/// Wrap a function that takes an immutable reference to self
fn wrap<F, R>(&self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(&Self) -> R,
R: ToRhai;
}
/// Implementation of RhaiWrapper for any type
impl<T> RhaiWrapper for T {
fn wrap_consuming<F, R>(self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(Self) -> R,
R: ToRhai,
{
let result = f(self);
result.to_rhai()
}
fn wrap_mut<F, R>(&mut self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(&mut Self) -> R,
R: ToRhai,
{
let result = f(self);
result.to_rhai()
}
fn wrap<F, R>(&self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(&Self) -> R,
R: ToRhai,
{
let result = f(self);
result.to_rhai()
}
}
/// Convert a Rhai Map to a Rust HashMap
pub fn map_to_hashmap(map: &Map) -> HashMap<String, String> {
let mut result = HashMap::new();
for (key, value) in map.iter() {
let k = key.clone().to_string();
let v = value.clone().to_string();
if !k.is_empty() && !v.is_empty() {
result.insert(k, v);
}
}
result
}
/// Convert a HashMap<String, String> to a Rhai Map
pub fn hashmap_to_map(map: &HashMap<String, String>) -> Map {
let mut result = Map::new();
for (key, value) in map.iter() {
result.insert(key.clone().into(), Dynamic::from(value.clone()));
}
result
}
/// Convert a Rhai Array to a Vec of strings
pub fn array_to_vec_string(array: &Array) -> Vec<String> {
array.iter()
.filter_map(|item| {
let s = item.clone().to_string();
if !s.is_empty() { Some(s) } else { None }
})
.collect()
}
/// Helper function to convert Dynamic to Option<String>
pub fn dynamic_to_string_option(value: &Dynamic) -> Option<String> {
if value.is_string() {
Some(value.clone().to_string())
} else {
None
}
}
/// Helper function to convert Dynamic to Option<u32>
pub fn dynamic_to_u32_option(value: &Dynamic) -> Option<u32> {
if value.is_int() {
Some(value.as_int().unwrap() as u32)
} else {
None
}
}
/// Helper function to convert Dynamic to Option<&str> with lifetime management
pub fn dynamic_to_str_option<'a>(value: &Dynamic, storage: &'a mut String) -> Option<&'a str> {
if value.is_string() {
*storage = value.clone().to_string();
Some(storage.as_str())
} else {
None
}
}

View File

@@ -1,11 +0,0 @@
// Re-export the utility modules
pub mod generic_wrapper;
pub mod wrapper;
pub mod engine;
// Re-export the utility traits and functions
pub use generic_wrapper::{RhaiWrapper, map_to_hashmap, array_to_vec_string,
dynamic_to_string_option, hashmap_to_map};
pub use engine::create_rhai_engine;
// The create_rhai_engine function is now in the engine module

View File

@@ -1,22 +0,0 @@
module mcp
import cli
pub const command = cli.Command{
sort_flags: true
name: 'rhai'
// execute: cmd_mcpgen
description: 'rhai command'
commands: [
cli.Command{
name: 'start'
execute: cmd_start
description: 'start the Rhai server'
},
]
}
fn cmd_start(cmd cli.Command) ! {
mut server := new_mcp_server()!
server.start()!
}

View File

@@ -1,33 +0,0 @@
module mcp
import incubaid.herolib.ai.mcp
import incubaid.herolib.schemas.jsonrpc
import log
pub fn new_mcp_server() !&Server {
log.info('Creating new Developer MCP server')
// Initialize the server with the empty handlers map
mut server := mcp.new_server(MemoryBackend{
tools: {
'generate_rhai_wrapper': generate_rhai_wrapper_spec
}
tool_handlers: {
'generate_rhai_wrapper': generate_rhai_wrapper_handler
}
prompts: {
'rhai_wrapper': rhai_wrapper_prompt_spec
}
prompt_handlers: {
'rhai_wrapper': rhai_wrapper_prompt_handler
}
}, ServerParams{
config: ServerConfiguration{
server_info: ServerInfo{
name: 'rhai'
version: '1.0.0'
}
}
})!
return server
}

View File

@@ -1,45 +0,0 @@
module mcp
import incubaid.herolib.ai.mcp
import incubaid.herolib.develop.codetools as code
import incubaid.herolib.ai.mcp.rhai.logic
import incubaid.herolib.schemas.jsonschema
import incubaid.herolib.lang.rust
import x.json2 as json
// Tool definition for the create_rhai_wrapper function
const rhai_wrapper_prompt_spec = Prompt{
name: 'rhai_wrapper'
description: 'provides a prompt for creating Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file'
arguments: [
PromptArgument{
name: 'source_path'
description: 'Path to the source directory'
required: true
},
]
}
// Tool handler for the create_rhai_wrapper function
pub fn rhai_wrapper_prompt_handler(arguments []string) ![]PromptMessage {
source_path := arguments[0]
// Read and combine all Rust files in the source directory
source_code := rust.read_source_code(source_path)!
// Extract the module name from the directory path (last component)
name := rust.extract_module_name_from_path(source_path)
source_pkg_info := rust.detect_source_package(source_path)!
result := logic.rhai_wrapper_generation_prompt(name, source_code, source_pkg_info)!
return [
PromptMessage{
role: 'assistant'
content: PromptContent{
typ: 'text'
text: result
}
},
]
}

View File

@@ -1,21 +0,0 @@
module mcp
import incubaid.herolib.ai.mcp
import x.json2 as json
import incubaid.herolib.schemas.jsonschema
import log
const specs = Tool{
name: 'rhai_interface'
description: 'Add Rhai Interface to Rust Code Files'
input_schema: jsonschema.Schema{
typ: 'object'
properties: {
'path': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
description: 'Path to a .rs file or directory containing .rs files to make rhai interface for'
})
}
required: ['path']
}
}

View File

@@ -1,38 +0,0 @@
module mcp
import incubaid.herolib.ai.mcp
import incubaid.herolib.develop.codetools as code
import incubaid.herolib.ai.mcp.rhai.logic
import incubaid.herolib.schemas.jsonschema
import x.json2 as json { Any }
// Tool definition for the generate_rhai_wrapper function
const generate_rhai_wrapper_spec = Tool{
name: 'generate_rhai_wrapper'
description: 'generate_rhai_wrapper receives the name of a V language function string, and the path to the module in which it exists.'
input_schema: jsonschema.Schema{
typ: 'object'
properties: {
'name': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
})
'source_path': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
})
}
required: ['name', 'source_path']
}
}
// Tool handler for the generate_rhai_wrapper function
pub fn generate_rhai_wrapper_handler(arguments map[string]Any) !ToolCallResult {
name := arguments['name'].str()
source_path := arguments['source_path'].str()
result := logic.generate_rhai_wrapper(name, source_path) or {
return mcp.error_tool_call_result(err)
}
return ToolCallResult{
is_error: false
content: mcp.result_to_mcp_tool_contents[string](result)
}
}

View File

@@ -1 +0,0 @@
module rhai