Procházet zdrojové kódy

Fix clipboard for Linux, use xclip on this platform

timvisee před 7 roky
rodič
revize
f11eabf868
6 změnil soubory, kde provedl 79 přidání a 16 odebrání
  1. 0 1
      Cargo.lock
  2. 0 2
      ROADMAP.md
  3. 3 2
      cli/Cargo.toml
  4. 4 2
      cli/src/action/upload.rs
  5. 1 2
      cli/src/main.rs
  6. 71 7
      cli/src/util.rs

+ 0 - 1
Cargo.lock

@@ -358,7 +358,6 @@ dependencies = [
  "derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "directories 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ffsend-api 0.0.1",
  "fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",

+ 0 - 2
ROADMAP.md

@@ -3,8 +3,6 @@ The first release used for gathering feedback on the application by selected
 people.
 
 Features:
-- Allow unarchiving on download 
-- Use clipboard through `xclip` on Linux if available for persistence
 - Write complete README
 - Polish command outputs, make it consistent (format, color)
 - Automated releases through CI

+ 3 - 2
cli/Cargo.toml

@@ -27,12 +27,10 @@ no-color = ["colored/no-color"]
 [dependencies]
 chrono = "0.4"
 clap = "2.31"
-clipboard = { version = "0.4", optional = true }
 colored = "1.6"
 derive_builder = "0.5"
 directories = "0.10"
 failure = "0.1"
-failure_derive = "0.1"
 ffsend-api = { version = "*", path = "../api" }
 fs2 = "0.4"
 lazy_static = "1.0"
@@ -46,3 +44,6 @@ tar = { version = "0.4", optional = true }
 tempfile = "3"
 toml = "0.4"
 version-compare = "0.0.6"
+
+[target.'cfg(not(target_os = "linux"))'.dependencies]
+clipboard = { version = "0.4", optional = true }

+ 4 - 2
cli/src/action/upload.rs

@@ -248,8 +248,10 @@ impl<'a> Upload<'a> {
 
         // Copy the URL in the user's clipboard
         #[cfg(feature = "clipboard")] {
-            if matcher_upload.copy() && set_clipboard(url.as_str().to_owned()).is_err() {
-                print_error_msg("failed to copy the URL to the clipboard");
+            if matcher_upload.copy() {
+                if let Err(err) = set_clipboard(url.as_str().to_owned()) {
+                    print_error(err.context("failed to copy the URL to the clipboard, ignoring"));
+                }
             }
         }
 

+ 1 - 2
cli/src/main.rs

@@ -3,9 +3,8 @@ extern crate chrono;
 extern crate clap;
 #[macro_use]
 extern crate derive_builder;
-extern crate failure;
 #[macro_use]
-extern crate failure_derive;
+extern crate failure;
 extern crate ffsend_api;
 #[cfg(feature = "history")]
 #[macro_use]

+ 71 - 7
cli/src/util.rs

@@ -1,4 +1,4 @@
-#[cfg(feature = "clipboard")]
+#[cfg(all(feature = "clipboard", not(target_os = "linux")))]
 extern crate clipboard;
 extern crate colored;
 extern crate directories;
@@ -7,8 +7,6 @@ extern crate open;
 
 use std::borrow::Borrow;
 use std::env::{current_exe, var_os};
-#[cfg(feature = "clipboard")]
-use std::error::Error as StdError;
 use std::ffi::OsStr;
 use std::fmt::{Debug, Display};
 use std::io::{
@@ -17,16 +15,22 @@ use std::io::{
     stderr,
     Write,
 };
+#[cfg(feature = "clipboard")]
+use std::io::ErrorKind as IoErrorKind;
 use std::path::Path;
 #[cfg(feature = "history")]
 use std::path::PathBuf;
 use std::process::{exit, ExitStatus};
+#[cfg(all(feature = "clipboard", target_os = "linux"))]
+use std::process::{Command, Stdio};
 
 use chrono::Duration;
 use failure::{err_msg, Fail};
+#[cfg(all(feature = "clipboard", not(target_os = "linux")))]
+use failure::{Compat, Error};
 use ffsend_api::url::Url;
 use rpassword::prompt_password_stderr;
-#[cfg(feature = "clipboard")]
+#[cfg(all(feature = "clipboard", not(target_os = "linux")))]
 use self::clipboard::{ClipboardContext, ClipboardProvider};
 use self::colored::*;
 #[cfg(feature = "history")]
@@ -262,9 +266,69 @@ pub fn open_path(path: &str) -> Result<ExitStatus, IoError> {
 
 /// Set the clipboard of the user to the given `content` string.
 #[cfg(feature = "clipboard")]
-pub fn set_clipboard(content: String) -> Result<(), Box<StdError>> {
-    let mut context: ClipboardContext = ClipboardProvider::new()?;
-    context.set_contents(content)
+pub fn set_clipboard(content: String) -> Result<(), ClipboardError> {
+    #[cfg(not(target_os = "linux"))] {
+        ClipboardProvider::new()
+            .and_then(|mut context: ClipboardContext| context.set_contents(content))
+            .map_err(|err| format_err!("{}", err).compat())
+            .map_err(ClipboardError::Generic)
+    }
+
+    #[cfg(target_os = "linux")] {
+        // Open an xclip process
+        let mut process = match Command::new("xclip")
+            .arg("-sel")
+            .arg("clip")
+            .stdin(Stdio::piped())
+            .spawn()
+        {
+            Ok(process) => process,
+            Err(err) => return Err(match err.kind() {
+                IoErrorKind::NotFound => ClipboardError::NoXclip,
+                _ => ClipboardError::Xclip(err),
+            }),
+        };
+
+        // Write the contents to the xclip process
+        process.stdin.as_mut().unwrap()
+            .write_all(content.as_bytes())
+            .map_err(ClipboardError::Xclip)?;
+
+        // Wait for xclip to exit
+        let status = process.wait()
+            .map_err(ClipboardError::Xclip)?;
+        if !status.success() {
+            return Err(ClipboardError::XclipStatus(status.code().unwrap_or(0)));
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(feature = "clipboard")]
+#[derive(Debug, Fail)]
+pub enum ClipboardError {
+    /// A generic error occurred while setting the clipboard contents
+    #[cfg(not(target_os = "linux"))]
+    #[fail(display = "failed to access clipboard")]
+    Generic(#[cause] Compat<Error>),
+
+    /// Xclip is not installed on the system, which is required for clipboard support.
+    #[cfg(target_os = "linux")]
+    #[fail(display = "failed to access clipboard, xclip is not installed")]
+    NoXclip,
+
+    /// An error occurred while using xclip to set the clipboard contents.
+    /// This problem probably occurred when stargin the xclip process, or while piping the
+    /// clipboard contents to the process.
+    #[cfg(target_os = "linux")]
+    #[fail(display = "failed to access clipboard using xclip")]
+    Xclip(#[cause] IoError),
+
+    /// Xclip unexpectetly exited with a non-successful status code.
+    #[cfg(target_os = "linux")]
+    #[fail(display = "failed to use clipboard, xclip exited with status code {}", _0)]
+    XclipStatus(i32),
 }
 
 /// Check for an emtpy password in the given `password`.