copypasta_ext/
osc52.rs

1//! OSC 52 escape sequence to set clipboard contents.
2//!
3//! This provider can set clipboard contents by outputting a sequence to stdout in supported
4//! terminals. It uses Xterm escape sequences, OSC 52 to be exact.
5//!
6//! Getting clipboard contents is not supported through this context and will error.
7//!
8//! ## Benefits
9//!
10//! - Keeps contents in clipboard for the terminal lifetime even after your application exists.
11//!
12//! ## Drawbacks
13//!
14//! - Requires terminal that supports these escape codes.
15//! - Doesn't catch errors while setting clipboard contents.
16//! - Cannot get clipboard contents.
17//!
18//! # Examples
19//!
20//! ```rust,no_run
21//! use copypasta_ext::prelude::*;
22//! use copypasta_ext::x11_bin::X11BinClipboardContext;
23//!
24//! let mut ctx = X11BinClipboardContext::new().unwrap();
25//! ctx.set_contents("some string".into()).unwrap();
26//! ```
27//!
28//! Use `new_with` to combine with another context such as [`X11ClipboardContext`][X11ClipboardContext] to support getting clipboard contents as well.
29//!
30//! ```rust,no_run
31//! use copypasta_ext::prelude::*;
32//! use copypasta_ext::osc52::Osc52ClipboardContext;
33//! use copypasta_ext::x11_bin::X11BinClipboardContext;
34//!
35//! let mut ctx = Osc52ClipboardContext::new_with(X11BinClipboardContext::new().unwrap()).unwrap();
36//! println!("{:?}", ctx.get_contents());
37//! ctx.set_contents("some string".into()).unwrap();
38//! ```
39//!
40//! [X11ClipboardContext]: https://docs.rs/copypasta/*/copypasta/x11_clipboard/struct.X11ClipboardContext.html
41
42use std::error::Error as StdError;
43use std::fmt;
44
45use base64::engine::Engine;
46
47use crate::combined::CombinedClipboardContext;
48use crate::display::DisplayServer;
49use crate::prelude::*;
50
51/// Platform specific context.
52///
53/// Alias for `Osc52ClipboardContext` on supported platforms, aliases to standard
54/// `ClipboardContext` provided by `rust-clipboard` on other platforms.
55pub type ClipboardContext = Osc52ClipboardContext;
56
57/// OSC 52 escape sequence to set clipboard contents.
58///
59/// See module documentation for more information.
60pub struct Osc52ClipboardContext;
61
62impl Osc52ClipboardContext {
63    pub fn new() -> Result<Self, Box<dyn StdError>> {
64        Ok(Self)
65    }
66
67    /// Construct combined with another context for getting the clipboard.
68    ///
69    /// This clipboard context only supports setting the clipboard contents.
70    /// You can combine this with the given context to support getting clipboard contents as well
71    /// to get the best of both worlds.
72    pub fn new_with<G>(get: G) -> Result<CombinedClipboardContext<G, Self>, Box<dyn StdError>>
73    where
74        G: ClipboardProvider,
75    {
76        Self::new()?.with(get)
77    }
78
79    /// Combine this context with [`X11ClipboardContext`][X11ClipboardContext].
80    ///
81    /// This clipboard context only supports setting the clipboard contents.
82    /// You can combine this with the given context to support getting clipboard contents as well
83    /// to get the best of both worlds.
84    pub fn with<G>(self, get: G) -> Result<CombinedClipboardContext<G, Self>, Box<dyn StdError>>
85    where
86        G: ClipboardProvider,
87    {
88        Ok(CombinedClipboardContext(get, self))
89    }
90}
91
92impl ClipboardProvider for Osc52ClipboardContext {
93    fn get_contents(&mut self) -> crate::ClipResult<String> {
94        Err(Error::Unsupported.into())
95    }
96
97    fn set_contents(&mut self, contents: String) -> crate::ClipResult<()> {
98        // Use OSC 52 escape sequence to set clipboard through stdout
99        print!(
100            "\x1B]52;c;{}\x07",
101            base64::engine::general_purpose::STANDARD.encode(&contents)
102        );
103        Ok(())
104    }
105}
106
107impl ClipboardProviderExt for Osc52ClipboardContext {
108    fn display_server(&self) -> Option<DisplayServer> {
109        Some(DisplayServer::Tty)
110    }
111
112    fn has_bin_lifetime(&self) -> bool {
113        false
114    }
115}
116
117/// Represents OSC 52 clipboard related error.
118#[derive(Debug)]
119#[non_exhaustive]
120pub enum Error {
121    /// Getting clipboard contents is not supported.
122    Unsupported,
123}
124
125impl fmt::Display for Error {
126    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127        match self {
128            Error::Unsupported => write!(
129                f,
130                "Getting clipboard contents is not supported through this context"
131            ),
132        }
133    }
134}
135
136impl StdError for Error {
137    fn source(&self) -> Option<&(dyn StdError + 'static)> {
138        None
139    }
140}