rat_text/
clipboard.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//!
//! There are too many clipboard crates.
//!

use crate::TextError;
use dyn_clone::{clone_box, DynClone};
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::sync::{Arc, Mutex, OnceLock};

#[derive(Debug)]
pub struct ClipboardError;

impl Display for ClipboardError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl Error for ClipboardError {}

impl From<ClipboardError> for TextError {
    fn from(_value: ClipboardError) -> Self {
        TextError::Clipboard
    }
}

/// Access some clipboard.
pub trait Clipboard: DynClone + Debug {
    /// Get text from the clipboard.
    fn get_string(&self) -> Result<String, ClipboardError>;

    /// Set text from the clipboard.
    fn set_string(&self, s: &str) -> Result<(), ClipboardError>;
}

static GLOBAL_CLIPBOARD: OnceLock<StaticClipboard> = OnceLock::new();

/// Get a Clone of the global default clipboard.
pub fn global_clipboard() -> Box<dyn Clipboard> {
    let c = GLOBAL_CLIPBOARD.get_or_init(StaticClipboard::default);
    Box::new(c.clone())
}

/// Change the global default clipboard.
pub fn set_global_clipboard(clipboard: impl Clipboard + Send + 'static) {
    let c = GLOBAL_CLIPBOARD.get_or_init(StaticClipboard::default);
    c.replace(clipboard);
}

/// Clipboard that can be set as a static.
/// It can replace the actual clipboard implementation at a later time.
/// Initializes with a LocalClipboard.
#[derive(Debug, Clone)]
struct StaticClipboard {
    clip: Arc<Mutex<Box<dyn Clipboard + Send>>>,
}

impl Default for StaticClipboard {
    fn default() -> Self {
        Self {
            clip: Arc::new(Mutex::new(Box::new(LocalClipboard::new()))),
        }
    }
}

impl StaticClipboard {
    /// Replace the static clipboard with the given one.
    fn replace(&self, clipboard: impl Clipboard + Send + 'static) {
        let mut clip = self.clip.lock().expect("clipboard-lock");
        *clip = Box::new(clipboard);
    }
}

impl Clipboard for StaticClipboard {
    fn get_string(&self) -> Result<String, ClipboardError> {
        self.clip.lock().expect("clipboard-lock").get_string()
    }

    fn set_string(&self, s: &str) -> Result<(), ClipboardError> {
        self.clip.lock().expect("clipboard-lock").set_string(s)
    }
}

/// Local clipboard.
/// A string in disguise.
#[derive(Debug, Default, Clone)]
pub struct LocalClipboard {
    text: Arc<Mutex<String>>,
}

impl LocalClipboard {
    pub fn new() -> Self {
        Self::default()
    }
}

impl Clipboard for LocalClipboard {
    fn get_string(&self) -> Result<String, ClipboardError> {
        match self.text.lock() {
            Ok(v) => Ok(v.clone()),
            Err(_) => Err(ClipboardError),
        }
    }

    fn set_string(&self, s: &str) -> Result<(), ClipboardError> {
        match self.text.lock() {
            Ok(mut v) => {
                *v = s.to_string();
                Ok(())
            }
            Err(_) => Err(ClipboardError),
        }
    }
}

impl Clone for Box<dyn Clipboard> {
    fn clone(&self) -> Self {
        clone_box(self.as_ref())
    }
}

impl Clipboard for Box<dyn Clipboard> {
    fn get_string(&self) -> Result<String, ClipboardError> {
        self.as_ref().get_string()
    }

    fn set_string(&self, s: &str) -> Result<(), ClipboardError> {
        self.as_ref().set_string(s)
    }
}