From 9985ebd8558c2557adb6097f2abc2fbc39e88a12 Mon Sep 17 00:00:00 2001 From: ClSlaid Date: Mon, 23 Oct 2023 01:08:42 +0800 Subject: [PATCH 1/5] feat: add set files util for pasteboard binding Signed-off-by: ClSlaid --- src/pasteboard/mod.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/pasteboard/mod.rs b/src/pasteboard/mod.rs index 9765811b..4338fc26 100644 --- a/src/pasteboard/mod.rs +++ b/src/pasteboard/mod.rs @@ -111,4 +111,31 @@ impl Pasteboard { Ok(urls) } } + + /// Write a list of path to the pasteboard + pub fn set_files(&self, paths: &[&Path]) -> Result<(), Box> { + unsafe { + let urls: Vec = paths + .iter() + .map(|p| { + let url = format!("file://{}", p.display()); + NSURL::with_str(&url) + }) + .collect(); + let url_arr = NSArray::new(&urls); + + let _: id = msg_send![&*self.0, clearContents]; + let succ: bool = msg_send![&*self.0, writeObjects: &*url_arr]; + + if succ { + Ok(()) + } else { + return Err(Box::new(Error { + code: 666, + domain: "com.cacao-rs.pasteboard".to_string(), + description: "Pasteboard server set urls fail.".to_string() + })); + } + } + } } From 48dc4ef44fb187daf77dbaeb02126340558b06d2 Mon Sep 17 00:00:00 2001 From: ClSlaid Date: Wed, 25 Oct 2023 14:04:23 +0800 Subject: [PATCH 2/5] patch: add clipboard paste file test Signed-off-by: ClSlaid --- src/pasteboard/mod.rs | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/pasteboard/mod.rs b/src/pasteboard/mod.rs index 4338fc26..e22a575c 100644 --- a/src/pasteboard/mod.rs +++ b/src/pasteboard/mod.rs @@ -11,6 +11,9 @@ //! //! // Copy a piece of text to the clipboard //! pasteboard.copy_text("My message here"); +//! +//! // Set file url to the clipboard +//! pasteboard.set_files(vec!["/bin/ls".parse().unwrap(), "/bin/cat".parse().unwrap()]).unwrap(); //! ``` use std::path::PathBuf; @@ -113,16 +116,17 @@ impl Pasteboard { } /// Write a list of path to the pasteboard - pub fn set_files(&self, paths: &[&Path]) -> Result<(), Box> { + pub fn set_files(&self, mut paths: Vec) -> Result<(), Box> { unsafe { - let urls: Vec = paths - .iter() - .map(|p| { - let url = format!("file://{}", p.display()); - NSURL::with_str(&url) + let url_arr: NSArray = paths + .iter_mut() + .map(|path| { + let url_str = format!("file://{}", path.display()); + let url = NSURL::with_str(&url_str); + url.objc.autorelease_return() }) - .collect(); - let url_arr = NSArray::new(&urls); + .collect::>() + .into(); let _: id = msg_send![&*self.0, clearContents]; let succ: bool = msg_send![&*self.0, writeObjects: &*url_arr]; @@ -130,12 +134,31 @@ impl Pasteboard { if succ { Ok(()) } else { - return Err(Box::new(Error { + Err(Box::new(Error { code: 666, domain: "com.cacao-rs.pasteboard".to_string(), description: "Pasteboard server set urls fail.".to_string() - })); + })) } } } } + +#[cfg(test)] +mod pasteboard_test { + use std::path::PathBuf; + + use super::Pasteboard; + + #[test] + fn paste_files() { + let pb = Pasteboard::unique(); + let paths: Vec = vec!["/bin/ls", "/bin/cat"].into_iter().map(|s| s.parse().unwrap()).collect(); + + pb.set_files(paths.clone()).unwrap(); + let urls = pb.get_file_urls().unwrap(); + let got: Vec = urls.into_iter().map(|u| u.pathbuf()).collect(); + assert_eq!(got, paths); + println!("successful!"); + } +} From f4940678214705214be9e0e800d566651c32fd20 Mon Sep 17 00:00:00 2001 From: ClSlaid Date: Wed, 25 Oct 2023 14:11:52 +0800 Subject: [PATCH 3/5] patch: remove verbose test message Signed-off-by: ClSlaid --- src/pasteboard/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pasteboard/mod.rs b/src/pasteboard/mod.rs index e22a575c..55d60679 100644 --- a/src/pasteboard/mod.rs +++ b/src/pasteboard/mod.rs @@ -159,6 +159,5 @@ mod pasteboard_test { let urls = pb.get_file_urls().unwrap(); let got: Vec = urls.into_iter().map(|u| u.pathbuf()).collect(); assert_eq!(got, paths); - println!("successful!"); } } From 5e34b6455207504e6f557b892f28e105f612a7c1 Mon Sep 17 00:00:00 2001 From: ClSlaid Date: Sun, 29 Oct 2023 01:50:03 +0800 Subject: [PATCH 4/5] feat: add set text function Signed-off-by: ClSlaid --- src/pasteboard/mod.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/pasteboard/mod.rs b/src/pasteboard/mod.rs index 55d60679..491ebf7d 100644 --- a/src/pasteboard/mod.rs +++ b/src/pasteboard/mod.rs @@ -11,9 +11,6 @@ //! //! // Copy a piece of text to the clipboard //! pasteboard.copy_text("My message here"); -//! -//! // Set file url to the clipboard -//! pasteboard.set_files(vec!["/bin/ls".parse().unwrap(), "/bin/cat".parse().unwrap()]).unwrap(); //! ``` use std::path::PathBuf; @@ -115,6 +112,23 @@ impl Pasteboard { } } + pub fn get_text(&self) -> Result> { + unsafe { + let pb_type: NSString = NSString::from(PasteboardType::String); + let opt_nsstr: id = msg_send![&*self.0, stringForType: &*pb_type]; + + if opt_nsstr == nil { + return Err(Box::new(Error { + code: 666, + domain: "com.cacao-rs.pasteboard".to_string(), + description: "Pasteboard server returned no data.".to_string() + })); + } + + Ok(NSString::retain(opt_nsstr).to_string()) + } + } + /// Write a list of path to the pasteboard pub fn set_files(&self, mut paths: Vec) -> Result<(), Box> { unsafe { @@ -150,6 +164,16 @@ mod pasteboard_test { use super::Pasteboard; + #[test] + fn paste_text() { + let pb = Pasteboard::unique(); + let txt = "hello, world!"; + + pb.copy_text(txt); + let txt_got = pb.get_text().unwrap(); + assert_eq!(txt, txt_got); + } + #[test] fn paste_files() { let pb = Pasteboard::unique(); From 05e1536b0b43aaae308ec72c0eed703e875b7b95 Mon Sep 17 00:00:00 2001 From: ClSlaid Date: Sun, 29 Oct 2023 06:14:15 +0800 Subject: [PATCH 5/5] patch: fix file url paste should url encoded Signed-off-by: ClSlaid --- Cargo.toml | 1 + src/pasteboard/mod.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 639e96c1..0388c6cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ libc = "0.2" os_info = "3.0.1" url = "2.1.1" uuid = { version = "1.1", features = ["v4"], optional = true } +percent-encoding = "2.3.0" [dev-dependencies] eval = "0.4" diff --git a/src/pasteboard/mod.rs b/src/pasteboard/mod.rs index 491ebf7d..a7ab472f 100644 --- a/src/pasteboard/mod.rs +++ b/src/pasteboard/mod.rs @@ -18,6 +18,7 @@ use std::path::PathBuf; use objc::rc::{Id, Shared}; use objc::runtime::Object; use objc::{class, msg_send, msg_send_id, sel}; +use percent_encoding::{percent_encode, AsciiSet}; use url::Url; use crate::error::Error; @@ -26,6 +27,8 @@ use crate::foundation::{id, nil, NSArray, NSString, NSURL}; mod types; pub use types::{PasteboardName, PasteboardType}; +const ENCODE_SET: AsciiSet = percent_encoding::CONTROLS.add(b' ').add(b'-').add(b'%'); + /// Represents an `NSPasteboard`, enabling you to handle copy/paste/drag and drop. #[derive(Debug)] pub struct Pasteboard(pub Id); @@ -106,7 +109,7 @@ impl Pasteboard { })); } - let urls = NSArray::retain(contents).iter().map(|url| NSURL::retain(url)).collect(); + let urls = NSArray::retain(contents).iter().map(NSURL::retain).collect(); Ok(urls) } @@ -135,7 +138,9 @@ impl Pasteboard { let url_arr: NSArray = paths .iter_mut() .map(|path| { - let url_str = format!("file://{}", path.display()); + let path = path.display().to_string(); + let encoded = percent_encode(path.as_bytes(), &ENCODE_SET).to_string(); + let url_str = format!("file://{}", encoded); let url = NSURL::with_str(&url_str); url.objc.autorelease_return() }) @@ -177,7 +182,10 @@ mod pasteboard_test { #[test] fn paste_files() { let pb = Pasteboard::unique(); - let paths: Vec = vec!["/bin/ls", "/bin/cat"].into_iter().map(|s| s.parse().unwrap()).collect(); + let paths: Vec = vec!["/bin/ls", "/bin/cat", "/tmp/👋- 2023 10 29.txt"] + .into_iter() + .map(|s| s.parse().unwrap()) + .collect(); pb.set_files(paths.clone()).unwrap(); let urls = pb.get_file_urls().unwrap();