botserver/src/basic/keywords/set.rs
Rodrigo Rodriguez (Pragmatismo) 73d0ab3a1a refactor: simplify UI panels, use pooled DB, add --noui flag
- Removed unused `id` and `app_state` fields from `ChatPanel`; updated constructor to accept but ignore the state, reducing memory footprint.
- Switched database access in `ChatPanel` from a raw `Mutex` lock to a connection pool (`app_state.conn.get()`), improving concurrency and error handling.
- Reordered and cleaned up imports in `status_panel.rs` and formatted struct fields for readability.
- Updated VS Code launch configuration to pass `--noui` argument, enabling headless mode for debugging.
- Bumped several crate versions in `Cargo.lock` (e.g., `bitflags` to 2.10.0, `syn` to 2.0.108, `cookie` to 0.16.2) and added the new `ashpd` dependency, aligning the project with latest library releases.
2025-11-11 09:42:52 -03:00

79 lines
2.9 KiB
Rust

use diesel::prelude::*;
use log::trace;
use log::{error};
use rhai::Dynamic;
use rhai::Engine;
use serde_json::{json, Value};
use std::error::Error;
use crate::shared::models::UserSession;
use crate::shared::state::AppState;
pub fn set_keyword(state: &AppState, _user: UserSession, engine: &mut Engine) {
let state_clone = state.clone();
engine
.register_custom_syntax(&["SET", "$expr$", ",", "$expr$", ",", "$expr$"], false, {
move |context, inputs| {
let table_name = context.eval_expression_tree(&inputs[0])?;
let filter = context.eval_expression_tree(&inputs[1])?;
let updates = context.eval_expression_tree(&inputs[2])?;
let table_str = table_name.to_string();
let filter_str = filter.to_string();
let updates_str = updates.to_string();
trace!("Starting execute_set with table: {}, filter: {}, updates: {}", table_str, filter_str, updates_str);
let mut conn = state_clone.conn.get().map_err(|e| format!("DB error: {}", e))?;
let result = execute_set(&mut *conn, &table_str, &filter_str, &updates_str)
.map_err(|e| format!("DB error: {}", e))?;
if let Some(rows_affected) = result.get("rows_affected") {
Ok(Dynamic::from(rows_affected.as_i64().unwrap_or(0)))
} else {
Err("No rows affected".into())
}
}
})
.unwrap();
}
pub fn execute_set(conn: &mut diesel::PgConnection, table_str: &str, filter_str: &str, updates_str: &str) -> Result<Value, String> {
let (set_clause, _update_values) = parse_updates(updates_str).map_err(|e| e.to_string())?;
let where_clause = parse_filter_for_diesel(filter_str).map_err(|e| e.to_string())?;
let query = format!("UPDATE {} SET {} WHERE {}", table_str, set_clause, where_clause);
let result = diesel::sql_query(&query).execute(conn).map_err(|e| {
error!("SQL execution error: {}", e);
e.to_string()
})?;
Ok(json!({
"command": "set",
"table": table_str,
"filter": filter_str,
"updates": updates_str,
"rows_affected": result
}))
}
fn parse_updates(updates_str: &str) -> Result<(String, Vec<String>), Box<dyn Error>> {
let mut set_clauses = Vec::new();
let mut params = Vec::new();
for (i, update) in updates_str.split(',').enumerate() {
let parts: Vec<&str> = update.split('=').collect();
if parts.len() != 2 {
return Err("Invalid update format".into());
}
let column = parts[0].trim();
let value = parts[1].trim();
if !column.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
return Err("Invalid column name".into());
}
set_clauses.push(format!("{} = ${}", column, i + 1));
params.push(value.to_string());
}
Ok((set_clauses.join(", "), params))
}
fn parse_filter_for_diesel(filter_str: &str) -> Result<String, Box<dyn Error>> {
let parts: Vec<&str> = filter_str.split('=').collect();
if parts.len() != 2 {
return Err("Invalid filter format. Expected 'KEY=VALUE'".into());
}
let column = parts[0].trim();
let value = parts[1].trim();
if !column.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
return Err("Invalid column name in filter".into());
}
Ok(format!("{} = '{}'", column, value))
}