use std::fmt;
pub trait Clipboard {
fn text(&mut self) -> Result<String, ClipboardError>;
fn set_text(&mut self, text: &str) -> Result<(), ClipboardError>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ClipboardError {
Empty,
Unavailable(String),
}
impl fmt::Display for ClipboardError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => f.write_str("the clipboard holds no text"),
Self::Unavailable(reason) => write!(f, "the clipboard is unavailable: {reason}"),
}
}
}
impl std::error::Error for ClipboardError {}
#[derive(Debug, Clone, Default)]
pub struct MemoryClipboard {
text: Option<String>,
}
impl Clipboard for MemoryClipboard {
fn text(&mut self) -> Result<String, ClipboardError> {
self.text.clone().ok_or(ClipboardError::Empty)
}
fn set_text(&mut self, text: &str) -> Result<(), ClipboardError> {
self.text = Some(text.to_owned());
Ok(())
}
}
pub(crate) struct Held(pub(crate) Box<dyn Clipboard>);
impl Default for Held {
fn default() -> Self {
Self(Box::new(MemoryClipboard::default()))
}
}
impl fmt::Debug for Held {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Clipboard")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_memory_clipboard_gives_back_the_last_text_put_on_it() {
let mut clipboard = MemoryClipboard::default();
assert_eq!(clipboard.text(), Err(ClipboardError::Empty));
clipboard.set_text("one").unwrap();
clipboard.set_text("two").unwrap();
assert_eq!(clipboard.text().unwrap(), "two");
assert_eq!(clipboard.text().unwrap(), "two", "reading leaves it there");
}
#[test]
fn errors_say_what_went_wrong() {
assert_eq!(
ClipboardError::Empty.to_string(),
"the clipboard holds no text"
);
let busy = ClipboardError::Unavailable("busy".into());
assert_eq!(busy.to_string(), "the clipboard is unavailable: busy");
}
}