use std::fmt;
pub trait Clipboard {
fn get_text(&mut self) -> Result<String, ClipboardError>;
fn set_text(&mut self, text: String) -> Result<(), ClipboardError>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClipboardError(String);
impl ClipboardError {
#[must_use]
pub fn new(message: impl Into<String>) -> Self {
Self(message.into())
}
}
impl fmt::Display for ClipboardError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "clipboard error: {}", self.0)
}
}
impl std::error::Error for ClipboardError {}
#[cfg(not(target_arch = "wasm32"))]
pub struct SystemClipboard(arboard::Clipboard);
#[cfg(not(target_arch = "wasm32"))]
impl fmt::Debug for SystemClipboard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SystemClipboard").finish_non_exhaustive()
}
}
#[cfg(not(target_arch = "wasm32"))]
impl SystemClipboard {
pub fn new() -> Result<Self, ClipboardError> {
arboard::Clipboard::new()
.map(Self)
.map_err(|e| ClipboardError::new(e.to_string()))
}
}
#[cfg(not(target_arch = "wasm32"))]
impl Clipboard for SystemClipboard {
fn get_text(&mut self) -> Result<String, ClipboardError> {
self.0
.get_text()
.map_err(|e| ClipboardError::new(e.to_string()))
}
fn set_text(&mut self, text: String) -> Result<(), ClipboardError> {
self.0
.set_text(text)
.map_err(|e| ClipboardError::new(e.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Default)]
struct FakeClipboard {
contents: Option<String>,
}
impl Clipboard for FakeClipboard {
fn get_text(&mut self) -> Result<String, ClipboardError> {
self.contents
.clone()
.ok_or_else(|| ClipboardError::new("clipboard is empty"))
}
fn set_text(&mut self, text: String) -> Result<(), ClipboardError> {
self.contents = Some(text);
Ok(())
}
}
#[test]
fn set_then_get_round_trips() {
let mut clip = FakeClipboard::default();
clip.set_text("hello".to_string()).unwrap();
assert_eq!(clip.get_text().unwrap(), "hello");
}
#[test]
fn get_before_any_set_is_an_error() {
let mut clip = FakeClipboard::default();
assert!(clip.get_text().is_err());
}
#[test]
fn set_overwrites_previous_contents() {
let mut clip = FakeClipboard::default();
clip.set_text("first".to_string()).unwrap();
clip.set_text("second".to_string()).unwrap();
assert_eq!(clip.get_text().unwrap(), "second");
}
#[test]
fn clipboard_error_display_includes_message() {
let err = ClipboardError::new("boom");
assert_eq!(err.to_string(), "clipboard error: boom");
}
#[test]
fn clipboard_error_equality() {
assert_eq!(ClipboardError::new("a"), ClipboardError::new("a"));
assert_ne!(ClipboardError::new("a"), ClipboardError::new("b"));
}
}