// src/tui/functions/common/form.rs use crate::services::grpc_client::GrpcClient; use crate::state::app::state::AppState; use crate::utils::data_converter; use anyhow::{anyhow, Context, Result}; use std::collections::HashMap; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SaveOutcome { NoChange, UpdatedExisting, CreatedNew(i64), } pub async fn save( app_state: &mut AppState, grpc_client: &mut GrpcClient, ) -> Result { if let Some(fs) = app_state.form_state_mut() { if !fs.has_unsaved_changes { return Ok(SaveOutcome::NoChange); } // Copy out what we need before dropping the mutable borrow let profile_name = fs.profile_name.clone(); let table_name = fs.table_name.clone(); let fields = fs.fields.clone(); let values = fs.values.clone(); let id = fs.id; let total_count = fs.total_count; let current_position = fs.current_position; let cache_key = format!("{}.{}", profile_name, table_name); let schema = app_state .schema_cache .get(&cache_key) .ok_or_else(|| { anyhow!( "Schema for table '{}' not found in cache. Cannot save.", table_name ) })?; let data_map: HashMap = fields .iter() .zip(values.iter()) .map(|(field_def, value)| (field_def.data_key.clone(), value.clone())) .collect(); let converted_data = data_converter::convert_and_validate_data(&data_map, schema) .map_err(|user_error| anyhow!(user_error))?; let is_new_entry = id == 0 || (total_count > 0 && current_position > total_count) || (total_count == 0 && current_position == 1); let outcome = if is_new_entry { let response = grpc_client .post_table_data(profile_name.clone(), table_name.clone(), converted_data) .await .context("Failed to post new table data")?; if response.success { if let Some(fs) = app_state.form_state_mut() { fs.id = response.inserted_id; fs.total_count += 1; fs.current_position = fs.total_count; fs.has_unsaved_changes = false; } SaveOutcome::CreatedNew(response.inserted_id) } else { return Err(anyhow!("Server failed to insert data: {}", response.message)); } } else { if id == 0 { return Err(anyhow!( "Cannot update record: ID is 0, but not classified as new entry." )); } let response = grpc_client .put_table_data(profile_name.clone(), table_name.clone(), id, converted_data) .await .context("Failed to put (update) table data")?; if response.success { if let Some(fs) = app_state.form_state_mut() { fs.has_unsaved_changes = false; } SaveOutcome::UpdatedExisting } else { return Err(anyhow!("Server failed to update data: {}", response.message)); } }; Ok(outcome) } else { Ok(SaveOutcome::NoChange) } } pub async fn revert( app_state: &mut AppState, grpc_client: &mut GrpcClient, ) -> Result { if let Some(fs) = app_state.form_state_mut() { if fs.id == 0 || (fs.total_count > 0 && fs.current_position > fs.total_count) || (fs.total_count == 0 && fs.current_position == 1) { let old_total_count = fs.total_count; fs.reset_to_empty(); fs.total_count = old_total_count; if fs.total_count > 0 { fs.current_position = fs.total_count + 1; } else { fs.current_position = 1; } return Ok("New entry cleared".to_string()); } if fs.current_position == 0 || fs.current_position > fs.total_count { if fs.total_count > 0 { fs.current_position = 1; } else { fs.reset_to_empty(); return Ok("No saved data to revert to; form cleared.".to_string()); } } let response = grpc_client .get_table_data_by_position( fs.profile_name.clone(), fs.table_name.clone(), fs.current_position as i32, ) .await .context(format!( "Failed to get table data by position {} for table {}.{}", fs.current_position, fs.profile_name, fs.table_name ))?; fs.update_from_response(&response.data, fs.current_position); Ok("Changes discarded, reloaded last saved version".to_string()) } else { Ok("Nothing to revert".to_string()) } }