diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e1b93a4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Tauri Development Debug", + "cargo": { + "args": ["build", "--manifest-path=./src-tauri/Cargo.toml", "--no-default-features"] + }, + // task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json` + "preLaunchTask": "ui:dev" + }, + { + "type": "lldb", + "request": "launch", + "name": "Tauri Production Debug", + "cargo": { + "args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"] + }, + // task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json` + "preLaunchTask": "ui:build" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..c3a854e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,25 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "ui:dev", + "type": "shell", + // `dev` keeps running in the background + // ideally you should also configure a `problemMatcher` + // see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson + "isBackground": true, + // change this to your `beforeDevCommand`: + "command": "cargo", + "args": ["tauri", "dev"] + }, + { + "label": "ui:build", + "type": "shell", + // change this to your `beforeBuildCommand`: + "command": "cargo", + "args": ["tauri", "build"] + } + ] +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1a41563..62245e2 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,8 +7,9 @@ mod project_db; mod query_db; mod utils; -use postgres::{get_schema_tables, get_sql_result, pg_connector}; -use project_db::{get_project_details, get_projects, remove_project}; +use postgres::{pg_connector, select_schema_tables, select_sql_result}; +use project_db::{delete_project, select_project_details, select_projects}; +use query_db::{delete_query, insert_query, select_queries}; use sled::Db; use std::sync::Arc; #[cfg(debug_assertions)] @@ -49,12 +50,15 @@ fn main() { Ok(()) }) .invoke_handler(tauri::generate_handler![ - get_projects, - get_project_details, - get_schema_tables, + delete_project, + delete_query, + insert_query, pg_connector, - get_sql_result, - remove_project, + select_projects, + select_project_details, + select_queries, + select_schema_tables, + select_sql_result, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/postgres.rs b/src-tauri/src/postgres.rs index 276ea24..4ec9562 100644 --- a/src-tauri/src/postgres.rs +++ b/src-tauri/src/postgres.rs @@ -41,7 +41,7 @@ pub async fn pg_connector(project: &str, key: &str, app: AppHandle) -> Result, ) -> Result> { @@ -64,7 +64,7 @@ pub async fn get_schema_tables( } #[tauri::command] -pub async fn get_sql_result( +pub async fn select_sql_result( sql: String, app_state: State<'_, AppState>, ) -> Result<(Vec, Vec>)> { diff --git a/src-tauri/src/project_db.rs b/src-tauri/src/project_db.rs index 47ce3ef..0f9a80f 100644 --- a/src-tauri/src/project_db.rs +++ b/src-tauri/src/project_db.rs @@ -12,7 +12,7 @@ pub struct ProjectDetails { } #[tauri::command] -pub async fn get_projects(app: AppHandle) -> Result> { +pub async fn select_projects(app: AppHandle) -> Result> { let app_state = app.state::(); let mut db = app_state.project_db.lock().await; if db.clone().is_none() { @@ -34,7 +34,7 @@ pub async fn get_projects(app: AppHandle) -> Result> { } #[tauri::command] -pub async fn get_project_details( +pub async fn select_project_details( project: String, app_state: State<'_, AppState>, ) -> Result { @@ -67,7 +67,7 @@ pub async fn get_project_details( } #[tauri::command] -pub async fn remove_project(project: String, app_state: State<'_, AppState>) -> Result<()> { +pub async fn delete_project(project: String, app_state: State<'_, AppState>) -> Result<()> { let db = app_state.project_db.lock().await; let db = db.clone().unwrap(); db.remove(project).unwrap(); diff --git a/src-tauri/src/query_db.rs b/src-tauri/src/query_db.rs index 139597f..0ab8614 100644 --- a/src-tauri/src/query_db.rs +++ b/src-tauri/src/query_db.rs @@ -1,2 +1,26 @@ +use std::collections::HashMap; +use serde::Serialize; +use tauri::Result; + +#[derive(Default, Serialize)] +pub struct QueryDetails { + pub title: String, + pub sql: String, +} + +#[tauri::command] +pub fn insert_query(key: String, sql: String) { + todo!() +} + +#[tauri::command] +pub async fn select_queries() -> Result> { + todo!() +} + +#[tauri::command] +pub async fn delete_query(key: String) { + todo!() +} diff --git a/src/app.rs b/src/app.rs index e39d3b6..264d0ef 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,20 +1,20 @@ use std::vec; use crate::{ - layout::layout, - query_editor::query_editor, - query_table::query_table, - store::{db::DBStore, editor::EditorState, query::QueryState}, + layout::layout, + query_editor::query_editor, + query_table::query_table, + store::{db::DBStore, editor::EditorState, query::QueryState}, }; use leptos::*; pub fn app() -> impl IntoView { - provide_context(DBStore::default()); - provide_context(EditorState::default()); - provide_context(QueryState::default()); + provide_context(DBStore::default()); + provide_context(EditorState::default()); + provide_context(QueryState::default()); - layout(Children::to_children(move || { - Fragment::new(vec![query_editor().into_view(), query_table().into_view()]) - })) + layout(Children::to_children(move || { + Fragment::new(vec![query_editor().into_view(), query_table().into_view()]) + })) } diff --git a/src/invoke.rs b/src/invoke.rs index 585d1d3..47fda65 100644 --- a/src/invoke.rs +++ b/src/invoke.rs @@ -4,23 +4,23 @@ use serde::{Deserialize, Serialize}; #[allow(non_camel_case_types)] pub enum Invoke { - get_projects, - get_project_details, - remove_project, - get_sql_result, - get_schema_tables, + delete_project, pg_connector, + select_projects, + select_project_details, + select_schema_tables, + select_sql_result, } impl Display for Invoke { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Invoke::get_projects => write!(f, "get_projects"), - Invoke::get_project_details => write!(f, "get_project_details"), - Invoke::remove_project => write!(f, "remove_project"), - Invoke::get_sql_result => write!(f, "get_sql_result"), - Invoke::get_schema_tables => write!(f, "get_schema_tables"), + Invoke::delete_project => write!(f, "delete_project"), Invoke::pg_connector => write!(f, "pg_connector"), + Invoke::select_projects => write!(f, "select_projects"), + Invoke::select_project_details => write!(f, "select_project_details"), + Invoke::select_schema_tables => write!(f, "select_schema_tables"), + Invoke::select_sql_result => write!(f, "select_sql_result"), } } } @@ -53,3 +53,4 @@ pub struct InvokeProjectDetailsArgs { pub struct InvokeRemoveProjectArgs { pub project: String, } + diff --git a/src/query_editor.rs b/src/query_editor.rs index 2171b89..dfc3e83 100644 --- a/src/query_editor.rs +++ b/src/query_editor.rs @@ -2,8 +2,8 @@ use std::{cell::RefCell, rc::Rc}; use leptos::{html::*, *}; use monaco::{ - api::{CodeEditor, CodeEditorOptions}, - sys::editor::IDimension, + api::{CodeEditor, CodeEditorOptions}, + sys::editor::{IDimension, IEditorMinimapOptions}, }; use wasm_bindgen::JsCast; @@ -12,31 +12,31 @@ use crate::store::{editor::EditorState, query::QueryState}; pub type ModelCell = Rc>>; pub fn query_editor() -> impl IntoView { - let query = use_context::().unwrap().sql; - let set_editor = use_context::().unwrap().editor; - let node_ref = create_node_ref(); - - node_ref.on_load(move |node| { - let div_element: &web_sys::HtmlDivElement = &node; - let html_element = div_element.unchecked_ref::(); - let editor = CodeEditor::create( - html_element, - Some(CodeEditorOptions { - value: Some(query.get()), - language: Some("sql".to_string()), - automatic_layout: Some(true), - dimension: Some(IDimension::new(0, 240)), - ..Default::default() - }), - ); - - set_editor.update(|prev| { - prev.replace(Some(editor)); - }); + let query = use_context::().unwrap().sql; + let set_editor = use_context::().unwrap().editor; + let node_ref = create_node_ref(); + + node_ref.on_load(move |node| { + let div_element: &web_sys::HtmlDivElement = &node; + let html_element = div_element.unchecked_ref::(); + let options = CodeEditorOptions::default().to_sys_options(); + options.set_value(Some(&query.get())); + options.set_language(Some("sql")); + options.set_automatic_layout(Some(true)); + options.set_dimension(Some(&IDimension::new(0, 240))); + let minimap_settings = IEditorMinimapOptions::default(); + minimap_settings.set_enabled(Some(false)); + options.set_minimap(Some(&minimap_settings)); + + let editor = CodeEditor::create(html_element, Some(options)); + + set_editor.update(|prev| { + prev.replace(Some(editor)); }); + }); - div() - .attr("class", "border-b-1 border-neutral-200 sticky") - .node_ref(node_ref) + div() + .attr("class", "border-b-1 border-neutral-200 sticky") + .node_ref(node_ref) } diff --git a/src/sidebar.rs b/src/sidebar.rs index ed577ec..74995cd 100644 --- a/src/sidebar.rs +++ b/src/sidebar.rs @@ -8,27 +8,27 @@ use leptos::{html::*, *}; pub fn sidebar() -> impl IntoView { let mut db = use_context::().unwrap(); - let get_project_details = create_action(move |(db, project): &(DBStore, String)| { + let select_project_details = create_action(move |(db, project): &(DBStore, String)| { let mut db_clone = *db; let project = project.clone(); - async move { db_clone.get_project_details(project).await } + async move { db_clone.select_project_details(project).await } }); let projects = create_resource( move || db.is_connecting.get(), move |_| async move { let projects = invoke( - &Invoke::get_projects.to_string(), + &Invoke::select_projects.to_string(), serde_wasm_bindgen::to_value(&InvokeProjectsArgs).unwrap_or_default(), ) .await; serde_wasm_bindgen::from_value::>(projects).unwrap() }, ); - let remove_project = create_action(move |(db, project): &(DBStore, String)| { + let delete_project = create_action(move |(db, project): &(DBStore, String)| { let mut db_clone = *db; let project = project.clone(); async move { - db_clone.remove_project(project).await.unwrap(); + db_clone.delete_project(project).await.unwrap(); projects.refetch(); } }); @@ -48,7 +48,7 @@ pub fn sidebar() -> impl IntoView { .child(&project) .on(ev::click, { let project = project.clone(); - move |_| get_project_details.dispatch((db, project.clone())) + move |_| select_project_details.dispatch((db, project.clone())) }), ) .child( @@ -58,7 +58,7 @@ pub fn sidebar() -> impl IntoView { .on(ev::click, { let project = project.clone(); move |_| { - remove_project.dispatch((db, project.clone())); + delete_project.dispatch((db, project.clone())); } }), ) diff --git a/src/store/db.rs b/src/store/db.rs index 7ea2ad2..d649a93 100644 --- a/src/store/db.rs +++ b/src/store/db.rs @@ -2,7 +2,7 @@ use crate::{ invoke::{Invoke, InvokePostgresConnectionArgs, InvokeRemoveProjectArgs, InvokeTablesArgs}, wasm_functions::invoke, }; -use leptos::{create_rw_signal, logging, RwSignal, SignalGetUntracked, SignalSet, SignalUpdate}; +use leptos::{create_rw_signal, RwSignal, SignalGetUntracked, SignalSet, SignalUpdate}; use std::collections::HashMap; #[derive(Clone, Copy, Debug)] @@ -50,7 +50,6 @@ impl DBStore { self.db_user.set(String::new()); self.db_password.set(String::new()); self.is_connecting.set(false); - logging::log!("{:?}", self.project.get_untracked()); } pub fn create_connection_string(&self) -> String { @@ -80,7 +79,7 @@ impl DBStore { self.is_connecting.set(false); } - pub async fn get_tables(&mut self, schema: String) -> Result, ()> { + pub async fn select_tables(&mut self, schema: String) -> Result, ()> { if let Some(tables) = self.tables.get_untracked().get(&schema) { if !tables.is_empty() { return Ok(tables.clone()); @@ -91,7 +90,7 @@ impl DBStore { schema: schema.to_string(), }) .unwrap(); - let tables = invoke(&Invoke::get_schema_tables.to_string(), args).await; + let tables = invoke(&Invoke::select_schema_tables.to_string(), args).await; let mut tables = serde_wasm_bindgen::from_value::>(tables).unwrap(); tables.sort(); let tables = tables @@ -104,13 +103,13 @@ impl DBStore { Ok(tables) } - pub async fn get_project_details(&mut self, project: String) -> Result<(), ()> { + pub async fn select_project_details(&mut self, project: String) -> Result<(), ()> { let args = serde_wasm_bindgen::to_value(&InvokePostgresConnectionArgs { project: project.clone(), key: String::new(), }) .unwrap(); - let project_details = invoke(&Invoke::get_project_details.to_string(), args).await; + let project_details = invoke(&Invoke::select_project_details.to_string(), args).await; let project_details = serde_wasm_bindgen::from_value::>(project_details).unwrap(); self.project.set(project); @@ -129,9 +128,9 @@ impl DBStore { Ok(()) } - pub async fn remove_project(&mut self, project: String) -> Result<(), ()> { + pub async fn delete_project(&mut self, project: String) -> Result<(), ()> { let args = serde_wasm_bindgen::to_value(&InvokeRemoveProjectArgs { project }).unwrap(); - invoke(&Invoke::remove_project.to_string(), args).await; + invoke(&Invoke::delete_project.to_string(), args).await; Ok(()) } } diff --git a/src/store/query.rs b/src/store/query.rs index 4e8739e..cc4f79b 100644 --- a/src/store/query.rs +++ b/src/store/query.rs @@ -48,7 +48,7 @@ impl QueryState { }) .unwrap(); - let data = invoke(&Invoke::get_sql_result.to_string(), args).await; + let data = invoke(&Invoke::select_sql_result.to_string(), args).await; let data = serde_wasm_bindgen::from_value::<(Vec, Vec>)>(data).unwrap(); self.sql_result.update(|prev| { *prev = Some(data); @@ -58,3 +58,4 @@ impl QueryState { }); } } + diff --git a/src/tables.rs b/src/tables.rs index 4119d41..829a514 100644 --- a/src/tables.rs +++ b/src/tables.rs @@ -2,83 +2,80 @@ use crate::store::{db::DBStore, editor::EditorState, query::QueryState}; use leptos::{html::*, *}; pub fn tables(schema: String) -> impl IntoView { - let mut db = use_context::().unwrap(); - let query_store = use_context::().unwrap(); - let tables = create_resource( - || {}, - move |_| { - let schema = schema.clone(); - async move { db.get_tables(schema).await.unwrap() } - }, - ); - let when = move || !tables.get().unwrap_or_default().is_empty(); - let show_fallback = move || ViewFn::from(|| p().attr("class", "pl-2").child("No tables found")); - let fallback = ViewFn::from(|| p().attr("class", "pl-2").child("Loading...")); - let tables = move || { - tables - .get() - .unwrap_or_default() - .into_iter() - .enumerate() - .map(|(i, (table, is_selected))| { - let table_clone = table.clone(); - li().attr("key", i) - .attr( - "class", - if is_selected { - "pl-4 font-semibold cursor-pointer" - } else { - "hover:font-semibold pl-4 cursor-pointer" - }, - ) - .on(ev::click, move |_| { - let schema = db - .schemas - .get_untracked() - .iter() - .find(|(_, is_selected)| **is_selected) - .unwrap() - .0 - .clone(); - let t_clone = table_clone.clone(); - spawn_local(async move { - let editor = - use_context::().unwrap().editor.get_untracked(); - editor - .borrow() - .as_ref() - .unwrap() - .get_model() - .unwrap() - .set_value(&format!( - "SELECT * FROM {}.{} LIMIT 100;", - schema, t_clone - )); - query_store.run_query().await; - }); - let table_clone = table_clone.clone(); - tables.update(move |prev| { - prev.iter_mut().for_each(|tables| { - tables.iter_mut().for_each(|(t, s)| { - *s = t == &table_clone; - }); - }); - }); - }) - .child(table) - }) - .collect_view() - }; - let show_children = - move || ChildrenFn::to_children(move || Fragment::new(vec![tables().into_view()])); - let children = ChildrenFn::to_children(move || { - Fragment::new(vec![Show(ShowProps { - children: show_children(), - when, - fallback: show_fallback(), - }) - .into_view()]) - }); - Suspense::(SuspenseProps { fallback, children }) + let mut db = use_context::().unwrap(); + let query_store = use_context::().unwrap(); + let tables = create_resource( + || {}, + move |_| { + let schema = schema.clone(); + async move { db.select_tables(schema).await.unwrap() } + }, + ); + let when = move || !tables.get().unwrap_or_default().is_empty(); + let show_fallback = move || ViewFn::from(|| p().attr("class", "pl-2").child("No tables found")); + let fallback = ViewFn::from(|| p().attr("class", "pl-2").child("Loading...")); + let tables = move || { + tables + .get() + .unwrap_or_default() + .into_iter() + .enumerate() + .map(|(i, (table, is_selected))| { + let table_clone = table.clone(); + li() + .attr("key", i) + .attr( + "class", + if is_selected { + "pl-4 font-semibold cursor-pointer" + } else { + "hover:font-semibold pl-4 cursor-pointer" + }, + ) + .on(ev::click, move |_| { + let schema = db + .schemas + .get_untracked() + .iter() + .find(|(_, is_selected)| **is_selected) + .unwrap() + .0 + .clone(); + let t_clone = table_clone.clone(); + spawn_local(async move { + let editor = use_context::().unwrap().editor.get_untracked(); + editor + .borrow() + .as_ref() + .unwrap() + .get_model() + .unwrap() + .set_value(&format!("SELECT * FROM {}.{} LIMIT 100;", schema, t_clone)); + query_store.run_query().await; + }); + let table_clone = table_clone.clone(); + tables.update(move |prev| { + prev.iter_mut().for_each(|tables| { + tables.iter_mut().for_each(|(t, s)| { + *s = t == &table_clone; + }); + }); + }); + }) + .child(table) + }) + .collect_view() + }; + let show_children = + move || ChildrenFn::to_children(move || Fragment::new(vec![tables().into_view()])); + let children = ChildrenFn::to_children(move || { + Fragment::new(vec![Show(ShowProps { + children: show_children(), + when, + fallback: show_fallback(), + }) + .into_view()]) + }); + Suspense::(SuspenseProps { fallback, children }) }