From 23894697d9921cd403133acf98d423aa89144efe Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Mon, 11 Feb 2019 17:04:44 -0500 Subject: [PATCH] Switch from Box to a custom enum, adapted from @fschutt's branch (c.f. issue #55). --- src/common.rs | 13 ++-- src/error.rs | 125 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/nop_clipboard.rs | 12 ++-- src/osx_clipboard.rs | 18 +++--- src/windows_clipboard.rs | 8 +-- src/x11_clipboard.rs | 8 +-- 7 files changed, 154 insertions(+), 32 deletions(-) create mode 100644 src/error.rs diff --git a/src/common.rs b/src/common.rs index 92f80d5..83e2d29 100644 --- a/src/common.rs +++ b/src/common.rs @@ -14,21 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -use std::error::Error; - -pub fn err(s: &str) -> Box { - Box::::from(s) -} +use error::ClipboardError; /// Trait for clipboard access pub trait ClipboardProvider: Sized { /// Create a context with which to access the clipboard - // TODO: consider replacing Box with an associated type? - fn new() -> Result>; + fn new() -> Result; /// Method to get the clipboard contents as a String - fn get_contents(&mut self) -> Result>; + fn get_contents(&mut self) -> Result; /// Method to set the clipboard contents as a String - fn set_contents(&mut self, String) -> Result<(), Box>; + fn set_contents(&mut self, String) -> Result<(), ClipboardError>; // TODO: come up with some platform-agnostic API for richer types // than just strings (c.f. issue #31) } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..0b2f1d4 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,125 @@ +use std::fmt::{self, Display, Formatter}; + +#[cfg(target_os="linux")] +use x11_clipboard_crate::error::Error as X11Error; + +use std::string::FromUtf8Error; +use std::error::Error; +use std::io::Error as IoError; + +#[derive(Debug)] +pub enum ClipboardError { + Unimplemented, + IoError(IoError), + EncodingError(FromUtf8Error), + #[cfg(target_os = "linux")] + X11ClipboardError(X11Error), + #[cfg(target_os = "macos")] + MacOsClipboardError(MacOsError), + #[cfg(target_os = "windows")] + WindowsClipboardError(WinError), +} + +#[cfg(target_os="windows")] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum WinError { + EmptyClipboard, + FormatNoSize, +} + +#[cfg(target_os = "windows")] +impl Display for WinError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +#[cfg(target_os = "windows")] +impl Error for WinError { + fn description(&self) -> &str { + use self::WinError::*; + match *self { + EmptyClipboard => "Empty clipboard or couldn't determine format of clipboard contents", + FormatNoSize => "Could not determine the length of the clipboard contents" + } + } + + fn cause(&self) -> Option<&Error> { + None + } +} + +impl From for ClipboardError { + fn from(e: IoError) -> Self { + ClipboardError::IoError(e) + } +} + +#[cfg(target_os="windows")] +impl From for ClipboardError { + fn from(e: WinError) -> Self { + ClipboardError::WindowsClipboardError(e) + } +} + +#[cfg(target_os="linux")] +impl From for ClipboardError { + fn from(e: X11Error) -> Self { + ClipboardError::X11ClipboardError(e) + } +} + +#[cfg(target_os="macos")] +impl From for ClipboardError { + fn from(e: MacOsError) -> Self { + ClipboardError::MacOsClipboardError(e) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg(target_os = "macos")] +pub enum MacOsError { + PasteWriteObjectsError, + ReadObjectsForClassesEmpty, + ReadObjectsForClassesNull, + PasteboardNotFound, + GeneralPasteboardNotFound, +} + +#[cfg(target_os = "macos")] +impl Display for MacOsError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + use self::MacOsError::*; + let msg = match *self { + PasteWriteObjectsError => "Could not paste objects to clipboard", + ReadObjectsForClassesEmpty => "Clipboard is empty", + ReadObjectsForClassesNull => "No objects to read", + PasteboardNotFound => "NSPasteboard class not found", + GeneralPasteboardNotFound => "NSPasteboard#generalPasteboard not found", + }; + write!(f, "{}", msg) + } +} + +impl Display for ClipboardError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + use self::ClipboardError::*; + match self { + Unimplemented => write!(f, "Clipboard::Unimplemented: Attempted to get or set the clipboard, which hasn't been implemented yet."), + IoError(ref e) => write!(f, "Clipboard::IoError: {} cause: {:?}", e.description(), e.cause()), + EncodingError(ref e) => write!(f, "Clipboard::EncodingError: {} cause: {:?}", e.description(), e.cause()), + #[cfg(target_os="linux")] + X11ClipboardError(ref e) => write!(f, "X11ClipboardError: {}", e), + #[cfg(target_os="macos")] + MacOsClipboardError(ref e) => write!(f, "MacOsClipboardError: {}", e), + #[cfg(target_os="windows")] + WindowsClipboardError(ref e) => write!(f, "WindowsClipboardError: {} cause: {:?}", e.description(), e.cause()), + } + } +} + +impl From for ClipboardError { + fn from(e: FromUtf8Error) -> Self { + ClipboardError::EncodingError(e) + } +} diff --git a/src/lib.rs b/src/lib.rs index de6169b..94c3065 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,8 @@ extern crate objc_foundation; mod common; pub use common::ClipboardProvider; +pub mod error; + #[cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten"))))] pub mod x11_clipboard; diff --git a/src/nop_clipboard.rs b/src/nop_clipboard.rs index 75a2913..70ddb72 100644 --- a/src/nop_clipboard.rs +++ b/src/nop_clipboard.rs @@ -15,22 +15,22 @@ limitations under the License. */ use common::ClipboardProvider; -use std::error::Error; +use error::ClipboardError; pub struct NopClipboardContext; impl ClipboardProvider for NopClipboardContext { - fn new() -> Result> { + fn new() -> Result { Ok(NopClipboardContext) } - fn get_contents(&mut self) -> Result> { + fn get_contents(&mut self) -> Result { println!("Attempting to get the contents of the clipboard, which hasn't yet been \ implemented on this platform."); - Ok("".to_string()) + Err(ClipboardError::Unimplemented) } - fn set_contents(&mut self, _: String) -> Result<(), Box> { + fn set_contents(&mut self, _: String) -> Result<(), ClipboardError> { println!("Attempting to set the contents of the clipboard, which hasn't yet been \ implemented on this platform."); - Ok(()) + Err(ClipboardError::Unimplemented) } } diff --git a/src/osx_clipboard.rs b/src/osx_clipboard.rs index cf8c0b3..305081b 100644 --- a/src/osx_clipboard.rs +++ b/src/osx_clipboard.rs @@ -19,7 +19,7 @@ use objc::runtime::{Object, Class}; use objc_foundation::{INSArray, INSString, INSObject}; use objc_foundation::{NSArray, NSDictionary, NSString, NSObject}; use objc_id::{Id, Owned}; -use std::error::Error; +use error::{MacOsError, ClipboardError}; use std::mem::transmute; pub struct OSXClipboardContext { @@ -31,16 +31,16 @@ pub struct OSXClipboardContext { extern "C" {} impl ClipboardProvider for OSXClipboardContext { - fn new() -> Result> { - let cls = try!(Class::get("NSPasteboard").ok_or(err("Class::get(\"NSPasteboard\")"))); + fn new() -> Result { + let cls = try!(Class::get("NSPasteboard").ok_or(MacOsError::PasteboardNotFound.into())); let pasteboard: *mut Object = unsafe { msg_send![cls, generalPasteboard] }; if pasteboard.is_null() { - return Err(err("NSPasteboard#generalPasteboard returned null")); + return Err(MacOsError::GeneralPasteboardNotFound.into()); } let pasteboard: Id = unsafe { Id::from_ptr(pasteboard) }; Ok(OSXClipboardContext { pasteboard: pasteboard }) } - fn get_contents(&mut self) -> Result> { + fn get_contents(&mut self) -> Result { let string_class: Id = { let cls: Id = unsafe { Id::from_ptr(class("NSString")) }; unsafe { transmute(cls) } @@ -51,24 +51,24 @@ impl ClipboardProvider for OSXClipboardContext { let obj: *mut NSArray = msg_send![self.pasteboard, readObjectsForClasses:&*classes options:&*options]; if obj.is_null() { - return Err(err("pasteboard#readObjectsForClasses:options: returned null")); + return Err(MacOsError::ReadObjectsForClassesNull.into()); } Id::from_ptr(obj) }; if string_array.count() == 0 { - Err(err("pasteboard#readObjectsForClasses:options: returned empty")) + Err(MacOsError::ReadObjectsForClassesEmpty.into()) } else { Ok(string_array[0].as_str().to_owned()) } } - fn set_contents(&mut self, data: String) -> Result<(), Box> { + fn set_contents(&mut self, data: String) -> Result<(), ClipboardError> { let string_array = NSArray::from_vec(vec![NSString::from_str(&data)]); let _: usize = unsafe { msg_send![self.pasteboard, clearContents] }; let success: bool = unsafe { msg_send![self.pasteboard, writeObjects:string_array] }; return if success { Ok(()) } else { - Err(err("NSPasteboard#writeObjects: returned false")) + Err(MacOsError::PasteWriteObjectsError.into()) }; } } diff --git a/src/windows_clipboard.rs b/src/windows_clipboard.rs index f534407..8075115 100644 --- a/src/windows_clipboard.rs +++ b/src/windows_clipboard.rs @@ -17,18 +17,18 @@ limitations under the License. use clipboard_win::{get_clipboard_string, set_clipboard_string}; use common::ClipboardProvider; -use std::error::Error; +use error::ClipboardError; pub struct WindowsClipboardContext; impl ClipboardProvider for WindowsClipboardContext { - fn new() -> Result> { + fn new() -> Result { Ok(WindowsClipboardContext) } - fn get_contents(&mut self) -> Result> { + fn get_contents(&mut self) -> Result { Ok(get_clipboard_string()?) } - fn set_contents(&mut self, data: String) -> Result<(), Box> { + fn set_contents(&mut self, data: String) -> Result<(), ClipboardError> { Ok(set_clipboard_string(&data)?) } } diff --git a/src/x11_clipboard.rs b/src/x11_clipboard.rs index 79d34aa..6e508ff 100644 --- a/src/x11_clipboard.rs +++ b/src/x11_clipboard.rs @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -use std::error::Error; +use error::ClipboardError; use std::time::Duration; use std::marker::PhantomData; use common::*; @@ -50,11 +50,11 @@ impl ClipboardProvider for X11ClipboardContext where S: Selection, { - fn new() -> Result, Box> { + fn new() -> Result, ClipboardError> { Ok(X11ClipboardContext(X11Clipboard::new()?, PhantomData)) } - fn get_contents(&mut self) -> Result> { + fn get_contents(&mut self) -> Result { Ok(String::from_utf8(self.0.load( S::atom(&self.0.getter.atoms), self.0.getter.atoms.utf8_string, @@ -63,7 +63,7 @@ where )?)?) } - fn set_contents(&mut self, data: String) -> Result<(), Box> { + fn set_contents(&mut self, data: String) -> Result<(), ClipboardError> { Ok(self.0.store( S::atom(&self.0.setter.atoms), self.0.setter.atoms.utf8_string,