1use std::fmt;
4
5pub trait Clipboard {
9 fn text(&mut self) -> Result<String, ClipboardError>;
15
16 fn set_text(&mut self, text: &str) -> Result<(), ClipboardError>;
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25#[non_exhaustive]
26pub enum ClipboardError {
27 Empty,
29 Unavailable(String),
31}
32
33impl fmt::Display for ClipboardError {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match self {
36 Self::Empty => f.write_str("the clipboard holds no text"),
37 Self::Unavailable(reason) => write!(f, "the clipboard is unavailable: {reason}"),
38 }
39 }
40}
41
42impl std::error::Error for ClipboardError {}
43
44#[derive(Debug, Clone, Default)]
46pub struct MemoryClipboard {
47 text: Option<String>,
48}
49
50impl Clipboard for MemoryClipboard {
51 fn text(&mut self) -> Result<String, ClipboardError> {
52 self.text.clone().ok_or(ClipboardError::Empty)
53 }
54
55 fn set_text(&mut self, text: &str) -> Result<(), ClipboardError> {
56 self.text = Some(text.to_owned());
57 Ok(())
58 }
59}
60
61pub(crate) struct Held(pub(crate) Box<dyn Clipboard>);
63
64impl Default for Held {
65 fn default() -> Self {
66 Self(Box::new(MemoryClipboard::default()))
67 }
68}
69
70impl fmt::Debug for Held {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 f.write_str("Clipboard")
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn a_memory_clipboard_gives_back_the_last_text_put_on_it() {
82 let mut clipboard = MemoryClipboard::default();
83 assert_eq!(clipboard.text(), Err(ClipboardError::Empty));
84 clipboard.set_text("one").unwrap();
85 clipboard.set_text("two").unwrap();
86 assert_eq!(clipboard.text().unwrap(), "two");
87 assert_eq!(clipboard.text().unwrap(), "two", "reading leaves it there");
88 }
89
90 #[test]
91 fn errors_say_what_went_wrong() {
92 assert_eq!(
93 ClipboardError::Empty.to_string(),
94 "the clipboard holds no text"
95 );
96 let busy = ClipboardError::Unavailable("busy".into());
97 assert_eq!(busy.to_string(), "the clipboard is unavailable: busy");
98 }
99}