Support copy sql with result

This commit is contained in:
Spxg
2025-05-22 01:27:44 +08:00
parent 50e620ba38
commit 097133d749
5 changed files with 261 additions and 14 deletions

148
Cargo.lock generated
View File

@@ -291,6 +291,27 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "csv"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "dashmap" name = "dashmap"
version = "6.1.0" version = "6.1.0"
@@ -351,6 +372,27 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.5" version = "0.2.5"
@@ -384,6 +426,12 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.2" version = "1.0.2"
@@ -640,6 +688,12 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hermit-abi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
[[package]] [[package]]
name = "hex" name = "hex"
version = "0.4.3" version = "0.4.3"
@@ -848,6 +902,17 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8"
[[package]]
name = "is-terminal"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [
"hermit-abi 0.5.1",
"libc",
"windows-sys",
]
[[package]] [[package]]
name = "istyles" name = "istyles"
version = "0.1.0" version = "0.1.0"
@@ -883,6 +948,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "leptos" name = "leptos"
version = "0.8.2" version = "0.8.2"
@@ -1015,6 +1086,16 @@ version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags",
"libc",
]
[[package]] [[package]]
name = "linear-map" name = "linear-map"
version = "1.2.0" version = "1.2.0"
@@ -1141,7 +1222,7 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi 0.3.9",
"libc", "libc",
] ]
@@ -1274,6 +1355,20 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "prettytable-rs"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a"
dependencies = [
"csv",
"encode_unicode",
"is-terminal",
"lazy_static",
"term",
"unicode-width",
]
[[package]] [[package]]
name = "proc-macro-error-attr2" name = "proc-macro-error-attr2"
version = "2.0.0" version = "2.0.0"
@@ -1427,6 +1522,17 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "redox_users"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom 0.2.16",
"libredox",
"thiserror 1.0.69",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.1" version = "1.11.1"
@@ -1717,6 +1823,7 @@ dependencies = [
"log", "log",
"once_cell", "once_cell",
"parking_lot", "parking_lot",
"prettytable-rs",
"reactive_stores", "reactive_stores",
"serde", "serde",
"serde-wasm-bindgen", "serde-wasm-bindgen",
@@ -1823,6 +1930,17 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "term"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
dependencies = [
"dirs-next",
"rustversion",
"winapi",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
@@ -1958,6 +2076,12 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.6" version = "0.2.6"
@@ -2121,6 +2245,22 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.9" version = "0.1.9"
@@ -2130,6 +2270,12 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"

View File

@@ -25,7 +25,7 @@ reactive_stores = "0.2.2"
thiserror = "2.0.12" thiserror = "2.0.12"
serde = "1.0.219" serde = "1.0.219"
serde_json = "1.0.140" serde_json = "1.0.140"
web-sys = { version = "0.3.77", features = ["Clipboard", "DedicatedWorkerGlobalScope", "File", "FileList", "HtmlSelectElement", "MediaQueryList", "Navigator", "Storage", "Worker", "WorkerOptions", "WorkerType"] } web-sys = { version = "0.3.77", features = ["BlobPropertyBag", "Clipboard", "DedicatedWorkerGlobalScope", "File", "FileList", "HtmlSelectElement", "MediaQueryList", "Navigator", "Storage", "Worker", "WorkerOptions", "WorkerType"] }
serde-wasm-bindgen = "0.6.5" serde-wasm-bindgen = "0.6.5"
parking_lot = "0.12.3" parking_lot = "0.12.3"
once_cell = "1.21.3" once_cell = "1.21.3"
@@ -37,3 +37,4 @@ console_log = "1.0.0"
log = "0.4.27" log = "0.4.27"
fragile = "2.0.1" fragile = "2.0.1"
hex = "0.4.3" hex = "0.4.3"
prettytable-rs = "0.10.0"

View File

@@ -1,12 +1,13 @@
use istyles::istyles; use istyles::istyles;
use leptos::{html::Input, prelude::*, tachys::html}; use leptos::{html::Input, prelude::*, tachys::html};
use prettytable::{Cell, Row, Table};
use reactive_stores::Store; use reactive_stores::Store;
use wasm_bindgen::{JsCast, prelude::Closure}; use wasm_bindgen::{JsCast, prelude::Closure};
use web_sys::{Blob, Event, FileReader, HtmlInputElement, Url, UrlSearchParams}; use web_sys::{Blob, Event, FileReader, HtmlInputElement, Url, UrlSearchParams};
use crate::{ use crate::{
DownloadDbOptions, FragileComfirmed, LoadDbOptions, PrepareOptions, SQLightError, DownloadDbOptions, FragileComfirmed, LoadDbOptions, PrepareOptions, SQLightError,
WorkerRequest, SQLiteStatementResult, WorkerRequest,
app::{ app::{
ImportProgress, ImportProgress,
advanced_options_menu::AdvancedOptionsMenu, advanced_options_menu::AdvancedOptionsMenu,
@@ -358,6 +359,44 @@ fn ShareButton() -> impl IntoView {
return; return;
}; };
let mut offset_inserts = vec![];
for result in &*state.output().read() {
let mut table_s = Table::new();
match result {
SQLiteStatementResult::Finish => continue,
SQLiteStatementResult::Step(table) => {
let end = table.position[1];
if let Some(values) = &table.values {
table_s.add_row(Row::new(
values.columns.iter().map(|s| Cell::new(s)).collect(),
));
for row in &values.rows {
table_s.add_row(Row::new(row.iter().map(|s| Cell::new(s)).collect()));
}
let result = table_s
.to_string()
.lines()
.map(|x| format!("-- {x}"))
.collect::<Vec<String>>()
.join("\n");
offset_inserts.push((end, format!("\n-- Output:\n{result}\n")));
}
}
}
}
let mut sql_with_result = code.clone();
for (idx, result) in offset_inserts.into_iter().rev() {
sql_with_result.insert_str(idx, &result);
}
state.share_sql_with_result().set(Some(sql_with_result));
if let Ok(href) = window().location().href().and_then(|href| { if let Ok(href) = window().location().href().and_then(|href| {
let url = Url::new(&href)?; let url = Url::new(&href)?;
let params = UrlSearchParams::new()?; let params = UrlSearchParams::new()?;
@@ -366,8 +405,9 @@ fn ShareButton() -> impl IntoView {
Ok(url.href()) Ok(url.href())
}) { }) {
state.share_href().set(Some(href)); state.share_href().set(Some(href));
change_focus(state, Some(Focus::Share));
} }
change_focus(state, Some(Focus::Share));
}; };
view! { <Button on_click=click>"Share"</Button> } view! { <Button on_click=click>"Share"</Button> }

View File

@@ -3,27 +3,29 @@ use std::{sync::Arc, time::Duration};
use istyles::istyles; use istyles::istyles;
use leptos::prelude::*; use leptos::prelude::*;
use reactive_stores::Store; use reactive_stores::Store;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::{JsFuture, spawn_local}; use wasm_bindgen_futures::{JsFuture, spawn_local};
use web_sys::{Blob, BlobPropertyBag, Url};
use crate::app::{GlobalState, GlobalStateStoreFields, icon::clipboard_icon}; use crate::app::{GlobalState, GlobalStateStoreFields, icon::clipboard_icon};
istyles!(styles, "assets/module.postcss/output/share.module.css.map"); istyles!(styles, "assets/module.postcss/output/share.module.css.map");
#[component] #[component]
fn Copied<H>(href: H, children: Children) -> impl IntoView fn Copied<S, H>(shared: S, href: H, children: Children) -> impl IntoView
where where
S: Fn() -> String + Send + Sync + 'static,
H: Fn() -> String + Send + Sync + 'static, H: Fn() -> String + Send + Sync + 'static,
{ {
let (copied, set_copied) = signal(false); let (copied, set_copied) = signal(false);
let href = Arc::new(href); let shared = Arc::new(shared);
let href1 = Arc::clone(&href);
let copy = move |_| { let copy = move |_| {
let href = Arc::clone(&href1); let shared = Arc::clone(&shared);
spawn_local(async move { spawn_local(async move {
set_copied.set(true); set_copied.set(true);
if let Err(err) = if let Err(err) =
JsFuture::from(window().navigator().clipboard().write_text(&href())).await JsFuture::from(window().navigator().clipboard().write_text(&shared())).await
{ {
log::error!("Failed to write href to clipboard: {err:?}"); log::error!("Failed to write href to clipboard: {err:?}");
} }
@@ -33,7 +35,9 @@ where
view! { view! {
<p class=move || { if *copied.read() { styles::active } else { styles::container } }> <p class=move || { if *copied.read() { styles::active } else { styles::container } }>
<a href=move || href()>{children()}</a> <a href=href target="_blank">
{children()}
</a>
<button class=styles::button on:click=copy> <button class=styles::button on:click=copy>
{clipboard_icon()} {clipboard_icon()}
</button> </button>
@@ -43,13 +47,66 @@ where
} }
#[component] #[component]
fn Links() -> impl IntoView { fn EmbeddedLinks() -> impl IntoView {
let state = expect_context::<Store<GlobalState>>(); let state = expect_context::<Store<GlobalState>>();
let code_url = move || state.share_href().get_untracked().unwrap_or_default(); let shared = move || state.share_href().get_untracked().unwrap_or_default();
view! { <Copied href=code_url>Embedded code in link</Copied> } let href = move || state.share_href().get_untracked().unwrap_or_default();
view! {
<Copied shared=shared href=href>
"Embedded code in link"
</Copied>
}
}
#[component]
fn SQLWithResultLinks() -> impl IntoView {
let state = expect_context::<Store<GlobalState>>();
let shared = move || {
state
.share_sql_with_result()
.get_untracked()
.unwrap_or_default()
};
let href = move || {
let text = state
.share_sql_with_result()
.get_untracked()
.unwrap_or_default();
let string_array = js_sys::Array::new();
string_array.push(&JsValue::from(text));
let blob_properties = BlobPropertyBag::new();
blob_properties.set_type("text/plain");
let blob =
Blob::new_with_str_sequence_and_options(&string_array, &blob_properties).unwrap();
let url = Url::create_object_url_with_blob(&blob).unwrap();
let url1 = url.clone();
set_timeout(
move || Url::revoke_object_url(&url1).unwrap(),
Duration::from_millis(5000),
);
url
};
view! {
<Copied shared=shared href=href>
"[Need run first] Copy sql with result"
</Copied>
}
} }
#[component] #[component]
pub fn Share() -> impl IntoView { pub fn Share() -> impl IntoView {
view! { <Links /> } view! {
<>
<EmbeddedLinks />
<SQLWithResultLinks />
</>
}
} }

View File

@@ -48,6 +48,8 @@ pub struct GlobalState {
#[serde(skip)] #[serde(skip)]
share_href: Option<String>, share_href: Option<String>,
#[serde(skip)] #[serde(skip)]
share_sql_with_result: Option<String>,
#[serde(skip)]
show_something: bool, show_something: bool,
#[serde(skip)] #[serde(skip)]
output: Vec<SQLiteStatementResult>, output: Vec<SQLiteStatementResult>,
@@ -75,6 +77,7 @@ impl Default for GlobalState {
is_focused: false, is_focused: false,
opened_focus: HashSet::new(), opened_focus: HashSet::new(),
share_href: None, share_href: None,
share_sql_with_result: None,
show_something: false, show_something: false,
output: Vec::new(), output: Vec::new(),
last_error: None, last_error: None,