- GET keyword added.
All checks were successful
GBCI / build (push) Successful in 14m52s

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-07-20 15:01:18 -03:00
parent 9aecbfc6fb
commit 90016ea373
4 changed files with 102 additions and 96 deletions

View file

@ -17,6 +17,8 @@ mod services;
async fn main() -> std::io::Result<()> {
dotenv().ok();
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let config = AppConfig::from_env();
let db_url = config.database_url();
let db_custom_url = config.database_custom_url();
@ -36,7 +38,7 @@ async fn main() -> std::io::Result<()> {
let script_service = ScriptService::new(&app_state.clone());
const TEXT : &str = include_str!("prompts/business/data-enrichment.bas");
const TEXT: &str = include_str!("prompts/business/data-enrichment.bas");
match script_service.compile(TEXT) {
Ok(ast) => match script_service.run(&ast) {
@ -48,10 +50,8 @@ async fn main() -> std::io::Result<()> {
// Start HTTP server
HttpServer::new(move || {
let cors = Cors::default()
.send_wildcard()
.allowed_origin("*")
.allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
.allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT])
.allowed_header(header::CONTENT_TYPE)

View file

@ -1,5 +1,5 @@
let items = FIND "gb.rob", "ACTION=EMUL1"
FOR EACH item IN items
let text = GET "example.com"
PRINT item.name
let text = GET "https://pragmatismo.com.br"
PRINT item.company
NEXT item

View file

@ -1,20 +1,40 @@
use rhai::Dynamic;
use rhai::Engine;
use rhai::{Dynamic, Engine};
use reqwest;
use crate::services::state::AppState;
use std::error::Error;
pub fn get_keyword(_state: &AppState, engine: &mut Engine) {
engine
.register_custom_syntax(
&["GET", "$expr$"],
false, // Expression, not statement
|context, inputs| {
let url = context.eval_expression_tree(&inputs[0])?;
let url_str = url.to_string();
engine.register_custom_syntax(
&["GET", "$expr$"],
false, // Expression, not statement
move |context, inputs| {
let url = context.eval_expression_tree(&inputs[0])?;
let url_str = url.to_string();
println!("GET executed: {}", url_str.to_string());
Ok(format!("Content from {}", url_str).into())
},
)
.unwrap();
if url_str.starts_with("https") {
println!("HTTPS GET request: {}", url_str);
// Use the same pattern as find_keyword
let fut = execute_get(&url_str);
let result = tokio::task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(fut)
}).map_err(|e| format!("HTTP request failed: {}", e))?;
Ok(Dynamic::from(result))
} else {
println!("GET executed: {}", url_str);
Ok(Dynamic::from(format!("Content from {}", url_str)))
}
}
).unwrap();
}
pub async fn execute_get(url: &str) -> Result<String, Box<dyn Error + Send + Sync>> {
println!("Starting execute_get with URL: {}", url);
let response = reqwest::get(url).await?;
let content = response.text().await?;
println!("GET request successful, got {} bytes", content.len());
Ok(format!("Secure content fetched: {}", content))
}

View file

@ -1,104 +1,90 @@
use smartstring::SmartString;
use log::{debug, warn};
use rhai::{Array, Dynamic};
use serde_json::{json, Value};
use smartstring::SmartString;
use sqlx::Column; // Required for .name() method
use sqlx::TypeInfo; // Required for .type_info() method
use sqlx::{postgres::PgRow, Row};
use std::error::Error;
use sqlx::{Decode, Type};
pub fn row_to_json(row: PgRow) -> Result<Value, Box<dyn Error>> {
let mut result = serde_json::Map::new();
let columns = row.columns();
println!("Processing {} columns", columns.len());
debug!("Converting row with {} columns", columns.len());
for (i, column) in columns.iter().enumerate() {
let column_name = column.name();
let type_name = column.type_info().name();
println!(
"Processing column {}: {} (type: {})",
i, column_name, type_name
);
let value: Value = match type_name {
"INT4" | "INT8" | "int4" | "int8" => match row.try_get::<i64, _>(i) {
Ok(v) => {
println!("Got int64 value: {}", v);
json!(v)
}
Err(e) => {
println!("Failed to get int64, trying i32: {}", e);
match row.try_get::<i32, _>(i) {
Ok(v) => json!(v as i64),
Err(_) => Value::Null,
}
}
},
"FLOAT4" | "FLOAT8" | "float4" | "float8" => match row.try_get::<f64, _>(i) {
Ok(v) => {
println!("Got float64 value: {}", v);
json!(v)
}
Err(e) => {
println!("Failed to get float64, trying f32: {}", e);
match row.try_get::<f32, _>(i) {
Ok(v) => json!(v as f64),
Err(_) => Value::Null,
}
}
},
"TEXT" | "VARCHAR" | "text" | "varchar" => match row.try_get::<String, _>(i) {
Ok(v) => {
println!("Got string value: {}", v);
json!(v)
}
Err(e) => {
println!("Failed to get string: {}", e);
Value::Null
}
},
"BOOL" | "bool" => match row.try_get::<bool, _>(i) {
Ok(v) => {
println!("Got bool value: {}", v);
json!(v)
}
Err(e) => {
println!("Failed to get bool: {}", e);
Value::Null
}
},
"JSON" | "JSONB" | "json" | "jsonb" => match row.try_get::<Value, _>(i) {
Ok(v) => {
println!("Got JSON value: {:?}", v);
v
}
Err(e) => {
println!("Failed to get JSON, trying as string: {}", e);
match row.try_get::<String, _>(i) {
Ok(s) => match serde_json::from_str(&s) {
Ok(v) => v,
Err(_) => json!(s),
},
Err(_) => Value::Null,
}
}
},
let value = match type_name {
"INT4" | "int4" => handle_nullable_type::<i32>(&row, i, column_name),
"INT8" | "int8" => handle_nullable_type::<i64>(&row, i, column_name),
"FLOAT4" | "float4" => handle_nullable_type::<f32>(&row, i, column_name),
"FLOAT8" | "float8" => handle_nullable_type::<f64>(&row, i, column_name),
"TEXT" | "VARCHAR" | "text" | "varchar" => handle_nullable_type::<String>(&row, i, column_name),
"BOOL" | "bool" => handle_nullable_type::<bool>(&row, i, column_name),
"JSON" | "JSONB" | "json" | "jsonb" => handle_json(&row, i, column_name),
_ => {
println!("Unknown type {}, trying as string", type_name);
match row.try_get::<String, _>(i) {
Ok(v) => json!(v),
Err(_) => Value::Null,
}
warn!("Unknown type {} for column {}", type_name, column_name);
handle_nullable_type::<String>(&row, i, column_name)
}
};
result.insert(column_name.to_string(), value);
}
println!("Finished processing row, got {} fields", result.len());
Ok(Value::Object(result))
}
fn handle_nullable_type<'r, T>(row: &'r PgRow, idx: usize, col_name: &str) -> Value
where
T: Type<sqlx::Postgres> + Decode<'r, sqlx::Postgres> + serde::Serialize + std::fmt::Debug,
{
match row.try_get::<Option<T>, _>(idx) {
Ok(Some(val)) => {
debug!("Successfully read column {} as {:?}", col_name, val);
json!(val)
}
Ok(None) => {
debug!("Column {} is NULL", col_name);
Value::Null
}
Err(e) => {
warn!("Failed to read column {}: {}", col_name, e);
Value::Null
}
}
}
fn handle_json(row: &PgRow, idx: usize, col_name: &str) -> Value {
// First try to get as Option<Value>
match row.try_get::<Option<Value>, _>(idx) {
Ok(Some(val)) => {
debug!("Successfully read JSON column {} as Value", col_name);
return val;
}
Ok(None) => return Value::Null,
Err(_) => (), // Fall through to other attempts
}
// Try as Option<String> that might contain JSON
match row.try_get::<Option<String>, _>(idx) {
Ok(Some(s)) => match serde_json::from_str(&s) {
Ok(val) => val,
Err(_) => {
debug!("Column {} contains string that's not JSON", col_name);
json!(s)
}
},
Ok(None) => Value::Null,
Err(e) => {
warn!("Failed to read JSON column {}: {}", col_name, e);
Value::Null
}
}
}
pub fn json_value_to_dynamic(value: &Value) -> Dynamic {
match value {
Value::Null => Dynamic::UNIT,