From aeb4e8af0f970b1372274d805bb382b697a10b2f Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Sun, 28 Dec 2025 14:27:52 -0300 Subject: [PATCH] Fix all clippy warnings - 0 warnings --- src/auto_task/app_logs.rs | 5 +- src/auto_task/ask_later.rs | 7 +- src/auto_task/autotask_api.rs | 51 +++-- src/auto_task/designer_ai.rs | 128 +++++------- src/auto_task/intent_classifier.rs | 54 ++--- src/auto_task/mod.rs | 4 +- src/auto_task/{auto_task.rs => task_types.rs} | 0 src/basic/keywords/app_server.rs | 22 +-- src/basic/keywords/db_api.rs | 186 ++++++++---------- src/basic/keywords/table_access.rs | 34 ++-- src/basic/keywords/table_definition.rs | 2 - src/core/kb/website_crawler_service.rs | 2 +- src/designer/mod.rs | 34 ++-- 13 files changed, 220 insertions(+), 309 deletions(-) rename src/auto_task/{auto_task.rs => task_types.rs} (100%) diff --git a/src/auto_task/app_logs.rs b/src/auto_task/app_logs.rs index b5984312a..020ff182e 100644 --- a/src/auto_task/app_logs.rs +++ b/src/auto_task/app_logs.rs @@ -1,10 +1,9 @@ use chrono::{DateTime, Duration, Utc}; use log::{debug, error, info, warn}; -use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, VecDeque}; use std::fmt::Write; -use std::sync::{Arc, RwLock}; +use std::sync::{Arc, LazyLock, RwLock}; use uuid::Uuid; const MAX_LOGS_PER_APP: usize = 500; @@ -446,7 +445,7 @@ impl Default for AppLogStore { } } -pub static APP_LOGS: Lazy> = Lazy::new(|| Arc::new(AppLogStore::new())); +pub static APP_LOGS: LazyLock> = LazyLock::new(|| Arc::new(AppLogStore::new())); pub fn log_generator_info(app_name: &str, message: &str) { APP_LOGS.log( diff --git a/src/auto_task/ask_later.rs b/src/auto_task/ask_later.rs index 1698430d2..ec1343b72 100644 --- a/src/auto_task/ask_later.rs +++ b/src/auto_task/ask_later.rs @@ -102,12 +102,9 @@ pub fn ask_later_keyword(state: Arc, user: UserSession, engine: &mut E } }); - let state_clone5 = state.clone(); - let user_clone5 = user.clone(); - engine.register_fn("list_pending_info", move || -> Dynamic { - let state = state_clone5.clone(); - let user = user_clone5.clone(); + let state = state.clone(); + let user = user.clone(); match list_pending_info(&state, &user) { Ok(items) => { diff --git a/src/auto_task/autotask_api.rs b/src/auto_task/autotask_api.rs index d7a102a26..1f7e959a0 100644 --- a/src/auto_task/autotask_api.rs +++ b/src/auto_task/autotask_api.rs @@ -1,4 +1,4 @@ -use crate::auto_task::auto_task::{ +use crate::auto_task::task_types::{ AutoTask, AutoTaskStatus, ExecutionMode, PendingApproval, PendingDecision, TaskPriority, }; use crate::auto_task::intent_classifier::IntentClassifier; @@ -1334,7 +1334,7 @@ fn create_auto_task_from_plan( pending_decisions: Vec::new(), pending_approvals: Vec::new(), risk_summary: None, - resource_usage: crate::auto_task::auto_task::ResourceUsage::default(), + resource_usage: crate::auto_task::task_types::ResourceUsage::default(), error: None, rollback_state: None, session_id: session.id.to_string(), @@ -1613,9 +1613,8 @@ fn update_task_status_db( } fn get_pending_items_for_bot(state: &Arc, bot_id: Uuid) -> Vec { - let mut conn = match state.conn.get() { - Ok(c) => c, - Err(_) => return Vec::new(), + let Ok(mut conn) = state.conn.get() else { + return Vec::new(); }; #[derive(QueryableByName)] @@ -1860,18 +1859,15 @@ pub struct PendingItemsResponse { pub async fn get_pending_items_handler(State(state): State>) -> impl IntoResponse { info!("Getting pending items"); - let session = match get_current_session(&state) { - Ok(s) => s, - Err(_) => { - return ( - StatusCode::UNAUTHORIZED, - Json(PendingItemsResponse { - items: Vec::new(), - count: 0, - }), - ) - .into_response(); - } + let Ok(session) = get_current_session(&state) else { + return ( + StatusCode::UNAUTHORIZED, + Json(PendingItemsResponse { + items: Vec::new(), + count: 0, + }), + ) + .into_response(); }; let items = get_pending_items_for_bot(&state, session.bot_id); @@ -1898,18 +1894,15 @@ pub async fn submit_pending_item_handler( ) -> impl IntoResponse { info!("Submitting pending item {item_id}: {}", request.value); - let session = match get_current_session(&state) { - Ok(s) => s, - Err(_) => { - return ( - StatusCode::UNAUTHORIZED, - Json(serde_json::json!({ - "success": false, - "error": "Authentication required" - })), - ) - .into_response(); - } + let Ok(session) = get_current_session(&state) else { + return ( + StatusCode::UNAUTHORIZED, + Json(serde_json::json!({ + "success": false, + "error": "Authentication required" + })), + ) + .into_response(); }; match resolve_pending_item(&state, &item_id, &request.value, session.bot_id) { diff --git a/src/auto_task/designer_ai.rs b/src/auto_task/designer_ai.rs index 7dcf5b0d7..5965fb391 100644 --- a/src/auto_task/designer_ai.rs +++ b/src/auto_task/designer_ai.rs @@ -6,7 +6,7 @@ use diesel::sql_query; use diesel::sql_types::{Text, Uuid as DieselUuid}; use log::{info, trace, warn}; use serde::{Deserialize, Serialize}; - +use std::fmt::Write; use std::sync::Arc; use uuid::Uuid; @@ -164,7 +164,7 @@ impl DesignerAI { line_number: None, }) .collect(), - preview: Some(self.generate_preview(&analysis)), + preview: Some(Self::generate_preview(&analysis)), requires_confirmation: true, confirmation_message: analysis.confirmation_reason, can_undo: true, @@ -174,10 +174,10 @@ impl DesignerAI { } // Apply the modification - self.apply_modification(&analysis, session).await + self.apply_modification(&analysis, session) } - pub async fn apply_confirmed_modification( + pub fn apply_confirmed_modification( &self, change_id: &str, session: &UserSession, @@ -186,7 +186,7 @@ impl DesignerAI { let pending = self.get_pending_change(change_id, session)?; match pending { - Some(analysis) => self.apply_modification(&analysis, session).await, + Some(analysis) => self.apply_modification(&analysis, session), None => Ok(ModificationResult { success: false, modification_type: ModificationType::Unknown, @@ -202,7 +202,7 @@ impl DesignerAI { } } - pub async fn undo_change( + pub fn undo_change( &self, change_id: &str, session: &UserSession, @@ -265,7 +265,7 @@ impl DesignerAI { } } - pub async fn get_context( + pub fn get_context( &self, session: &UserSession, current_app: Option<&str>, @@ -335,11 +335,10 @@ Respond ONLY with valid JSON."# ); let response = self.call_llm(&prompt).await?; - self.parse_analysis_response(&response, instruction) + Self::parse_analysis_response(&response, instruction) } fn parse_analysis_response( - &self, response: &str, instruction: &str, ) -> Result> { @@ -393,14 +392,12 @@ Respond ONLY with valid JSON."# } Err(e) => { warn!("Failed to parse LLM analysis: {e}"); - // Fallback to heuristic analysis - self.analyze_modification_heuristic(instruction) + Self::analyze_modification_heuristic(instruction) } } } fn analyze_modification_heuristic( - &self, instruction: &str, ) -> Result> { let lower = instruction.to_lowercase(); @@ -461,7 +458,7 @@ Respond ONLY with valid JSON."# }) } - async fn apply_modification( + fn apply_modification( &self, analysis: &AnalyzedModification, session: &UserSession, @@ -476,25 +473,20 @@ Respond ONLY with valid JSON."# // Generate new content based on modification type let new_content = match analysis.modification_type { ModificationType::Style => { - self.apply_style_changes(&original_content, &analysis.changes) - .await? + Self::apply_style_changes(&original_content, &analysis.changes)? } ModificationType::Html => { - self.apply_html_changes(&original_content, &analysis.changes) - .await? + Self::apply_html_changes(&original_content, &analysis.changes)? } ModificationType::Database => { - self.apply_database_changes(&original_content, &analysis.changes, session) - .await? + Self::apply_database_changes(&original_content, &analysis.changes)? } - ModificationType::Tool => self.generate_tool_file(&analysis.changes, session).await?, + ModificationType::Tool => Self::generate_tool_file(&analysis.changes)?, ModificationType::Scheduler => { - self.generate_scheduler_file(&analysis.changes, session) - .await? + Self::generate_scheduler_file(&analysis.changes)? } ModificationType::Multiple => { - // Handle multiple changes sequentially - self.apply_multiple_changes(analysis, session).await? + Self::apply_multiple_changes()? } ModificationType::Unknown => { return Ok(ModificationResult { @@ -522,7 +514,7 @@ Respond ONLY with valid JSON."# description: analysis.summary.clone(), file_path: analysis.target_file.clone(), original_content, - new_content: new_content.clone(), + new_content, timestamp: Utc::now(), can_undo: true, }; @@ -552,8 +544,7 @@ Respond ONLY with valid JSON."# }) } - async fn apply_style_changes( - &self, + fn apply_style_changes( original: &str, changes: &[CodeChange], ) -> Result> { @@ -580,7 +571,7 @@ Respond ONLY with valid JSON."# } } _ => { - content.push_str("\n"); + content.push('\n'); content.push_str(&change.content); } } @@ -589,8 +580,7 @@ Respond ONLY with valid JSON."# Ok(content) } - async fn apply_html_changes( - &self, + fn apply_html_changes( original: &str, changes: &[CodeChange], ) -> Result> { @@ -627,11 +617,9 @@ Respond ONLY with valid JSON."# Ok(content) } - async fn apply_database_changes( - &self, + fn apply_database_changes( original: &str, changes: &[CodeChange], - session: &UserSession, ) -> Result> { let mut content = original.to_string(); @@ -654,28 +642,27 @@ Respond ONLY with valid JSON."# content.push_str(&change.content); } _ => { - content.push_str("\n"); + content.push('\n'); content.push_str(&change.content); } } } // Sync schema to database - self.sync_schema_changes(session)?; + Self::sync_schema_changes()?; Ok(content) } - async fn generate_tool_file( - &self, + fn generate_tool_file( changes: &[CodeChange], - _session: &UserSession, ) -> Result> { let mut content = String::new(); - content.push_str(&format!( + let _ = write!( + content, "' Tool generated by Designer\n' Created: {}\n\n", Utc::now().format("%Y-%m-%d %H:%M") - )); + ); for change in changes { if !change.content.is_empty() { @@ -687,16 +674,15 @@ Respond ONLY with valid JSON."# Ok(content) } - async fn generate_scheduler_file( - &self, + fn generate_scheduler_file( changes: &[CodeChange], - _session: &UserSession, ) -> Result> { let mut content = String::new(); - content.push_str(&format!( + let _ = write!( + content, "' Scheduler generated by Designer\n' Created: {}\n\n", Utc::now().format("%Y-%m-%d %H:%M") - )); + ); for change in changes { if !change.content.is_empty() { @@ -708,32 +694,28 @@ Respond ONLY with valid JSON."# Ok(content) } - async fn apply_multiple_changes( - &self, - _analysis: &AnalyzedModification, - _session: &UserSession, - ) -> Result> { - // For multiple changes, each would be applied separately - // Return summary of changes + fn apply_multiple_changes() -> Result> { Ok("Multiple changes applied".to_string()) } - fn generate_preview(&self, analysis: &AnalyzedModification) -> String { + fn generate_preview(analysis: &AnalyzedModification) -> String { let mut preview = String::new(); - preview.push_str(&format!("File: {}\n\nChanges:\n", analysis.target_file)); + let _ = writeln!(preview, "File: {}\n\nChanges:", analysis.target_file); for (i, change) in analysis.changes.iter().enumerate() { - preview.push_str(&format!( - "{}. {} at '{}'\n", + let _ = writeln!( + preview, + "{}. {} at '{}'", i + 1, change.change_type, change.target - )); + ); if !change.content.is_empty() { - preview.push_str(&format!( - " New content: {}\n", + let _ = writeln!( + preview, + " New content: {}", &change.content[..change.content.len().min(100)] - )); + ); } } @@ -746,21 +728,20 @@ Respond ONLY with valid JSON."# ) -> Result, Box> { let mut conn = self.state.conn.get()?; - // Query information_schema for tables in the bot's schema - let query = format!( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = 'public' - AND table_type = 'BASE TABLE' - LIMIT 50" - ); - #[derive(QueryableByName)] struct TableRow { #[diesel(sql_type = Text)] table_name: String, } - let tables: Vec = sql_query(&query).get_results(&mut conn).unwrap_or_default(); + let tables: Vec = sql_query( + "SELECT table_name FROM information_schema.tables + WHERE table_schema = 'public' + AND table_type = 'BASE TABLE' + LIMIT 50", + ) + .get_results(&mut conn) + .unwrap_or_default(); Ok(tables .into_iter() @@ -783,7 +764,7 @@ Respond ONLY with valid JSON."# if let Ok(entries) = std::fs::read_dir(&tools_path) { for entry in entries.flatten() { if let Some(name) = entry.file_name().to_str() { - if name.ends_with(".bas") { + if name.to_lowercase().ends_with(".bas") { tools.push(name.to_string()); } } @@ -804,7 +785,7 @@ Respond ONLY with valid JSON."# if let Ok(entries) = std::fs::read_dir(&schedulers_path) { for entry in entries.flatten() { if let Some(name) = entry.file_name().to_str() { - if name.ends_with(".bas") { + if name.to_lowercase().ends_with(".bas") { schedulers.push(name.to_string()); } } @@ -920,10 +901,7 @@ Respond ONLY with valid JSON."# Ok(()) } - fn sync_schema_changes( - &self, - _session: &UserSession, - ) -> Result<(), Box> { + fn sync_schema_changes() -> Result<(), Box> { // This would trigger the TABLE keyword parser to sync // For now, just log info!("Schema changes need to be synced to database"); diff --git a/src/auto_task/intent_classifier.rs b/src/auto_task/intent_classifier.rs index 909411c37..5f2e0d4b7 100644 --- a/src/auto_task/intent_classifier.rs +++ b/src/auto_task/intent_classifier.rs @@ -208,13 +208,13 @@ impl IntentClassifier { match classification.intent_type { IntentType::AppCreate => self.handle_app_create(classification, session).await, - IntentType::Todo => self.handle_todo(classification, session).await, - IntentType::Monitor => self.handle_monitor(classification, session).await, + IntentType::Todo => self.handle_todo(classification, session), + IntentType::Monitor => self.handle_monitor(classification, session), IntentType::Action => self.handle_action(classification, session).await, - IntentType::Schedule => self.handle_schedule(classification, session).await, - IntentType::Goal => self.handle_goal(classification, session).await, - IntentType::Tool => self.handle_tool(classification, session).await, - IntentType::Unknown => self.handle_unknown(classification, session).await, + IntentType::Schedule => self.handle_schedule(classification, session), + IntentType::Goal => self.handle_goal(classification, session), + IntentType::Tool => self.handle_tool(classification, session), + IntentType::Unknown => Self::handle_unknown(classification), } } @@ -274,12 +274,10 @@ Respond with JSON only: ); let response = self.call_llm(&prompt).await?; - self.parse_classification_response(&response, intent) + Self::parse_classification_response(&response, intent) } - /// Parse LLM response into ClassifiedIntent fn parse_classification_response( - &self, response: &str, original_intent: &str, ) -> Result> { @@ -380,14 +378,12 @@ Respond with JSON only: } Err(e) => { warn!("Failed to parse LLM response, using heuristic: {e}"); - self.classify_heuristic(original_intent) + Self::classify_heuristic(original_intent) } } } - /// Fallback heuristic classification when LLM fails fn classify_heuristic( - &self, intent: &str, ) -> Result> { let lower = intent.to_lowercase(); @@ -459,11 +455,6 @@ Respond with JSON only: }) } - // ========================================================================= - // INTENT HANDLERS - // ========================================================================= - - /// Handle APP_CREATE: Generate full application with HTMX pages, tables, tools async fn handle_app_create( &self, classification: &ClassifiedIntent, @@ -550,8 +541,7 @@ Respond with JSON only: } } - /// Handle TODO: Save task to tasks table - async fn handle_todo( + fn handle_todo( &self, classification: &ClassifiedIntent, session: &UserSession, @@ -595,8 +585,7 @@ Respond with JSON only: }) } - /// Handle MONITOR: Create ON CHANGE event handler - async fn handle_monitor( + fn handle_monitor( &self, classification: &ClassifiedIntent, session: &UserSession, @@ -643,7 +632,7 @@ END ON message: format!("Monitor created for: {subject}"), created_resources: vec![CreatedResource { resource_type: "event".to_string(), - name: handler_name.clone(), + name: handler_name, path: Some(event_path), }], app_url: None, @@ -655,7 +644,6 @@ END ON }) } - /// Handle ACTION: Execute immediately async fn handle_action( &self, classification: &ClassifiedIntent, @@ -709,8 +697,7 @@ END ON }) } - /// Handle SCHEDULE: Create SET SCHEDULE automation - async fn handle_schedule( + fn handle_schedule( &self, classification: &ClassifiedIntent, session: &UserSession, @@ -783,8 +770,7 @@ END SCHEDULE }) } - /// Handle GOAL: Create autonomous LLM loop with metrics - async fn handle_goal( + fn handle_goal( &self, classification: &ClassifiedIntent, session: &UserSession, @@ -857,8 +843,7 @@ END GOAL }) } - /// Handle TOOL: Create voice/chat command trigger - async fn handle_tool( + fn handle_tool( &self, classification: &ClassifiedIntent, session: &UserSession, @@ -926,23 +911,20 @@ END TRIGGER }) } - /// Handle UNKNOWN: Request clarification - async fn handle_unknown( - &self, + fn handle_unknown( classification: &ClassifiedIntent, - _session: &UserSession, ) -> Result> { info!("Handling UNKNOWN intent - requesting clarification"); - let suggestions = if !classification.alternative_types.is_empty() { + let suggestions = if classification.alternative_types.is_empty() { + "- Create an app\n- Add a task\n- Set up monitoring\n- Schedule automation".to_string() + } else { classification .alternative_types .iter() .map(|a| format!("- {}: {}", a.intent_type, a.reason)) .collect::>() .join("\n") - } else { - "- Create an app\n- Add a task\n- Set up monitoring\n- Schedule automation".to_string() }; Ok(IntentResult { diff --git a/src/auto_task/mod.rs b/src/auto_task/mod.rs index 11c6d4849..92b09a607 100644 --- a/src/auto_task/mod.rs +++ b/src/auto_task/mod.rs @@ -1,12 +1,12 @@ pub mod app_generator; pub mod app_logs; pub mod ask_later; -pub mod auto_task; pub mod autotask_api; pub mod designer_ai; pub mod intent_classifier; pub mod intent_compiler; pub mod safety_layer; +pub mod task_types; pub use app_generator::{ AppGenerator, AppStructure, FileType, GeneratedApp, GeneratedFile, GeneratedPage, PageType, @@ -18,7 +18,6 @@ pub use app_logs::{ ClientLogRequest, LogLevel, LogQueryParams, LogSource, LogStats, APP_LOGS, }; pub use ask_later::{ask_later_keyword, PendingInfoItem}; -pub use auto_task::{AutoTask, AutoTaskStatus, ExecutionMode, TaskPriority}; pub use autotask_api::{ apply_recommendation_handler, cancel_task_handler, classify_intent_handler, compile_intent_handler, create_and_execute_handler, execute_plan_handler, execute_task_handler, @@ -28,6 +27,7 @@ pub use autotask_api::{ submit_pending_item_handler, }; pub use designer_ai::DesignerAI; +pub use task_types::{AutoTask, AutoTaskStatus, ExecutionMode, TaskPriority}; pub use intent_classifier::{ClassifiedIntent, IntentClassifier, IntentType}; pub use intent_compiler::{CompiledIntent, IntentCompiler}; pub use safety_layer::{AuditEntry, ConstraintCheckResult, SafetyLayer, SimulationResult}; diff --git a/src/auto_task/auto_task.rs b/src/auto_task/task_types.rs similarity index 100% rename from src/auto_task/auto_task.rs rename to src/auto_task/task_types.rs diff --git a/src/basic/keywords/app_server.rs b/src/basic/keywords/app_server.rs index 65d756517..c0eda5458 100644 --- a/src/basic/keywords/app_server.rs +++ b/src/basic/keywords/app_server.rs @@ -35,18 +35,17 @@ pub async fn serve_app_index( State(state): State>, Path(params): Path, ) -> impl IntoResponse { - serve_app_file_internal(&state, ¶ms.app_name, "index.html").await + serve_app_file_internal(&state, ¶ms.app_name, "index.html") } pub async fn serve_app_file( State(state): State>, Path(params): Path, ) -> impl IntoResponse { - serve_app_file_internal(&state, ¶ms.app_name, ¶ms.file_path).await + serve_app_file_internal(&state, ¶ms.app_name, ¶ms.file_path) } -async fn serve_app_file_internal(state: &AppState, app_name: &str, file_path: &str) -> Response { - // Sanitize paths to prevent directory traversal +fn serve_app_file_internal(state: &AppState, app_name: &str, file_path: &str) -> Response { let sanitized_app_name = sanitize_path_component(app_name); let sanitized_file_path = sanitize_path_component(file_path); @@ -54,8 +53,6 @@ async fn serve_app_file_internal(state: &AppState, app_name: &str, file_path: &s return (StatusCode::BAD_REQUEST, "Invalid path").into_response(); } - // Construct full file path from SITE_ROOT - // Apps are synced to: {site_path}/{app_name}/ let site_path = state .config .as_ref() @@ -67,19 +64,16 @@ async fn serve_app_file_internal(state: &AppState, app_name: &str, file_path: &s site_path, sanitized_app_name, sanitized_file_path ); - trace!("Serving app file: {}", full_path); + trace!("Serving app file: {full_path}"); - // Check if file exists let path = std::path::Path::new(&full_path); if !path.exists() { - warn!("App file not found: {}", full_path); + warn!("App file not found: {full_path}"); return (StatusCode::NOT_FOUND, "File not found").into_response(); } - // Determine content type let content_type = get_content_type(&sanitized_file_path); - // Read and serve the file match std::fs::read(&full_path) { Ok(contents) => Response::builder() .status(StatusCode::OK) @@ -94,7 +88,7 @@ async fn serve_app_file_internal(state: &AppState, app_name: &str, file_path: &s .into_response() }), Err(e) => { - error!("Failed to read file {}: {}", full_path, e); + error!("Failed to read file {full_path}: {e}"); (StatusCode::INTERNAL_SERVER_ERROR, "Failed to read file").into_response() } } @@ -109,13 +103,11 @@ pub async fn list_all_apps(State(state): State>) -> impl IntoRespo let mut apps = Vec::new(); - // List all directories in site_path that have an index.html (are apps) if let Ok(entries) = std::fs::read_dir(&site_path) { for entry in entries.flatten() { if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { if let Some(name) = entry.file_name().to_str() { - // Skip .gbai directories and other system folders - if name.starts_with('.') || name.ends_with(".gbai") { + if name.starts_with('.') || name.to_lowercase().ends_with(".gbai") { continue; } diff --git a/src/basic/keywords/db_api.rs b/src/basic/keywords/db_api.rs index 310551069..813dea76a 100644 --- a/src/basic/keywords/db_api.rs +++ b/src/basic/keywords/db_api.rs @@ -210,34 +210,28 @@ pub async fn get_record_handler( let table_name = sanitize_identifier(&table); let user_roles = user_roles_from_headers(&headers); - let record_id = match Uuid::parse_str(&id) { - Ok(uuid) => uuid, - Err(_) => { - return ( - StatusCode::BAD_REQUEST, - Json(RecordResponse { - success: false, - data: None, - message: Some("Invalid UUID format".to_string()), - }), - ) - .into_response() - } + let Ok(record_id) = Uuid::parse_str(&id) else { + return ( + StatusCode::BAD_REQUEST, + Json(RecordResponse { + success: false, + data: None, + message: Some("Invalid UUID format".to_string()), + }), + ) + .into_response(); }; - let mut conn = match state.conn.get() { - Ok(c) => c, - Err(e) => { - return ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(RecordResponse { - success: false, - data: None, - message: Some(format!("Database connection error: {e}")), - }), - ) - .into_response() - } + let Ok(mut conn) = state.conn.get() else { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(RecordResponse { + success: false, + data: None, + message: Some("Database connection error".to_string()), + }), + ) + .into_response(); }; // Check table-level read access @@ -314,19 +308,16 @@ pub async fn create_record_handler( let table_name = sanitize_identifier(&table); let user_roles = user_roles_from_headers(&headers); - let obj = match payload.as_object() { - Some(o) => o, - None => { - return ( - StatusCode::BAD_REQUEST, - Json(RecordResponse { - success: false, - data: None, - message: Some("Payload must be a JSON object".to_string()), - }), - ) - .into_response() - } + let Some(obj) = payload.as_object() else { + return ( + StatusCode::BAD_REQUEST, + Json(RecordResponse { + success: false, + data: None, + message: Some("Payload must be a JSON object".to_string()), + }), + ) + .into_response(); }; let mut columns: Vec = vec!["id".to_string()]; @@ -341,22 +332,18 @@ pub async fn create_record_handler( values.push(value_to_sql(value)); } - let mut conn = match state.conn.get() { - Ok(c) => c, - Err(e) => { - return ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(RecordResponse { - success: false, - data: None, - message: Some(format!("Database connection error: {e}")), - }), - ) - .into_response() - } + let Ok(mut conn) = state.conn.get() else { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(RecordResponse { + success: false, + data: None, + message: Some("Database connection error".to_string()), + }), + ) + .into_response(); }; - // Check table-level write access let access_info = match check_table_access(&mut conn, &table_name, &user_roles, AccessType::Write) { Ok(info) => info, @@ -438,34 +425,28 @@ pub async fn update_record_handler( let table_name = sanitize_identifier(&table); let user_roles = user_roles_from_headers(&headers); - let record_id = match Uuid::parse_str(&id) { - Ok(uuid) => uuid, - Err(_) => { - return ( - StatusCode::BAD_REQUEST, - Json(RecordResponse { - success: false, - data: None, - message: Some("Invalid UUID format".to_string()), - }), - ) - .into_response() - } + let Ok(record_id) = Uuid::parse_str(&id) else { + return ( + StatusCode::BAD_REQUEST, + Json(RecordResponse { + success: false, + data: None, + message: Some("Invalid UUID format".to_string()), + }), + ) + .into_response(); }; - let obj = match payload.as_object() { - Some(o) => o, - None => { - return ( - StatusCode::BAD_REQUEST, - Json(RecordResponse { - success: false, - data: None, - message: Some("Payload must be a JSON object".to_string()), - }), - ) - .into_response() - } + let Some(obj) = payload.as_object() else { + return ( + StatusCode::BAD_REQUEST, + Json(RecordResponse { + success: false, + data: None, + message: Some("Payload must be a JSON object".to_string()), + }), + ) + .into_response(); }; let mut set_clauses: Vec = Vec::new(); @@ -588,37 +569,30 @@ pub async fn delete_record_handler( let table_name = sanitize_identifier(&table); let user_roles = user_roles_from_headers(&headers); - let record_id = match Uuid::parse_str(&id) { - Ok(uuid) => uuid, - Err(_) => { - return ( - StatusCode::BAD_REQUEST, - Json(DeleteResponse { - success: false, - deleted: 0, - message: Some("Invalid UUID format".to_string()), - }), - ) - .into_response() - } + let Ok(record_id) = Uuid::parse_str(&id) else { + return ( + StatusCode::BAD_REQUEST, + Json(DeleteResponse { + success: false, + deleted: 0, + message: Some("Invalid UUID format".to_string()), + }), + ) + .into_response(); }; - let mut conn = match state.conn.get() { - Ok(c) => c, - Err(e) => { - return ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(DeleteResponse { - success: false, - deleted: 0, - message: Some(format!("Database connection error: {e}")), - }), - ) - .into_response() - } + let Ok(mut conn) = state.conn.get() else { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(DeleteResponse { + success: false, + deleted: 0, + message: Some("Database connection error".to_string()), + }), + ) + .into_response(); }; - // Check table-level write access (delete requires write) if let Err(e) = check_table_access(&mut conn, &table_name, &user_roles, AccessType::Write) { return ( StatusCode::FORBIDDEN, diff --git a/src/basic/keywords/table_access.rs b/src/basic/keywords/table_access.rs index a101b975f..d17f3568b 100644 --- a/src/basic/keywords/table_access.rs +++ b/src/basic/keywords/table_access.rs @@ -99,10 +99,10 @@ impl UserRoles { } // Also check if user is marked as admin in context - if let Some(Value::Bool(true)) = session.context_data.get("is_admin") { - if !roles.contains(&"admin".to_string()) { - roles.push("admin".to_string()); - } + if matches!(session.context_data.get("is_admin"), Some(Value::Bool(true))) + && !roles.contains(&"admin".to_string()) + { + roles.push("admin".to_string()); } Self { @@ -224,26 +224,21 @@ pub fn load_table_access_info( .bind::(table_name) .get_result(conn); - let table_def = match table_result { - Ok(row) => row, - Err(_) => { - trace!( - "No table definition found for '{}', allowing open access", - table_name - ); - return None; // No definition = open access - } + let Ok(table_def) = table_result else { + trace!( + "No table definition found for '{table_name}', allowing open access" + ); + return None; }; let mut info = TableAccessInfo { table_name: table_def.table_name, - read_roles: parse_roles_string(&table_def.read_roles), - write_roles: parse_roles_string(&table_def.write_roles), + read_roles: parse_roles_string(table_def.read_roles.as_ref()), + write_roles: parse_roles_string(table_def.write_roles.as_ref()), field_read_roles: HashMap::new(), field_write_roles: HashMap::new(), }; - // Query field-level permissions let fields_result: Result, _> = sql_query( "SELECT f.field_name, f.read_roles, f.write_roles FROM dynamic_table_fields f @@ -255,8 +250,8 @@ pub fn load_table_access_info( if let Ok(fields) = fields_result { for field in fields { - let field_read = parse_roles_string(&field.read_roles); - let field_write = parse_roles_string(&field.write_roles); + let field_read = parse_roles_string(field.read_roles.as_ref()); + let field_write = parse_roles_string(field.write_roles.as_ref()); if !field_read.is_empty() { info.field_read_roles @@ -279,9 +274,8 @@ pub fn load_table_access_info( Some(info) } -fn parse_roles_string(roles: &Option) -> Vec { +fn parse_roles_string(roles: Option<&String>) -> Vec { roles - .as_ref() .map(|s| { s.split(';') .map(|r| r.trim().to_string()) diff --git a/src/basic/keywords/table_definition.rs b/src/basic/keywords/table_definition.rs index d794816ff..445141ae5 100644 --- a/src/basic/keywords/table_definition.rs +++ b/src/basic/keywords/table_definition.rs @@ -286,8 +286,6 @@ fn parse_field_definition( reference_table = Some(parts[i + 1].to_string()); } } - // Skip READ, BY, WRITE as they're handled separately - "read" | "by" | "write" => {} _ => {} } } diff --git a/src/core/kb/website_crawler_service.rs b/src/core/kb/website_crawler_service.rs index d5f9e0cb0..1edba6d73 100644 --- a/src/core/kb/website_crawler_service.rs +++ b/src/core/kb/website_crawler_service.rs @@ -244,7 +244,7 @@ pub async fn ensure_crawler_service_running( Arc::clone(kb_manager), )); - let _ = service.start(); + drop(service.start()); info!("Website crawler service initialized"); diff --git a/src/designer/mod.rs b/src/designer/mod.rs index 6ff9d23dc..5033a8399 100644 --- a/src/designer/mod.rs +++ b/src/designer/mod.rs @@ -10,6 +10,8 @@ use axum::{ use chrono::{DateTime, Utc}; use diesel::prelude::*; use serde::{Deserialize, Serialize}; +use std::fmt::Write; +use std::path::Path; use std::sync::Arc; use uuid::Uuid; @@ -1096,13 +1098,14 @@ fn build_designer_prompt(request: &DesignerModifyRequest) -> String { .map(|ctx| { let mut info = String::new(); if let Some(ref html) = ctx.page_html { - info.push_str(&format!( - "\nCurrent page HTML (first 500 chars):\n{}\n", + let _ = writeln!( + info, + "\nCurrent page HTML (first 500 chars):\n{}", &html[..html.len().min(500)] - )); + ); } if let Some(ref tables) = ctx.tables { - info.push_str(&format!("\nAvailable tables: {}\n", tables.join(", "))); + let _ = writeln!(info, "\nAvailable tables: {}", tables.join(", ")); } info }) @@ -1226,7 +1229,7 @@ async fn parse_and_apply_changes( code: Option, } - let parsed: LlmChangeResponse = serde_json::from_str(llm_response).unwrap_or(LlmChangeResponse { + let parsed: LlmChangeResponse = serde_json::from_str(llm_response).unwrap_or_else(|_| LlmChangeResponse { _understanding: Some("Could not parse LLM response".to_string()), changes: None, message: Some("I understood your request but encountered an issue processing it. Could you try rephrasing?".to_string()), @@ -1324,15 +1327,16 @@ async fn apply_file_change( } fn get_content_type(filename: &str) -> &'static str { - if filename.ends_with(".html") { - "text/html" - } else if filename.ends_with(".css") { - "text/css" - } else if filename.ends_with(".js") { - "application/javascript" - } else if filename.ends_with(".json") { - "application/json" - } else { - "text/plain" + match Path::new(filename) + .extension() + .and_then(|e| e.to_str()) + .map(|e| e.to_lowercase()) + .as_deref() + { + Some("html") => "text/html", + Some("css") => "text/css", + Some("js") => "application/javascript", + Some("json") => "application/json", + _ => "text/plain", } }