diff --git a/src/backend/backend_manager.rs b/src/backend/backend_manager.rs index c8c0d2e..9b927e6 100644 --- a/src/backend/backend_manager.rs +++ b/src/backend/backend_manager.rs @@ -1,5 +1,5 @@ use super::csv_handler::{DataEntry, ImportedData}; -use super::database_handler::{DBLoginData, QueryResult, Table, TableField, Tables}; +use super::database_handler::{DBLoginData, QueryResult, Table, Tables}; use super::parser::parse; use sqlx::MySqlConnection; use std::sync::Arc; @@ -55,6 +55,9 @@ impl BackendManger { println!("{}", e); } } + let mut db_table_data = self.db_table_data.lock().await; + let mut csv_data = self.csv_data.lock().await; + try_match_headers_to_fields(&mut db_table_data, None, &mut csv_data) } Communication::ImportDBEntries(usize) => { todo!() @@ -72,45 +75,11 @@ impl BackendManger { .describe_table(&mut self.db_connection.as_mut().unwrap()) .await; - match try_match_headers_to_fields( + try_match_headers_to_fields( &mut db_table_data, - table_index, - &csv_data.data.iter_row(0), - ) { - Ok(_) => { - /* If some got automapped, try to parse them all */ - for (col_index, field) in db_table_data - .tables - .get_mut(table_index) - .unwrap() - .fields - .as_mut() - .unwrap() - .iter_mut() - .enumerate() - { - println!( - " > automapping field \"{}\" to col \"{:?}\"", - &field.description.field, field.mapped_to_col - ); - if let Some(col_index) = field.mapped_to_col { - for cell in csv_data.data.iter_col_mut(col_index) { - cell.curr_field_description = - Some(field.description.clone()); - parse(cell); - } - } - if is_whole_col_parsed(&mut csv_data, col_index) { - println!("col \"{}\" is parsed whole!", &col_index); - csv_data.parsed_cols.push(col_index); - } - if is_whole_table_parsed(&csv_data) { - csv_data.is_parsed = true; - } - } - } - Err(_) => (), - } + Some(table_index), + &mut csv_data, + ); } Communication::RemoveRow(usize) => { todo!() @@ -127,7 +96,7 @@ impl BackendManger { } if is_whole_col_parsed(&mut csv_data, col_index) { println!("col \"{}\" is parsed whole!", &col_index); - csv_data.parsed_cols.push(col_index - 1); + csv_data.parsed_cols.push(col_index); } if is_whole_table_parsed(&csv_data) { csv_data.is_parsed = true; @@ -198,27 +167,32 @@ pub fn is_whole_col_parsed(csv_data: &mut MutexGuard, col_index: u match parse { Ok(_) => return true, Err(_) => { - println!( - "Cel \"{}\" in Col \"{}\" isnt parsed :(", - cel.data, col_index - ); return false; } } } else { - println!( - "Cel \"{}\" in Col \"{}\" isnt parsed :(", - cel.data, col_index - ); false } }) } pub fn try_match_headers_to_fields( tables: &mut MutexGuard, - table_index: usize, - csv_headers: &std::slice::Iter<'_, DataEntry>, -) -> Result<(), ()> { + table_index: Option, + csv_data: &mut MutexGuard, +) { + if !csv_data.are_headers || csv_data.data.is_empty() { + return; + } + let final_table_index: usize; + if let Some(i) = table_index { + final_table_index = i; + } else if let Some(cw_table_i) = tables.current_working_table { + final_table_index = cw_table_i; + } else { + return; + } + let table_index = final_table_index; + let db_fields = tables .tables .get_mut(table_index) @@ -228,19 +202,49 @@ pub fn try_match_headers_to_fields( .unwrap(); let mut has_matched_some = false; for field in db_fields { - for (i, header) in csv_headers.clone().enumerate() { + for (i, header) in csv_data.data.iter_row(0).into_iter().enumerate() { if &field.description.field == &header.data { field.mapped_to_col = Some(i); has_matched_some = true; } } } - match has_matched_some { - true => return Ok(()), - false => return Err(()), + + if has_matched_some == false + || tables.current_working_table.is_none() + || tables.tables.is_empty() + { + return; + } + /* If some got automapped, try to parse them all */ + if let Some(table_i) = tables.current_working_table { + if let Some(table) = tables.tables.get_mut(table_i) { + if let Some(fields) = table.fields.as_mut() { + for (col_index, field) in fields.iter().enumerate() { + println!( + " > automapping field \"{}\" to col \"{:?}\"", + &field.description.field, field.mapped_to_col + ); + if let Some(col_index) = field.mapped_to_col { + for cell in csv_data.data.iter_mut() { + cell.curr_field_description = Some(field.description.clone()); + parse(cell); + } + } + if is_whole_col_parsed(csv_data, col_index) { + println!("col \"{}\" is parsed whole!", &col_index); + csv_data.parsed_cols.push(col_index); + } + if is_whole_table_parsed(&csv_data) { + csv_data.is_parsed = true; + } + } + } + } } } +#[derive(Debug)] pub enum Communication { ValidateCreditentials( DBLoginData, diff --git a/src/main.rs b/src/main.rs index 3b8fdf7..2d93d9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(debug_assertions), deny(dead_code), deny(unused_variables))] +#![cfg_attr(debug_assertions, allow(dead_code), allow(unused_variables))] mod backend; mod ui; use backend::backend_manager::BackendManger; diff --git a/src/ui/db_login_window.rs b/src/ui/db_login_window.rs index 2d0498c..a3eb904 100644 --- a/src/ui/db_login_window.rs +++ b/src/ui/db_login_window.rs @@ -37,7 +37,7 @@ impl CSQLWindow for DBLoginWindow { ctx: &egui::Context, _ui: &mut egui::Ui, _frame: &mut eframe::Frame, - ) -> Option { + ) -> Option>> { egui::Window::new("MySQL Login") .id(egui::Id::new("MySQL Login")) .resizable(false) @@ -51,13 +51,11 @@ impl CSQLWindow for DBLoginWindow { }); if let Some(result) = &self.are_creditentials_verified { match result { - Ok(_) => return Some(ExitStatus::Ok), + Ok(_) => return Some(Ok(ExitStatus::Ok)), Err(e) => { - return Some(ExitStatus::Err(Box::from(, - >>::into( - e.to_string() - )))) + >>::into(e.to_string())))) } } } else { diff --git a/src/ui/db_transaction_window.rs b/src/ui/db_transaction_window.rs index d328aed..6321205 100644 --- a/src/ui/db_transaction_window.rs +++ b/src/ui/db_transaction_window.rs @@ -1,6 +1,4 @@ use egui::{Context, Ui}; -use sqlx::mysql::MySqlQueryResult; -use std::error::Error; use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::oneshot; @@ -42,7 +40,7 @@ impl CSQLWindow for DBTransactionWindow { ctx: &Context, ui: &mut Ui, frame: &mut eframe::Frame, - ) -> Option { + ) -> Option>> { egui::Window::new("Database Transactions") .id(egui::Id::new("Database Transactions")) .resizable(false) @@ -62,7 +60,7 @@ impl CSQLWindow for DBTransactionWindow { }); if self.is_finished { - return Some(ExitStatus::Ok); + return Some(Ok(ExitStatus::Ok)); } None } @@ -129,7 +127,7 @@ impl DBTransactionWindow { .auto_shrink([false; 2]) .stick_to_bottom(true) .show(ui, |ui| { - let mut table = egui_extras::TableBuilder::new(ui) + let table = egui_extras::TableBuilder::new(ui) .striped(true) .resizable(true) .cell_layout(egui::Layout::left_to_right(egui::Align::LEFT)) @@ -155,7 +153,7 @@ impl DBTransactionWindow { ui.strong("Result"); }); }) - .body(|mut body| { + .body(|body| { body.rows(15.0, self.log_history.len(), |row_index, mut row| { let log = self.log_history.get(row_index).unwrap(); row.col(|ui| { @@ -187,6 +185,6 @@ impl DBTransactionWindow { } pub struct OptionsDBTransactionWindow { - substitute_zero_dates_for_NULL: bool, + substitute_zero_dates_for_null: bool, remove_id_field_from_insert: bool, } diff --git a/src/ui/table_window.rs b/src/ui/table_window.rs index b14e8ac..1a73ca4 100644 --- a/src/ui/table_window.rs +++ b/src/ui/table_window.rs @@ -13,8 +13,9 @@ pub struct SpreadSheetWindow { csv_data_handle: Arc>, db_table_data_handle: Arc>, current_table: Option, - should_export_to_db: bool, + return_status: Option, debug_autoload_file_sent: bool, + is_table_description_request_sent: bool, } impl SpreadSheetWindow { pub fn default( @@ -29,7 +30,8 @@ impl SpreadSheetWindow { db_table_data_handle, current_table: None, debug_autoload_file_sent: false, - should_export_to_db: false, + is_table_description_request_sent: false, + return_status: None, } } } @@ -39,11 +41,11 @@ impl CSQLWindow for SpreadSheetWindow { ctx: &egui::Context, ui: &mut egui::Ui, frame: &mut eframe::Frame, - ) -> Option { + ) -> Option>> { self.ui(ui, ctx); - if self.should_export_to_db { - self.should_export_to_db = false; - return Some(ExitStatus::StartTransactionWindow); + if let Some(return_status) = self.return_status { + self.return_status = None; + return Some(Ok(return_status)); } None } @@ -57,15 +59,18 @@ impl SpreadSheetWindow { } else { is_csv_parsed = false; } - /* Program Menu */ ui.horizontal(|ui| { - ui.group(|ui| {}); + if ui.button("Log into DB").clicked() { + self.return_status = Some(ExitStatus::StartLoginWindow); + } + ui.add_space(ui.available_width()); }); + ui.separator(); /* CSV & DB menu */ ui.horizontal(|ui| { ui.columns(2, |uis| { - uis[0].group(|ui| { + uis[0].horizontal(|ui| { if ui.button("Import file").clicked() { self.open_file(); } @@ -75,22 +80,24 @@ impl SpreadSheetWindow { if ui.button("Save as...").clicked() { self.save_file(); } + + ui.add_space(ui.available_width()); + ui.separator(); }); - uis[1].group(|ui| { - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - self.table_options(ui); - if ui.button("Import DB").clicked() { + uis[1].with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + ui.add_enabled_ui(is_csv_parsed, |ui| { + if ui.button("Append to DB").clicked() { todo!(); } - ui.add_enabled_ui(is_csv_parsed, |ui| { - if ui.button("Save to DB").clicked() { - self.should_export_to_db = true; - } - if ui.button("Append to DB").clicked() { - todo!(); - } - }); + if ui.button("Save to DB").clicked() { + self.return_status = Some(ExitStatus::StartTransactionWindow); + } }); + if ui.button("Import DB").clicked() { + todo!(); + } + self.table_options(ui); + ui.add_space(ui.available_width()); }); }) }); @@ -103,10 +110,11 @@ impl SpreadSheetWindow { }); SpreadSheetWindow::preview_files_being_dropped(ctx); - if self.current_table.is_none() { + if self.current_table.is_some() { ui.group(|ui| { egui::ScrollArea::horizontal().show(ui, |ui| { - self.table_builder(ui); + self.table_builder(ui) + .unwrap_or_else(|e| println!("Table un-intd, {:?}", e)); }); }); } @@ -142,45 +150,62 @@ impl SpreadSheetWindow { }); } } - fn table_builder(&mut self, ui: &mut Ui) { + fn table_builder(&mut self, ui: &mut Ui) -> Result<(), Box> { if let Ok(csv_data) = &mut self.csv_data_handle.try_lock() { if let Ok(db_table_data) = &mut self.db_table_data_handle.try_lock() { //ref to all fields in curr db table - let mut curr_db_table_fields: &mut Vec = db_table_data + let mut curr_db_table_fields: &mut Vec = match db_table_data .tables .get_mut(self.current_table.unwrap()) - .unwrap() + .ok_or("no current table")? .fields .as_mut() - .unwrap(); - let mut table = TableBuilder::new(ui) - .striped(true) - .cell_layout(egui::Layout::left_to_right(egui::Align::Center)); - - for _i in 0..csv_data.data.cols() + 1 { - table = table.column(Column::auto().resizable(true).clip(false)); + .ok_or("no fields yet") + { + Ok(val) => val, + Err(e) => { + if !self.is_table_description_request_sent { + self.sender.try_send(Communication::GetTableDescription( + self.current_table.unwrap(), + ))?; + self.is_table_description_request_sent = true; + } + return Err(Box::from( + >>::into(e.to_string()), + )); + } + }; + if csv_data.data.is_empty() { + return Err(>>::into( + "no rows yet".to_string(), + )); } - /* If cols got prematched by name */ + let table = TableBuilder::new(ui) + .striped(true) + .cell_layout(egui::Layout::left_to_right(egui::Align::Center)) + /*First collumn for row actions */ + .column(Column::auto().at_most(30.0).resizable(false).clip(false)); table - .column(Column::remainder()) + .columns( + Column::auto().resizable(true).clip(false), + csv_data.data.cols(), + ) .min_scrolled_height(0.0) .header(20., |mut header| { /* First Col for row mutators */ header.col(|ui|{ - ui.add_space(45.0); }); - - for i in 0..csv_data.data.cols() { - header.col(|ui| { - SpreadSheetWindow::add_header_col(ui,i, csv_data,&mut curr_db_table_fields, &mut self.sender); - }); - } + SpreadSheetWindow::add_header_cols(&mut header, csv_data,&mut curr_db_table_fields, &mut self.sender); }) .body(|body| { - body.rows(15., csv_data.data.rows() -1, |row_index, mut row| { + body.rows(15., csv_data.data.rows() - csv_data.are_headers as usize, |row_index, mut row| { + let headers_i_offset = csv_data.are_headers as usize; + row.col(|ui|{ + if ui.button("x").clicked(){} + }); for curr_cell in - csv_data.data.iter_row_mut(row_index+1) + csv_data.data.iter_row_mut(row_index + headers_i_offset) { /* If cell is bound to a field, color it's bg according to is_parsed */ row.col(|ui| { @@ -214,68 +239,72 @@ impl SpreadSheetWindow { } else { ui.centered_and_justified(|ui| ui.heading("Drag and drop or Open a file...")); } + Ok(()) } } impl SpreadSheetWindow { - pub fn add_header_col( - ui: &mut Ui, - i: usize, + pub fn add_header_cols( + header: &mut egui_extras::TableRow, csv_data: &mut MutexGuard, curr_db_table_fields: &mut Vec, sender: &mut Sender, ) { - ui.vertical_centered_justified(|ui| { - if ui.button("x").clicked() { - todo!() - } - if csv_data.parsed_cols.contains(&i) { - /* If the whole col is parsed, change header bg to green, else normal */ - ui.style_mut().visuals.override_text_color = Some(egui::Color32::GREEN); - } else { - ui.style_mut().visuals.override_text_color = Some(egui::Color32::RED); - } - let mut combo_box: ComboBox; - if csv_data.are_headers { - combo_box = ComboBox::new(i, csv_data.data.get(0, i).unwrap().data.clone()); - } else { - combo_box = ComboBox::new(i, ""); - } + for i in 0..csv_data.data.cols() { + header.col(|ui| { + ui.vertical_centered_justified(|ui| { + if ui.button("x").clicked() {} + if csv_data.parsed_cols.contains(&i) { + /* If the whole col is parsed, change header bg to green, else normal */ + ui.style_mut().visuals.override_text_color = Some(egui::Color32::GREEN); + } else { + ui.style_mut().visuals.override_text_color = Some(egui::Color32::RED); + } + let mut combo_box: ComboBox; + if csv_data.are_headers { + combo_box = ComboBox::new(i, csv_data.data.get(0, i).unwrap().data.clone()); + } else { + combo_box = ComboBox::new(i, ""); + } - //if any field is assinged to this combobox, show it's text, else "----" - if let Some(selected_field) = curr_db_table_fields - .iter() - .find(|field| field.mapped_to_col == Some(i)) - { - combo_box = combo_box.selected_text(selected_field.description.field.clone()); - } else { - combo_box = combo_box.selected_text("-----"); - } - - /* When a Field gets attached to Col, */ - combo_box.show_ui(ui, |ui| { - for field in curr_db_table_fields.iter_mut() { - if ui - .selectable_value( - &mut field.mapped_to_col, - Some(i), - field.description.field.clone(), - ) - .clicked() + //if any field is assinged to this combobox, show it's text, else "----" + if let Some(selected_field) = curr_db_table_fields + .iter() + .find(|field| field.mapped_to_col == Some(i)) { - match sender.try_send(Communication::TryParseCol(i)) { - Ok(_) => { - for cel in csv_data.data.iter_col_mut(i) { - cel.curr_field_description = Some(field.description.clone()); + combo_box = + combo_box.selected_text(selected_field.description.field.clone()); + } else { + combo_box = combo_box.selected_text("-----"); + } + + /* When a Field gets attached to Col, */ + combo_box.show_ui(ui, |ui| { + for field in curr_db_table_fields.iter_mut() { + if ui + .selectable_value( + &mut field.mapped_to_col, + Some(i), + field.description.field.clone(), + ) + .clicked() + { + match sender.try_send(Communication::TryParseCol(i)) { + Ok(_) => { + for cel in csv_data.data.iter_col_mut(i) { + cel.curr_field_description = + Some(field.description.clone()); + } + } + Err(e) => println!("failed sending parsecol request, {}", e), } } - Err(e) => println!("failed sending parsecol request, {}", e), } - } - } + }); + ui.reset_style(); + }); }); - ui.reset_style(); - }); + } } pub fn save_file(&mut self) { @@ -296,19 +325,24 @@ impl SpreadSheetWindow { } pub fn open_file(&mut self) { + let final_path; /*preloads file in debug */ if cfg!(debug_assertions) && !self.debug_autoload_file_sent { - let path = std::path::PathBuf::from("/home/djkato/Dokumenty/csql/oc_product.csv"); - println!(" * Preloading \"{:?}\"...", path); + final_path = std::path::PathBuf::from("/home/djkato/Dokumenty/csql/oc_product.csv"); + println!(" * Preloading \"{:?}\"...", final_path); self.debug_autoload_file_sent = true; - } - if let Some(path) = rfd::FileDialog::new() - .add_filter("Spreadsheets", &["csv"]) - .pick_file() - { + } else { + if let Some(path) = rfd::FileDialog::new() + .add_filter("Spreadsheets", &["csv"]) + .pick_file() + { + final_path = path; + } else { + return; + } self.sender .try_send(Communication::LoadImportFilePath( - path.display().to_string(), + final_path.display().to_string(), )) .unwrap_or_else(|err| println!("failed to send loadimportpath, {}", err)); } diff --git a/src/ui/window_manager.rs b/src/ui/window_manager.rs index 48984c1..d35ec01 100644 --- a/src/ui/window_manager.rs +++ b/src/ui/window_manager.rs @@ -5,12 +5,10 @@ use crate::ui::db_login_window::DBLoginWindow; use crate::ui::table_window::SpreadSheetWindow; use eframe::{run_native, App, NativeOptions}; use std::sync::Arc; -use tokio::sync::mpsc::{channel, Receiver, Sender}; -use tokio::sync::oneshot; +use tokio::sync::mpsc::{channel, Sender}; use tokio::sync::Mutex; use super::db_transaction_window::DBTransactionWindow; -use super::{db_login_window, db_transaction_window}; pub fn create_ui( sender: Sender, @@ -61,8 +59,11 @@ impl App for CSQL { egui::CentralPanel::default().show(ctx, |ui| { if let Some(result) = self.spreadsheet_window.refresh(ctx, ui, frame) { match result { - ExitStatus::StartLoginWindow => self.should_open_transaction_window = true, - ExitStatus::StartTransactionWindow => self.should_open_login_window = true, + Ok(status) => match status { + ExitStatus::StartLoginWindow => self.should_open_transaction_window = true, + ExitStatus::StartTransactionWindow => self.should_open_login_window = true, + _ => {} + }, _ => (), } }; @@ -71,26 +72,37 @@ impl App for CSQL { if let Some(db_login_window) = self.db_login_window.as_mut() { if let Some(result) = db_login_window.refresh(ctx, ui, frame) { match result { - ExitStatus::Ok => { + Ok(status) => { self.db_login_window = None; self.should_open_login_window = false; } _ => (), } } + } else { + self.db_login_window = Some(DBLoginWindow::default(self.sender.clone())); } } if self.should_open_transaction_window { if let Some(db_transaction_window) = self.db_transaction_window.as_mut() { if let Some(result) = db_transaction_window.refresh(ctx, ui, frame) { match result { - ExitStatus::Ok => { + Ok(status) => { self.db_transaction_window = None; self.should_open_transaction_window = false; } _ => (), } } + } else { + if let Ok(db_table_data) = self.db_table_data_handle.try_lock() { + if let Some(cw_table_i) = db_table_data.current_working_table { + self.db_transaction_window = Some(DBTransactionWindow::default( + self.sender.clone(), + cw_table_i, + )) + } + } } } }); @@ -103,11 +115,11 @@ pub trait CSQLWindow { ctx: &egui::Context, ui: &mut egui::Ui, frame: &mut eframe::Frame, - ) -> Option; + ) -> Option>>; } +#[derive(Clone, Copy)] pub enum ExitStatus { StartTransactionWindow, StartLoginWindow, Ok, - Err(Box), }