1use crate::TextError;
6use dyn_clone::{clone_box, DynClone};
7use std::error::Error;
8use std::fmt::{Debug, Display, Formatter};
9use std::sync::{Arc, Mutex, OnceLock};
10
11#[derive(Debug)]
12pub struct ClipboardError;
13
14impl Display for ClipboardError {
15 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
16 write!(f, "{:?}", self)
17 }
18}
19
20impl Error for ClipboardError {}
21
22impl From<ClipboardError> for TextError {
23 fn from(_value: ClipboardError) -> Self {
24 TextError::Clipboard
25 }
26}
27
28pub trait Clipboard: DynClone + Debug {
30 fn get_string(&self) -> Result<String, ClipboardError>;
32
33 fn set_string(&self, s: &str) -> Result<(), ClipboardError>;
35}
36
37static GLOBAL_CLIPBOARD: OnceLock<StaticClipboard> = OnceLock::new();
38
39pub fn global_clipboard() -> Box<dyn Clipboard> {
41 let c = GLOBAL_CLIPBOARD.get_or_init(StaticClipboard::default);
42 Box::new(c.clone())
43}
44
45pub fn set_global_clipboard(clipboard: impl Clipboard + Send + 'static) {
47 let c = GLOBAL_CLIPBOARD.get_or_init(StaticClipboard::default);
48 c.replace(clipboard);
49}
50
51#[derive(Debug, Clone)]
55struct StaticClipboard {
56 clip: Arc<Mutex<Box<dyn Clipboard + Send>>>,
57}
58
59impl Default for StaticClipboard {
60 fn default() -> Self {
61 Self {
62 clip: Arc::new(Mutex::new(Box::new(LocalClipboard::new()))),
63 }
64 }
65}
66
67impl StaticClipboard {
68 fn replace(&self, clipboard: impl Clipboard + Send + 'static) {
70 let mut clip = self.clip.lock().expect("clipboard-lock");
71 *clip = Box::new(clipboard);
72 }
73}
74
75impl Clipboard for StaticClipboard {
76 fn get_string(&self) -> Result<String, ClipboardError> {
77 self.clip.lock().expect("clipboard-lock").get_string()
78 }
79
80 fn set_string(&self, s: &str) -> Result<(), ClipboardError> {
81 self.clip.lock().expect("clipboard-lock").set_string(s)
82 }
83}
84
85#[derive(Debug, Default, Clone)]
88pub struct LocalClipboard {
89 text: Arc<Mutex<String>>,
90}
91
92impl LocalClipboard {
93 pub fn new() -> Self {
94 Self::default()
95 }
96}
97
98impl Clipboard for LocalClipboard {
99 fn get_string(&self) -> Result<String, ClipboardError> {
100 match self.text.lock() {
101 Ok(v) => Ok(v.clone()),
102 Err(_) => Err(ClipboardError),
103 }
104 }
105
106 fn set_string(&self, s: &str) -> Result<(), ClipboardError> {
107 match self.text.lock() {
108 Ok(mut v) => {
109 *v = s.to_string();
110 Ok(())
111 }
112 Err(_) => Err(ClipboardError),
113 }
114 }
115}
116
117impl Clone for Box<dyn Clipboard> {
118 fn clone(&self) -> Self {
119 clone_box(self.as_ref())
120 }
121}
122
123impl Clipboard for Box<dyn Clipboard> {
124 fn get_string(&self) -> Result<String, ClipboardError> {
125 self.as_ref().get_string()
126 }
127
128 fn set_string(&self, s: &str) -> Result<(), ClipboardError> {
129 self.as_ref().set_string(s)
130 }
131}