diff --git a/src/backend/backend_manager.rs b/src/backend/backend_manager.rs index fa34199..c8c0d2e 100644 --- a/src/backend/backend_manager.rs +++ b/src/backend/backend_manager.rs @@ -1,9 +1,7 @@ use super::csv_handler::{DataEntry, ImportedData}; use super::database_handler::{DBLoginData, QueryResult, Table, TableField, Tables}; use super::parser::parse; -use sqlx::mysql::MySqlQueryResult; use sqlx::MySqlConnection; -use std::error::Error; use std::sync::Arc; use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::{oneshot, Mutex, MutexGuard}; @@ -43,12 +41,7 @@ impl BackendManger { } } } - Communication::AreCreditentialsValidated(sender) => sender - .send(self.db_login_data.is_verified) - .unwrap_or_else(|e| { - println!("Server - Failed answering if credit. validated"); - }), - Communication::LoadImportFilePath(path, sender) => { + Communication::LoadImportFilePath(path) => { self.imported_data.path = path; match self.imported_data.load_csv() { Ok(_) => { @@ -57,21 +50,19 @@ impl BackendManger { data.are_headers = self.imported_data.are_headers.clone(); data.data = self.imported_data.data.clone(); data.path = self.imported_data.path.clone(); - - drop(data); - sender.send(true).unwrap_or_else(|err| { - println!("Server - failed to send loaded file callback, {}", err) - }) } Err(e) => { println!("{}", e); - sender.send(false).unwrap_or_else(|e| { - println!("Server - Failed answering if imported{}", e) - }) } } } - Communication::GetTableDescription(table_index, sender) => { + Communication::ImportDBEntries(usize) => { + todo!() + } + Communication::SaveCSV(path) => { + todo!() + } + Communication::GetTableDescription(table_index) => { let mut db_table_data = self.db_table_data.lock().await; let mut csv_data = self.csv_data.lock().await; db_table_data @@ -80,59 +71,53 @@ impl BackendManger { .unwrap() .describe_table(&mut self.db_connection.as_mut().unwrap()) .await; - if csv_data.are_headers { - match try_match_headers_to_fields( - &mut db_table_data, - table_index, - &csv_data.data.iter_row(0), - ) { - Ok(_) => { - 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; + + match 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); } } - sender.send(true).unwrap_or_else(|e| { - println!( - "Server - failed to respond to getTableDescription, {}", - e - ) - }); + 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(_) => sender.send(false).unwrap_or_else(|e| { - println!("Server - failed to respond to getTableDescription, {}", e) - }), } - } else { - sender.send(false).unwrap_or_else(|e| { - println!("Server - failed to respond to getTableDescription, {}", e) - }); + Err(_) => (), } } + Communication::RemoveRow(usize) => { + todo!() + } + Communication::RemoveCol(usize) => { + todo!() + } Communication::TryParseCol(col_index) => { let mut csv_data = self.csv_data.lock().await; for cell in csv_data.data.iter_col_mut(col_index) { @@ -261,9 +246,12 @@ pub enum Communication { DBLoginData, oneshot::Sender>>, ), - AreCreditentialsValidated(oneshot::Sender), - LoadImportFilePath(String, oneshot::Sender), - GetTableDescription(usize, oneshot::Sender), + LoadImportFilePath(String), + GetTableDescription(usize), + ImportDBEntries(usize), + RemoveCol(usize), + RemoveRow(usize), + SaveCSV(String), TryParseCol(usize), StartInserting(usize, Sender, oneshot::Sender), TryCommit(oneshot::Sender), diff --git a/src/backend/database_handler.rs b/src/backend/database_handler.rs index 26da699..2247e58 100644 --- a/src/backend/database_handler.rs +++ b/src/backend/database_handler.rs @@ -1,14 +1,14 @@ use crate::backend::csv_handler::DataEntry; use core::num::ParseIntError; -use eframe::glow::Query; use sqlx::mysql::MySqlQueryResult; use sqlx::{mysql::MySqlConnectOptions, ConnectOptions}; -use sqlx::{FromRow, MySqlConnection}; +use sqlx::{Executor, FromRow, MySqlConnection}; use std::error::Error; -use std::slice::Iter; #[derive(Default)] pub struct Tables { pub tables: Vec, + pub current_working_table: Option, + pub is_connection_verified: bool, } #[derive(Default, Clone)] @@ -43,8 +43,10 @@ impl Tables { .fetch_all(connection) .await .unwrap(); - + let mut table_i = 0; for table in qr_tables { + println!(" >Found table:{}. {}", &table_i, &table.tables_in_quotes); + table_i += 1; self.tables.push({ Table { name: table.tables_in_quotes, @@ -59,23 +61,23 @@ impl Tables { impl Table { pub async fn transaction_commit(connection: &mut MySqlConnection) -> QueryResult { - match sqlx::query("COMMIT").execute(connection).await { + match connection.execute("COMMIT").await { Ok(res) => { return QueryResult { - query: "ROLLBACK".to_owned(), + query: "COMMIT".to_owned(), result: Ok(res), } } Err(e) => { return QueryResult { - query: "ROLLBACK".to_owned(), + query: "COMMIT".to_owned(), result: Err(Box::new(e)), } } } } pub async fn transaction_rollback(connection: &mut MySqlConnection) -> QueryResult { - match sqlx::query("ROLLBACK").execute(connection).await { + match connection.execute("ROLLBACK").await { Ok(res) => { return QueryResult { query: "ROLLBACK".to_owned(), @@ -91,16 +93,24 @@ impl Table { } } pub async fn start_transaction(connection: &mut MySqlConnection) -> QueryResult { - match sqlx::query("BEGIN").execute(connection).await { - Ok(res) => { - return QueryResult { - query: "ROLLBACK".to_owned(), - result: Ok(res), + match connection.execute("SET autocommit = OFF").await { + Ok(_) => match connection.execute("BEGIN").await { + Ok(res) => { + return QueryResult { + query: "BEGIN".to_owned(), + result: Ok(res), + } } - } + Err(e) => { + return QueryResult { + query: "BEGIN".to_owned(), + result: Err(Box::new(e)), + } + } + }, Err(e) => { return QueryResult { - query: "ROLLBACK".to_owned(), + query: "SET autocommit = OFF".to_owned(), result: Err(Box::new(e)), } } @@ -127,6 +137,7 @@ impl Table { row.1 + "\'" + next_row.data.as_str() + "\', ", ) }); + /* remove last ", " from both */ fields.0.pop(); fields.0.pop(); fields.1.pop(); @@ -187,7 +198,7 @@ pub struct QueryResult { pub result: Result>, } -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub struct DBLoginData { pub user_name: String, pub database: String, diff --git a/src/ui/db_login_window.rs b/src/ui/db_login_window.rs index ba056bb..2d0498c 100644 --- a/src/ui/db_login_window.rs +++ b/src/ui/db_login_window.rs @@ -1,15 +1,16 @@ +use super::window_manager::{CSQLWindow, ExitStatus}; use crate::backend::backend_manager::Communication; use crate::backend::database_handler::DBLoginData; use egui::{Context, Ui, Vec2}; use tokio::sync::mpsc::Sender; use tokio::sync::oneshot; - pub struct DBLoginWindow { db_login_data: DBLoginData, sender: Sender, are_creditentials_verified_receiver: Option>>>, - are_creditentials_verified_error: Option>, + are_creditentials_verified: Option>>, + debug_autoconnect_sent: bool, } impl DBLoginWindow { pub fn default(sender: Sender) -> DBLoginWindow { @@ -25,23 +26,18 @@ impl DBLoginWindow { }, sender, are_creditentials_verified_receiver: None, - are_creditentials_verified_error: None, + are_creditentials_verified: None, + debug_autoconnect_sent: false, } } } -impl DBLoginWindow { - pub fn show(&mut self, ctx: &Context, ui: &mut Ui, is_db_connection_verified: bool) { - let alignment: egui::Align2; - - match is_db_connection_verified { - true => { - alignment = egui::Align2::CENTER_TOP; - } - false => { - alignment = egui::Align2::CENTER_CENTER; - } - } - +impl CSQLWindow for DBLoginWindow { + fn refresh( + &mut self, + ctx: &egui::Context, + _ui: &mut egui::Ui, + _frame: &mut eframe::Frame, + ) -> Option { egui::Window::new("MySQL Login") .id(egui::Id::new("MySQL Login")) .resizable(false) @@ -49,13 +45,27 @@ impl DBLoginWindow { .title_bar(true) .scroll2([false, false]) .enabled(true) - .default_open(!is_db_connection_verified) - .anchor(alignment, Vec2::ZERO) + .anchor(egui::Align2::CENTER_CENTER, Vec2::ZERO) .show(ctx, |ui| { self.ui(ctx, ui); }); + if let Some(result) = &self.are_creditentials_verified { + match result { + Ok(_) => return Some(ExitStatus::Ok), + Err(e) => { + return Some(ExitStatus::Err(Box::from(, + >>::into( + e.to_string() + )))) + } + } + } else { + return None; + } } - +} +impl DBLoginWindow { fn ui(&mut self, ctx: &Context, ui: &mut Ui) { ui.heading("Log into the database:"); ui.group(|ui| { @@ -92,6 +102,21 @@ impl DBLoginWindow { ui.horizontal(|ui| { ui.checkbox(&mut self.db_login_data.should_remember, "Remember"); + /* Autoconnects in debug mode */ + if cfg!(debug_assertions) && !self.debug_autoconnect_sent { + println!(" * pre-connecting to \"{:?}\"...", &self.db_login_data); + let (sender, receiver) = oneshot::channel(); + self.are_creditentials_verified_receiver = Some(receiver); + self.sender + .try_send(Communication::ValidateCreditentials( + self.db_login_data.clone(), + sender, + )) + .unwrap_or_else(|e| { + println!("Failed to send authenticator... {}", e); + }); + self.debug_autoconnect_sent = true + } if ui.button("Connect").clicked() { let (sender, receiver) = oneshot::channel(); self.are_creditentials_verified_receiver = Some(receiver); @@ -107,16 +132,19 @@ impl DBLoginWindow { }); /* If db login bad, return sqlx error to UI */ - if let Some(err) = &mut self.are_creditentials_verified_error { + if let Some(Err(err)) = &mut self.are_creditentials_verified { ui.style_mut().visuals.override_text_color = Some(egui::Color32::RED); ui.label(format!("{}", err)); + } else if let Some(Ok(_)) = &self.are_creditentials_verified { + ui.style_mut().visuals.override_text_color = Some(egui::Color32::GREEN); + ui.label("Connected"); } if let Some(receiver) = &mut self.are_creditentials_verified_receiver { if let Ok(response) = receiver.try_recv() { match response { - Ok(_) => self.are_creditentials_verified_error = None, - Err(err) => self.are_creditentials_verified_error = Some(err), + Ok(_) => self.are_creditentials_verified = Some(Ok(())), + Err(err) => self.are_creditentials_verified = Some(Err(err)), } self.are_creditentials_verified_receiver = None; } diff --git a/src/ui/db_transaction_window.rs b/src/ui/db_transaction_window.rs index d175b89..d328aed 100644 --- a/src/ui/db_transaction_window.rs +++ b/src/ui/db_transaction_window.rs @@ -7,13 +7,15 @@ use tokio::sync::oneshot; use crate::backend::backend_manager::Communication; use crate::backend::database_handler::QueryResult; +use super::window_manager::{CSQLWindow, ExitStatus}; + pub struct DBTransactionWindow { sender: Sender, - /* (Query, Result) */ log_history: Vec, logs_receiver: Option>, is_log_finished_receiver: Option>, - result: Option>>>, + is_log_finished: bool, + is_finished: bool, final_result_receiver: Option>, working_table_index: usize, } @@ -25,16 +27,22 @@ impl DBTransactionWindow { DBTransactionWindow { sender, log_history: vec![], - result: None, + is_finished: false, logs_receiver: None, final_result_receiver: None, is_log_finished_receiver: None, + is_log_finished: false, working_table_index, } } } -impl DBTransactionWindow { - pub fn show(&mut self, ctx: &Context, ui: &mut Ui, frame: &mut eframe::Frame) { +impl CSQLWindow for DBTransactionWindow { + fn refresh( + &mut self, + ctx: &Context, + ui: &mut Ui, + frame: &mut eframe::Frame, + ) -> Option { egui::Window::new("Database Transactions") .id(egui::Id::new("Database Transactions")) .resizable(false) @@ -50,10 +58,24 @@ impl DBTransactionWindow { .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO) .show(ctx, |ui| { self.log(); - self.ui(ctx, ui); + self.ui(ctx, ui, frame); }); + + if self.is_finished { + return Some(ExitStatus::Ok); + } + None } +} +impl DBTransactionWindow { pub fn log(&mut self) { + if let Some(finish_receiver) = self.final_result_receiver.as_mut() { + if let Ok(result) = finish_receiver.try_recv() { + if result.result.is_ok() { + self.is_finished = true; + } + } + } if self.logs_receiver.is_some() { if let Ok(log) = self.logs_receiver.as_mut().unwrap().try_recv() { self.log_history.push(log); @@ -61,8 +83,8 @@ impl DBTransactionWindow { } if self.is_log_finished_receiver.is_some() { if let Ok(finished) = self.is_log_finished_receiver.as_mut().unwrap().try_recv() { - self.result = Some(vec![]); - println!("FINISHED THE QUERY!!!"); + self.is_log_finished = finished; + println!("FINISHED QUERYING!!!"); } } if self.is_log_finished_receiver.is_none() && self.logs_receiver.is_none() { @@ -79,8 +101,8 @@ impl DBTransactionWindow { .unwrap_or_else(|_| println!("Failed to send startInserting")); } } - pub fn ui(&mut self, ctx: &Context, ui: &mut Ui) { - ui.add_enabled_ui(self.result.is_some(), |ui| { + pub fn ui(&mut self, ctx: &Context, ui: &mut Ui, frame: &mut eframe::Frame) { + ui.add_enabled_ui(self.is_log_finished, |ui| { ui.horizontal(|ui| { if ui.button("Roll back").clicked() { if self.final_result_receiver.is_none() { @@ -103,7 +125,7 @@ impl DBTransactionWindow { }); }); /* DB Output Stuff */ - egui::ScrollArea::both() + egui::ScrollArea::vertical() .auto_shrink([false; 2]) .stick_to_bottom(true) .show(ui, |ui| { @@ -111,9 +133,17 @@ impl DBTransactionWindow { .striped(true) .resizable(true) .cell_layout(egui::Layout::left_to_right(egui::Align::LEFT)) - .column(egui_extras::Column::remainder().clip(true)) + .column( + egui_extras::Column::remainder() + .clip(true) + .at_most(frame.info().window_info.size.x / 1.3 / 2.0), + ) .stick_to_bottom(true) - .column(egui_extras::Column::remainder().clip(true)) + .column( + egui_extras::Column::remainder() + .clip(true) + .at_most(frame.info().window_info.size.x / 1.3 / 2.0), + ) .stick_to_bottom(true); table @@ -155,3 +185,8 @@ impl DBTransactionWindow { }); } } + +pub struct OptionsDBTransactionWindow { + 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 1457eed..b14e8ac 100644 --- a/src/ui/table_window.rs +++ b/src/ui/table_window.rs @@ -1,23 +1,20 @@ +use super::window_manager::{CSQLWindow, ExitStatus}; use crate::backend::backend_manager::Communication; use crate::backend::csv_handler::ImportedData; use crate::backend::database_handler::{TableField, Tables}; use egui::{ComboBox, Context, Ui}; -use egui_extras::{Column, Size, StripBuilder, TableBuilder}; +use egui_extras::{Column, TableBuilder}; use std::error::Error; use std::sync::Arc; -use tokio::sync::Mutex; -use tokio::sync::{mpsc::Sender, oneshot}; +use tokio::sync::mpsc::Sender; +use tokio::sync::{Mutex, MutexGuard}; pub struct SpreadSheetWindow { sender: Sender, csv_data_handle: Arc>, db_table_data_handle: Arc>, - is_file_loaded_receiver: Option>, - is_file_loaded: bool, current_table: Option, - is_table_described_receiver: Option>, - is_table_described: bool, - is_table_matched_with_headers: bool, - commit_to_db_sender: Sender, + should_export_to_db: bool, + debug_autoload_file_sent: bool, } impl SpreadSheetWindow { pub fn default( @@ -28,147 +25,121 @@ impl SpreadSheetWindow { ) -> SpreadSheetWindow { SpreadSheetWindow { sender, - is_file_loaded_receiver: None, - is_file_loaded: false, - is_table_described_receiver: None, - is_table_described: false, - is_table_matched_with_headers: false, csv_data_handle, db_table_data_handle, current_table: None, - commit_to_db_sender, + debug_autoload_file_sent: false, + should_export_to_db: false, } } } -impl SpreadSheetWindow { - pub fn show(&mut self, ctx: &Context, ui: &mut Ui, is_db_connection_verified: bool) { - self.ui(ui, ctx, is_db_connection_verified); +impl CSQLWindow for SpreadSheetWindow { + fn refresh( + &mut self, + ctx: &egui::Context, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + ) -> Option { + self.ui(ui, ctx); + if self.should_export_to_db { + self.should_export_to_db = false; + return Some(ExitStatus::StartTransactionWindow); + } + None } - - fn ui(&mut self, ui: &mut Ui, ctx: &Context, is_db_connection_verified: bool) { - /* Program Menu */ - - ui.horizontal_top(|ui| { - if ui.button("Open File").clicked() { - if let Some(path) = rfd::FileDialog::new() - .add_filter("Spreadsheets", &["csv"]) - .pick_file() - { - self.open_file(path.display().to_string()); - }; - } - if ui.button("Save File").clicked() { - println!("Saving file lol"); - } - ui.add_space(ui.available_width() * 0.8); - - let is_csv_parsed: bool; - if let Ok(csv_data) = self.csv_data_handle.try_lock() { - is_csv_parsed = csv_data.is_parsed.clone(); - } else { - is_csv_parsed = false; - } - ui.add_enabled_ui(is_csv_parsed, |ui| { - if ui.button("Commit to Database").clicked() { - self.commit_to_db_sender - .try_send(self.current_table.unwrap()) - .unwrap_or_else(|_| println!("failed to send committodb-UI")) - } - }); - }); - - /* if db isn't connected, don't allow imports */ - - if !is_db_connection_verified { - return; - }; - - /* Handle file drops */ - - ctx.input(|i| { - if !i.raw.dropped_files.is_empty() { - self.open_file( - i.raw - .dropped_files - .clone() - .get(0) - .unwrap() - .path - .as_ref() - .unwrap() - .display() - .to_string(), - ); - } - }); - self.check_file(); - SpreadSheetWindow::preview_files_being_dropped(ctx); - - if !self.is_file_loaded { - ui.centered_and_justified(|ui| ui.heading("Drag and drop or Open a file...")); +} +impl SpreadSheetWindow { + fn ui(&mut self, ui: &mut Ui, ctx: &Context) { + /* if csv was auto mapped and parsed */ + let is_csv_parsed: bool; + if let Ok(csv_data) = self.csv_data_handle.try_lock() { + is_csv_parsed = csv_data.is_parsed.clone(); + } else { + is_csv_parsed = false; } - if self.is_file_loaded { - ui.group(|ui| { - StripBuilder::new(ui) //So the window doesn't grow with the innerts - .size(Size::remainder().at_least(100.0)) // for the table - .vertical(|mut strip| { - strip.cell(|ui| { - egui::ScrollArea::horizontal().show(ui, |ui| self.table_options(ui)); - }); - }); - }); - }; - } - fn table_options(&mut self, ui: &mut Ui) { - /* Create table select option */ - - if let Ok(db_table_data) = &mut self.db_table_data_handle.try_lock() { - let mut select_table = ComboBox::from_label("Select Table"); - if let Some(table_index) = self.current_table { - select_table = select_table - .selected_text(&db_table_data.tables.get(table_index).unwrap().name); - } - ui.vertical(|ui| { - select_table.show_ui(ui, |ui| { - for (table_i, table) in db_table_data.tables.iter().enumerate() { - ui.selectable_value( - &mut self.current_table, - Some(table_i.clone()), - &table.name, - ); + /* Program Menu */ + ui.horizontal(|ui| { + ui.group(|ui| {}); + }); + /* CSV & DB menu */ + ui.horizontal(|ui| { + ui.columns(2, |uis| { + uis[0].group(|ui| { + if ui.button("Import file").clicked() { + self.open_file(); + } + if ui.button("Save").clicked() { + self.save_file(); + } + if ui.button("Save as...").clicked() { + self.save_file(); } }); - }); - - /* If a table is selected, try if it's fields are discovered */ - if !self.is_table_described { - if let Some(table_i) = self.current_table { - if db_table_data.tables.get(table_i).unwrap().fields.is_some() { - self.is_table_described = true; - } else if self.is_table_described_receiver.is_some() { - match self - .is_table_described_receiver - .as_mut() - .unwrap() - .try_recv() - { - Ok(res) => self.is_table_matched_with_headers = res, - Err(e) => println!("Failed receiving is_table_described, {}", e), + 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() { + todo!(); } - } else { - let (sender, receiver) = oneshot::channel(); - self.is_table_described_receiver = Some(receiver); - self.sender - .try_send(Communication::GetTableDescription(table_i, sender)) - .unwrap_or_else(|e| println!("Failed asking to describe table, {}", e)); + 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!(); + } + }); + }); + }); + }) + }); + + /* Handle file drops */ + ctx.input(|i| { + if !i.raw.dropped_files.is_empty() { + self.open_dropped_file(i); + } + }); + SpreadSheetWindow::preview_files_being_dropped(ctx); + + if self.current_table.is_none() { + ui.group(|ui| { + egui::ScrollArea::horizontal().show(ui, |ui| { + self.table_builder(ui); + }); + }); + } + } + fn table_options(&mut self, ui: &mut Ui) { + /* Create table select option, only enable if the tables are discovered yet*/ + if let Ok(db_table_data) = &mut self.db_table_data_handle.try_lock() { + ui.add_enabled_ui(db_table_data.tables.get(0).is_some(), |ui| { + let mut select_table = ComboBox::from_label("Select Table").width(100.0); + if let Some(table_index) = self.current_table { + if let Some(current_table) = &db_table_data.tables.get(table_index) { + select_table = select_table.selected_text(current_table.name.clone()); } } - } - } - - if self.is_table_described { - self.table_builder(ui); + /* autoselect oc_product in debug mode */ + if cfg!(debug_assertions) && self.current_table.is_none() { + self.current_table = Some(88); + if let Some(table_88) = &db_table_data.tables.get(88) { + select_table = select_table.selected_text(table_88.name.clone()); + } + } + ui.vertical(|ui| { + select_table.show_ui(ui, |ui| { + for (table_i, table) in db_table_data.tables.iter().enumerate() { + ui.selectable_value( + &mut self.current_table, + Some(table_i.clone()), + &table.name, + ); + } + }); + }); + }); } } fn table_builder(&mut self, ui: &mut Ui) { @@ -186,7 +157,7 @@ impl SpreadSheetWindow { .striped(true) .cell_layout(egui::Layout::left_to_right(egui::Align::Center)); - for _i in 0..csv_data.data.cols() { + for _i in 0..csv_data.data.cols() + 1 { table = table.column(Column::auto().resizable(true).clip(false)); } /* If cols got prematched by name */ @@ -195,62 +166,15 @@ impl SpreadSheetWindow { .column(Column::remainder()) .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| { - 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() - { - self.is_table_described = false; - match self.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) - } - } - } - }); - ui.reset_style(); - }); + SpreadSheetWindow::add_header_col(ui,i, csv_data,&mut curr_db_table_fields, &mut self.sender); + }); } }) .body(|body| { @@ -287,33 +211,125 @@ impl SpreadSheetWindow { }); }); } + } else { + ui.centered_and_justified(|ui| ui.heading("Drag and drop or Open a file...")); } } } impl SpreadSheetWindow { - pub fn check_file(&mut self) { - if let Some(receiver) = &mut self.is_file_loaded_receiver { - match receiver.try_recv() { - Ok(boole) => { - if boole { - self.is_file_loaded_receiver = None; - } - self.is_file_loaded = boole; - } - Err(e) => println!("Failed receiving load file callback, {}", e), + pub fn add_header_col( + ui: &mut Ui, + i: usize, + 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, ""); + } + + //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() + { + 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), + } + } + } + }); + ui.reset_style(); + }); + } + + pub fn save_file(&mut self) { + let mut save_name = "to-csv".to_owned(); + if let Some(table_i) = self.current_table { + if let Ok(db_table_data) = self.db_table_data_handle.try_lock() { + save_name = db_table_data.tables.get(table_i).unwrap().name.clone(); + } + } + if let Some(path) = rfd::FileDialog::new() + .set_file_name(format!("db-{}-converted.csv", save_name).as_str()) + .save_file() + { + self.sender + .try_send(Communication::SaveCSV(path.display().to_string())) + .unwrap_or_else(|err| println!("failed to send loadimportpath, {}", err)); + }; + } + + pub fn open_file(&mut self) { + /*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); + self.debug_autoload_file_sent = true; + } + if let Some(path) = rfd::FileDialog::new() + .add_filter("Spreadsheets", &["csv"]) + .pick_file() + { + self.sender + .try_send(Communication::LoadImportFilePath( + path.display().to_string(), + )) + .unwrap_or_else(|err| println!("failed to send loadimportpath, {}", err)); } } - pub fn open_file(&mut self, path: String) { - if !self.is_file_loaded_receiver.is_some() { - let (sed, rec) = oneshot::channel(); - self.sender - .try_send(Communication::LoadImportFilePath(path, sed)) - .unwrap_or_else(|err| println!("failed to send loadimportpath, {}", err)); - self.is_file_loaded_receiver = Some(rec); - } + pub fn open_dropped_file(&mut self, path: &egui::InputState) { + let path = path + .raw + .dropped_files + .clone() + .get(0) + .unwrap() + .path + .as_ref() + .unwrap() + .display() + .to_string(); + + self.sender + .try_send(Communication::LoadImportFilePath(path)) + .unwrap_or_else(|err| println!("failed to send loadimportpathdropped, {}", err)); } pub fn preview_files_being_dropped(ctx: &egui::Context) { diff --git a/src/ui/window_manager.rs b/src/ui/window_manager.rs index 992c1b7..48984c1 100644 --- a/src/ui/window_manager.rs +++ b/src/ui/window_manager.rs @@ -10,6 +10,7 @@ use tokio::sync::oneshot; use tokio::sync::Mutex; use super::db_transaction_window::DBTransactionWindow; +use super::{db_login_window, db_transaction_window}; pub fn create_ui( sender: Sender, @@ -32,15 +33,13 @@ pub fn create_ui( csv_data_handle.clone(), db_table_data_handle.clone(), ), - db_login_window: DBLoginWindow::default(sender.clone()), + db_login_window: None, db_transaction_window: None, - is_db_connection_verified_receiver: None, sender, - is_db_connection_verified: false, csv_data_handle, db_table_data_handle, - open_db_transaction_window_receiver, - should_open_db_transaction_window: None, + should_open_transaction_window: false, + should_open_login_window: true, }) }), ) @@ -48,60 +47,67 @@ pub fn create_ui( struct CSQL { spreadsheet_window: SpreadSheetWindow, - db_login_window: DBLoginWindow, + db_login_window: Option, db_transaction_window: Option, sender: Sender, - is_db_connection_verified_receiver: Option>, - is_db_connection_verified: bool, csv_data_handle: Arc>, db_table_data_handle: Arc>, - open_db_transaction_window_receiver: Receiver, - should_open_db_transaction_window: Option, + should_open_transaction_window: bool, + should_open_login_window: bool, } impl App for CSQL { fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { egui::CentralPanel::default().show(ctx, |ui| { - self.db_login_window - .show(ctx, ui, self.is_db_connection_verified); - - self.spreadsheet_window - .show(ctx, ui, self.is_db_connection_verified); - - if let Ok(resp) = self.open_db_transaction_window_receiver.try_recv() { - self.should_open_db_transaction_window = Some(resp); - } - if let Some(db_transaction_window) = self.db_transaction_window.as_mut() { - db_transaction_window.show(ctx, ui, frame) - } - if let Some(working_table_index) = self.should_open_db_transaction_window { - let db_transaction_window = - DBTransactionWindow::default(self.sender.clone(), working_table_index); - self.db_transaction_window = Some(db_transaction_window); - self.db_transaction_window - .as_mut() - .unwrap() - .show(ctx, ui, frame); - self.should_open_db_transaction_window = None; - } - - /* Changes self if db connection is verified */ - if let Some(oneshot_receiver) = &mut self.is_db_connection_verified_receiver { - if let Ok(boole) = oneshot_receiver.try_recv() { - self.is_db_connection_verified = boole; - self.is_db_connection_verified_receiver = None; + 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, + _ => (), } - } else { - let (sed, rec) = oneshot::channel(); - self.is_db_connection_verified_receiver = Some(rec); - match self - .sender - .try_send(Communication::AreCreditentialsValidated(sed)) - { - Ok(_) => (), - Err(e) => println!("Failed to send-verify db conn oneshot, {}", e), + }; + + if self.should_open_login_window { + 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 => { + self.db_login_window = None; + self.should_open_login_window = false; + } + _ => (), + } + } + } + } + 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 => { + self.db_transaction_window = None; + self.should_open_transaction_window = false; + } + _ => (), + } + } } } }); } } + +pub trait CSQLWindow { + fn refresh( + &mut self, + ctx: &egui::Context, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + ) -> Option; +} +pub enum ExitStatus { + StartTransactionWindow, + StartLoginWindow, + Ok, + Err(Box), +}