diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f0994e..5a5c15a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,10 +83,5 @@ jobs: with: path: target key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - name: Install dependencies - if: runner.os == 'Linux' - run: | - sudo apt-get install libxcb-xkb-dev - sudo apt install libxcb-composite0-dev - name: Run Tests run: cargo test --workspace -- --skip=e2e --color always diff --git a/Cargo.lock b/Cargo.lock index 4f71d39..90d2885 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,7 +220,6 @@ dependencies = [ "objc", "objc-foundation", "objc_id", - "x11-clipboard", ] [[package]] @@ -556,6 +555,7 @@ dependencies = [ "toml", "tui", "unicode-width", + "which", ] [[package]] @@ -1869,6 +1869,17 @@ dependencies = [ "webpki", ] +[[package]] +name = "which" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" +dependencies = [ + "either", + "lazy_static", + "libc", +] + [[package]] name = "whoami" version = "1.1.2" @@ -1929,25 +1940,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" -[[package]] -name = "x11-clipboard" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b397ace6e980510de59a4fe3d4c758dffab231d6d747ce9fa1aba6b6035d5f32" -dependencies = [ - "xcb", -] - -[[package]] -name = "xcb" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62056f63138b39116f82a540c983cc11f1c90cd70b3d492a70c25eaa50bd22a6" -dependencies = [ - "libc", - "log", -] - [[package]] name = "zeroize" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 693b45f..796ad1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,8 +36,5 @@ async-trait = "0.1.50" itertools = "0.10.0" rust_decimal = "1.15" -[target.'cfg(any(target_os = "macos", windows))'.dependencies] -copypasta = { version = "0.7.0", default-features = false } - -[target.'cfg(not(any(target_os = "macos", windows)))'.dependencies] -copypasta = { version = "0.7.0", features = ["x11"], default-features = false } +[target.'cfg(all(target_family="unix",not(target_os="macos")))'.dependencies] +which = "4.1" diff --git a/database-tree/src/databasetreeitems.rs b/database-tree/src/databasetreeitems.rs index 406163e..ca3a77f 100644 --- a/database-tree/src/databasetreeitems.rs +++ b/database-tree/src/databasetreeitems.rs @@ -31,7 +31,7 @@ impl DatabaseTreeItems { item.set_collapsed(false); item } else { - let mut item = item.clone(); + let mut item = item; item.show(); item } diff --git a/src/app.rs b/src/app.rs index 5e94820..28fab7e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,4 @@ -use crate::clipboard::Clipboard; +use crate::clipboard::copy_to_clipboard; use crate::components::{CommandInfo, Component as _, DrawableComponent as _, EventState}; use crate::database::{MySqlPool, Pool, PostgresPool, RECORDS_LIMIT_PER_PAGE}; use crate::event::Key; @@ -31,7 +31,6 @@ pub struct App { databases: DatabasesComponent, connections: ConnectionsComponent, table_status: TableStatusComponent, - clipboard: Clipboard, pool: Option>, pub config: Config, pub error: ErrorComponent, @@ -50,7 +49,6 @@ impl App { table_status: TableStatusComponent::default(), error: ErrorComponent::new(config.key_config), focus: Focus::ConnectionList, - clipboard: Clipboard::new(), pool: None, } } @@ -262,7 +260,7 @@ impl App { if key == self.config.key_config.copy { if let Some(text) = self.record_table.table.selected_cells() { - self.clipboard.store(text) + copy_to_clipboard(text.as_str())? } } @@ -312,7 +310,7 @@ impl App { if key == self.config.key_config.copy { if let Some(text) = self.structure_table.selected_cells() { - self.clipboard.store(text) + copy_to_clipboard(text.as_str())? } }; } diff --git a/src/clipboard.rs b/src/clipboard.rs index 7fafd12..e54f6c4 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -1,68 +1,68 @@ -use copypasta::ClipboardContext; -use copypasta::ClipboardProvider; +use anyhow::{anyhow, Result}; +#[cfg(all(target_family = "unix", not(target_os = "macos")))] +use std::ffi::OsStr; +use std::io::Write; +use std::process::{Command, Stdio}; -pub struct Clipboard { - clipboard: Box, - selection: Option>, -} +fn execute_copy_command(command: Command, text: &str) -> Result<()> { + let mut command = command; -impl Clipboard { - pub fn new() -> Self { - Self::default() - } + let mut process = command + .stdin(Stdio::piped()) + .stdout(Stdio::null()) + .spawn() + .map_err(|e| anyhow!("`{:?}`: {}", command, e))?; - // #[cfg(any(test, not(any(target_os = "macos", windows))))] - // pub fn new_nop() -> Self { - // Self { - // clipboard: Box::new(NopClipboardContext::new().unwrap()), - // selection: None, - // } - // } -} + process + .stdin + .as_mut() + .ok_or_else(|| anyhow!("`{:?}`", command))? + .write_all(text.as_bytes()) + .map_err(|e| anyhow!("`{:?}`: {}", command, e))?; -impl Default for Clipboard { - fn default() -> Self { - return Self { - clipboard: Box::new(ClipboardContext::new().unwrap()), - selection: None, - }; + process + .wait() + .map_err(|e| anyhow!("`{:?}`: {}", command, e))?; - // #[cfg(target_os = "linux")] - // return Self { - // clipboard: Box::new(ClipboardContext::new().unwrap()), - // selection: Some(Box::new( - // X11ClipboardContext::::new().unwrap(), - // )), - // }; + Ok(()) +} - // #[cfg(not(any(target_os = "macos", windows)))] - // return Self::new_nop(); +#[cfg(all(target_family = "unix", not(target_os = "macos")))] +fn gen_command(path: impl AsRef, xclip_syntax: bool) -> Command { + let mut c = Command::new(path); + if xclip_syntax { + c.arg("-selection"); + c.arg("clipboard"); + } else { + c.arg("--clipboard"); } + c } -impl Clipboard { - pub fn store(&mut self, text: impl Into) { - let clipboard = match &mut self.selection { - Some(provider) => provider, - None => &mut self.clipboard, - }; +#[cfg(all(target_family = "unix", not(target_os = "macos")))] +pub fn copy_to_clipboard(string: &str) -> Result<()> { + use std::path::PathBuf; + use which::which; + let (path, xclip_syntax) = which("xclip").ok().map_or_else( + || { + ( + which("xsel").ok().unwrap_or_else(|| PathBuf::from("xsel")), + false, + ) + }, + |path| (path, true), + ); - clipboard.set_contents(text.into()).unwrap_or_else(|err| { - panic!("Unable to store text in clipboard: {}", err); - }); - } + let cmd = gen_command(path, xclip_syntax); + execute_copy_command(cmd, string) +} - pub fn _load(&mut self) -> String { - let clipboard = match &mut self.selection { - Some(provider) => provider, - None => &mut self.clipboard, - }; +#[cfg(target_os = "macos")] +pub fn copy_to_clipboard(string: &str) -> Result<()> { + execute_copy_command(Command::new("pbcopy"), string) +} - match clipboard.get_contents() { - Err(err) => { - panic!("Unable to load text from clipboard: {}", err); - } - Ok(text) => text, - } - } +#[cfg(windows)] +pub fn copy_to_clipboard(string: &str) -> Result<()> { + execute_copy_command(Command::new("clip"), string) } diff --git a/src/database/postgres.rs b/src/database/postgres.rs index 7c2365a..9686b6f 100644 --- a/src/database/postgres.rs +++ b/src/database/postgres.rs @@ -265,7 +265,7 @@ fn convert_column_value_to_string(row: &PgRow, column: &PgColumn) -> anyhow::Res } if let Ok(value) = row.try_get(column_name) { let value: Option> = value; - return Ok(value.map_or("NULL".to_string(), |v| v.join(",").to_string())); + return Ok(value.map_or("NULL".to_string(), |v| v.join(","))); } Err(anyhow::anyhow!( "column type not implemented: `{}` {}",