diff --git a/copypasta/src/lib.rs b/copypasta/src/lib.rs index 9fe354f6..b498e2e9 100644 --- a/copypasta/src/lib.rs +++ b/copypasta/src/lib.rs @@ -32,10 +32,12 @@ pub trait Load : Sized { /// order to load the contents from other applications. pub trait Store : Load { /// Sets the primary clipboard contents - fn store_primary(&mut self, contents: String) -> Result<(), Self::Err>; + fn store_primary(&mut self, contents: S) -> Result<(), Self::Err> + where S: Into; /// Sets the secondary clipboard contents - fn store_selection(&mut self, contents: String) -> Result<(), Self::Err>; + fn store_selection(&mut self, contents: S) -> Result<(), Self::Err> + where S: Into; } #[cfg(target_os = "linux")] diff --git a/copypasta/src/macos.rs b/copypasta/src/macos.rs index 1b133b92..1942ac43 100644 --- a/copypasta/src/macos.rs +++ b/copypasta/src/macos.rs @@ -34,6 +34,10 @@ mod ns { ReadObjectsForClasses, } + /// Errors from writing strings to the pasteboard + #[derive(Debug)] + pub struct WriteStringError; + /// A trait for reading contents from the pasteboard /// /// This is intended to reflect the underlying objective C API @@ -43,6 +47,12 @@ mod ns { fn read_object(&self) -> Result; } + /// A trait for writing contents to the pasteboard + pub trait PasteboardWriteObject { + type Err; + fn write_object(&mut self, T) -> Result<(), Self::Err>; + } + impl PasteboardReadObject for Pasteboard { type Err = ReadStringError; fn read_object(&self) -> Result { @@ -113,6 +123,40 @@ mod ns { } } + impl PasteboardWriteObject for Pasteboard { + type Err = WriteStringError; + + fn write_object(&mut self, object: String) -> Result<(), Self::Err> { + let objects = NSArray::from_vec(vec![NSString::from_str(&object)]); + + self.clear_contents(); + + // The writeObjects method returns true in case of success, and + // false otherwise. + let ok: bool = unsafe { + msg_send![self.0, writeObjects:objects] + }; + + if ok { + Ok(()) + } else { + Err(WriteStringError) + } + } + } + + impl ::std::error::Error for WriteStringError { + fn description(&self) -> &str { + "Failed writing string to the NSPasteboard (writeContents returned false)" + } + } + + impl ::std::fmt::Display for WriteStringError { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.write_str(::std::error::Error::description(self)) + } + } + impl ::std::error::Error for ReadStringError { fn description(&self) -> &str { match *self { @@ -171,6 +215,17 @@ mod ns { Ok(Pasteboard(id)) } + + /// Clears the existing contents of the pasteboard, preparing it for new + /// contents. + /// + /// This is the first step in providing data on the pasteboard. The + /// return value is the change count of the pasteboard + pub fn clear_contents(&mut self) -> usize { + unsafe { + msg_send![self.0, clearContents] + } + } } } @@ -178,6 +233,7 @@ mod ns { pub enum Error { CreatePasteboard(ns::NewPasteboardError), ReadString(ns::ReadStringError), + WriteString(ns::WriteStringError), } @@ -186,6 +242,7 @@ impl ::std::error::Error for Error { match *self { Error::CreatePasteboard(ref err) => Some(err), Error::ReadString(ref err) => Some(err), + Error::WriteString(ref err) => Some(err), } } @@ -193,6 +250,7 @@ impl ::std::error::Error for Error { match *self { Error::CreatePasteboard(ref _err) => "Failed to create pasteboard", Error::ReadString(ref _err) => "Failed to read string from pasteboard", + Error::WriteString(ref _err) => "Failed to write string to pasteboard", } } } @@ -206,6 +264,9 @@ impl ::std::fmt::Display for Error { Error::ReadString(ref err) => { write!(f, "Failed to read string from pasteboard: {}", err) }, + Error::WriteString(ref err) => { + write!(f, "Failed to write string to pasteboard: {}", err) + }, } } } @@ -222,6 +283,12 @@ impl From for Error { } } +impl From for Error { + fn from(val: ns::WriteStringError) -> Error { + Error::WriteString(val) + } +} + pub struct Clipboard(ns::Pasteboard); impl super::Load for Clipboard { @@ -232,19 +299,41 @@ impl super::Load for Clipboard { } fn load_primary(&self) -> Result { - self::ns::PasteboardReadObject::::read_object(&self.0) + use self::ns::PasteboardReadObject; + + self.0.read_object() .map_err(::std::convert::From::from) } } +impl super::Store for Clipboard { + fn store_primary(&mut self, contents: S) -> Result<(), Self::Err> + where S: Into + { + use self::ns::PasteboardWriteObject; + + self.0.write_object(contents.into()) + .map_err(::std::convert::From::from) + } + + fn store_selection(&mut self, _contents: S) -> Result<(), Self::Err> + where S: Into + { + // No such thing on macOS + Ok(()) + } +} + #[cfg(test)] mod tests { use super::Clipboard; - use ::Load; + use ::{Load, Store}; #[test] - fn create_clipboard_and_load_contents() { - let clipboard = Clipboard::new().unwrap(); - println!("{:?}", clipboard.load_primary()); + fn create_clipboard_save_load_contents() { + let mut clipboard = Clipboard::new().unwrap(); + clipboard.store_primary("arst").unwrap(); + let loaded = clipboard.load_primary().unwrap(); + assert_eq!("arst", loaded); } }