Update dependencies and enhance script functionality
All checks were successful
GBCI / build (push) Successful in 16m37s
All checks were successful
GBCI / build (push) Successful in 16m37s
- Added `smartstring` dependency to Cargo.toml - Refactored script logic in `main.rs` for improved item handling - Enhanced `dns.sh` to log standard output and error - Introduced JSON handling functions in `script.rs` for dynamic value conversion
This commit is contained in:
parent
39ae9cb977
commit
0f0ea3e137
5 changed files with 157 additions and 79 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1655,6 +1655,7 @@ dependencies = [
|
||||||
"rhai",
|
"rhai",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"smartstring",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
19
Cargo.toml
19
Cargo.toml
|
@ -12,30 +12,29 @@ actix-cors = "0.6"
|
||||||
actix-multipart = "0.6"
|
actix-multipart = "0.6"
|
||||||
actix-web = "4"
|
actix-web = "4"
|
||||||
actix-ws="0.3.0"
|
actix-ws="0.3.0"
|
||||||
|
anyhow = "1.0"
|
||||||
|
async-stream = "0.3"
|
||||||
bytes = "1.1"
|
bytes = "1.1"
|
||||||
futures-util = "0.3"
|
|
||||||
reqwest = { version = "0.11", features = ["json", "stream"] }
|
|
||||||
rhai = "1.22.2"
|
|
||||||
|
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
|
env_logger = "0.10"
|
||||||
|
futures = "0.3"
|
||||||
|
futures-util = "0.3"
|
||||||
imap = "2.0"
|
imap = "2.0"
|
||||||
|
langchain-rust = "4.4.3"
|
||||||
lettre = { version = "0.10", features = ["smtp-transport", "builder", "tokio1", "tokio1-native-tls"] }
|
lettre = { version = "0.10", features = ["smtp-transport", "builder", "tokio1", "tokio1-native-tls"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mailparse = "0.13"
|
mailparse = "0.13"
|
||||||
minio = { git = "https://github.com/minio/minio-rs", branch = "master" }
|
minio = { git = "https://github.com/minio/minio-rs", branch = "master" }
|
||||||
native-tls = "0.2"
|
native-tls = "0.2"
|
||||||
|
reqwest = { version = "0.11", features = ["json", "stream"] }
|
||||||
|
rhai = "1.22.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
smartstring = "1.0" # Use the latest version from crates.io
|
||||||
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }
|
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tokio-stream = "0.1.17"
|
tokio-stream = "0.1.17"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["fmt"] }
|
tracing-subscriber = { version = "0.3", features = ["fmt"] }
|
||||||
env_logger = "0.10"
|
|
||||||
anyhow = "1.0"
|
|
||||||
futures = "0.3"
|
|
||||||
langchain-rust = "4.4.3"
|
|
||||||
async-stream = "0.3"
|
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,11 @@ async fn main() -> std::io::Result<()> {
|
||||||
let script_service = ScriptService::new();
|
let script_service = ScriptService::new();
|
||||||
|
|
||||||
let script = r#"
|
let script = r#"
|
||||||
let items = [ 1, 2, 3, 4]
|
let items = FIND "rob", "ACTION=EMUL1"
|
||||||
FOR EACH item IN items
|
FOR EACH item IN items
|
||||||
PRINT item
|
let text = GET "example.com"
|
||||||
NEXT item
|
PRINT item.name
|
||||||
let list = FIND "rob", "ACTION=EMUL1"
|
NEXT item "#;
|
||||||
let text = GET "example.com" "#;
|
|
||||||
|
|
||||||
match script_service.compile(script) {
|
match script_service.compile(script) {
|
||||||
Ok(ast) => {
|
Ok(ast) => {
|
||||||
|
|
|
@ -57,6 +57,9 @@ After=network.target
|
||||||
User=gbuser
|
User=gbuser
|
||||||
ExecStart=/opt/gbo/bin/coredns -conf /opt/gbo/conf/Corefile
|
ExecStart=/opt/gbo/bin/coredns -conf /opt/gbo/conf/Corefile
|
||||||
Restart=always
|
Restart=always
|
||||||
|
StandardOutput=append:/opt/gbo/logs/stdout.log
|
||||||
|
StandardError=append:/opt/gbo/logs/stderr.log
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF2
|
EOF2
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
use smartstring::SmartString;
|
||||||
|
use anyhow::Error;
|
||||||
use rhai::module_resolvers::StaticModuleResolver;
|
use rhai::module_resolvers::StaticModuleResolver;
|
||||||
use rhai::{Array, Dynamic, Engine, FnPtr, Scope};
|
use rhai::{Array, Dynamic, Engine, FnPtr, Scope};
|
||||||
use rhai::{EvalAltResult, ImmutableString, LexError, ParseError, ParseErrorType, Position};
|
use rhai::{EvalAltResult, ImmutableString, LexError, ParseError, ParseErrorType, Position};
|
||||||
use serde_json::json;
|
use serde_json::{json, Value};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct ScriptService {
|
pub struct ScriptService {
|
||||||
|
@ -9,6 +11,47 @@ pub struct ScriptService {
|
||||||
module_resolver: StaticModuleResolver,
|
module_resolver: StaticModuleResolver,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn json_value_to_dynamic(value: &Value) -> Dynamic {
|
||||||
|
match value {
|
||||||
|
Value::Null => Dynamic::UNIT,
|
||||||
|
Value::Bool(b) => Dynamic::from(*b),
|
||||||
|
Value::Number(n) => {
|
||||||
|
if let Some(i) = n.as_i64() {
|
||||||
|
Dynamic::from(i)
|
||||||
|
} else if let Some(f) = n.as_f64() {
|
||||||
|
Dynamic::from(f)
|
||||||
|
} else {
|
||||||
|
Dynamic::UNIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::String(s) => Dynamic::from(s.clone()),
|
||||||
|
Value::Array(arr) => Dynamic::from(
|
||||||
|
arr.iter()
|
||||||
|
.map(json_value_to_dynamic)
|
||||||
|
.collect::<rhai::Array>(),
|
||||||
|
),
|
||||||
|
Value::Object(obj) => Dynamic::from(
|
||||||
|
obj.iter()
|
||||||
|
.map(|(k, v)| (SmartString::from(k), json_value_to_dynamic(v)))
|
||||||
|
.collect::<rhai::Map>(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts any value to an array - single values become single-element arrays
|
||||||
|
fn to_array(value: Dynamic) -> Array {
|
||||||
|
if value.is_array() {
|
||||||
|
// Already an array - return as-is
|
||||||
|
value.cast::<Array>()
|
||||||
|
} else if value.is_unit() || value.is::<()>() {
|
||||||
|
// Handle empty/unit case
|
||||||
|
Array::new()
|
||||||
|
} else {
|
||||||
|
// Convert single value to single-element array
|
||||||
|
Array::from([value])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ScriptService {
|
impl ScriptService {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
@ -18,67 +61,76 @@ impl ScriptService {
|
||||||
engine.set_allow_anonymous_fn(true);
|
engine.set_allow_anonymous_fn(true);
|
||||||
engine.set_allow_looping(true);
|
engine.set_allow_looping(true);
|
||||||
|
|
||||||
use rhai::{Array, Dynamic, Engine, Scope};
|
engine
|
||||||
|
.register_custom_syntax(
|
||||||
|
&[
|
||||||
|
"FOR", "EACH", "$ident$", "IN", "$expr$", "$block$", "NEXT", "$ident$",
|
||||||
|
],
|
||||||
|
true, // We're modifying the scope by adding the loop variable
|
||||||
|
|context, inputs| {
|
||||||
|
// Get the iterator variable names
|
||||||
|
let loop_var = inputs[0].get_string_value().unwrap();
|
||||||
|
let next_var = inputs[3].get_string_value().unwrap();
|
||||||
|
|
||||||
engine
|
// Verify variable names match
|
||||||
.register_custom_syntax(
|
if loop_var != next_var {
|
||||||
&[
|
return Err(format!(
|
||||||
"FOR", "EACH", "$ident$", "IN", "$expr$", "$block$", "NEXT", "$ident$",
|
"NEXT variable '{}' doesn't match FOR EACH variable '{}'",
|
||||||
],
|
next_var, loop_var
|
||||||
true, // We're modifying the scope by adding the loop variable
|
)
|
||||||
|context, inputs| {
|
.into());
|
||||||
// Get the iterator variable names
|
|
||||||
let loop_var = inputs[0].get_string_value().unwrap();
|
|
||||||
let next_var = inputs[3].get_string_value().unwrap();
|
|
||||||
|
|
||||||
// Verify variable names match
|
|
||||||
if loop_var != next_var {
|
|
||||||
return Err(format!(
|
|
||||||
"NEXT variable '{}' doesn't match FOR EACH variable '{}'",
|
|
||||||
next_var, loop_var
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the collection expression
|
|
||||||
let collection = context.eval_expression_tree(&inputs[1])?;
|
|
||||||
|
|
||||||
// Get the block as an expression tree
|
|
||||||
let block = &inputs[2];
|
|
||||||
|
|
||||||
// Convert to array
|
|
||||||
let array: Array = if collection.is_array() {
|
|
||||||
collection.cast()
|
|
||||||
} else {
|
|
||||||
return Err("FOR EACH can only iterate over arrays".into());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remember original scope length
|
|
||||||
let orig_len = context.scope().len();
|
|
||||||
|
|
||||||
for item in array {
|
|
||||||
// Push the loop variable into the scope
|
|
||||||
context.scope_mut().push(loop_var.to_string(), item);
|
|
||||||
|
|
||||||
// Evaluate the block with the current scope
|
|
||||||
match context.eval_expression_tree(block) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) if e.to_string() == "EXIT FOR" => break,
|
|
||||||
Err(e) => {
|
|
||||||
// Rewind the scope before returning error
|
|
||||||
context.scope_mut().rewind(orig_len);
|
|
||||||
return Err(e);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the loop variable for next iteration
|
// Evaluate the collection expression
|
||||||
context.scope_mut().rewind(orig_len);
|
let collection = context.eval_expression_tree(&inputs[1])?;
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
// Debug: Print the collection type
|
||||||
},
|
println!("Collection type: {}", collection.type_name());
|
||||||
)
|
let ccc = collection.clone();
|
||||||
.unwrap();
|
// Convert to array - with proper error handling
|
||||||
|
let array = match collection.into_array() {
|
||||||
|
Ok(arr) => arr,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format!(
|
||||||
|
"foreach expected array, got {}: {}",
|
||||||
|
ccc.type_name(),
|
||||||
|
err
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Get the block as an expression tree
|
||||||
|
let block = &inputs[2];
|
||||||
|
|
||||||
|
// Remember original scope length
|
||||||
|
let orig_len = context.scope().len();
|
||||||
|
|
||||||
|
for item in array {
|
||||||
|
// Push the loop variable into the scope
|
||||||
|
context.scope_mut().push(loop_var.clone(), item);
|
||||||
|
|
||||||
|
// Evaluate the block with the current scope
|
||||||
|
match context.eval_expression_tree(block) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) if e.to_string() == "EXIT FOR" => {
|
||||||
|
context.scope_mut().rewind(orig_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Rewind the scope before returning error
|
||||||
|
context.scope_mut().rewind(orig_len);
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the loop variable for next iteration
|
||||||
|
context.scope_mut().rewind(orig_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Dynamic::UNIT)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Register EXIT FOR
|
// Register EXIT FOR
|
||||||
engine
|
engine
|
||||||
|
@ -99,14 +151,37 @@ engine
|
||||||
let table_str = table_name.to_string();
|
let table_str = table_name.to_string();
|
||||||
let filter_str = filter.to_string();
|
let filter_str = filter.to_string();
|
||||||
|
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
let result = json!({
|
let result = json!({
|
||||||
"command": "find",
|
"command": "find",
|
||||||
"table": table_str,
|
"table": table_str,
|
||||||
"filter": filter_str,
|
"filter": filter_str,
|
||||||
"results": []
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "dummy1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "dummy2"
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
println!("SET executed: {}", result.to_string());
|
|
||||||
Ok(Dynamic::from(result.to_string()))
|
if let serde_json::Value::Object(ref obj) = result {
|
||||||
|
if let Some(results_value) = obj.get("results") {
|
||||||
|
let dynamic_results = json_value_to_dynamic(results_value);
|
||||||
|
|
||||||
|
// Now you can work with it as Dynamic
|
||||||
|
let array = to_array(dynamic_results);
|
||||||
|
Ok(Dynamic::from(array))
|
||||||
|
} else {
|
||||||
|
Err("No results found".into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("Invalid result format".into())
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -304,6 +379,7 @@ engine
|
||||||
if is_basic_command || !for_stack.is_empty() || is_control_flow {
|
if is_basic_command || !for_stack.is_empty() || is_control_flow {
|
||||||
// Don'ta add semicolons for BASIC-style commands or inside blocks
|
// Don'ta add semicolons for BASIC-style commands or inside blocks
|
||||||
result.push_str(trimmed);
|
result.push_str(trimmed);
|
||||||
|
result.push(';');
|
||||||
} else {
|
} else {
|
||||||
// Add semicolons only for non-BASIC statements
|
// Add semicolons only for non-BASIC statements
|
||||||
result.push_str(trimmed);
|
result.push_str(trimmed);
|
||||||
|
|
Loading…
Add table
Reference in a new issue