pub(crate) fn set_system_clipboard(text: &str) {
#[cfg(not(test))]
{
if set_native(text) {
return;
}
emit_osc52(text);
}
#[cfg(test)]
{
let _ = text;
}
}
#[allow(dead_code)]
pub(crate) fn get_system_clipboard() -> Option<String> {
#[cfg(not(test))]
{
with_native(|cb| cb.get_text().ok()).flatten()
}
#[cfg(test)]
{
None
}
}
#[cfg(not(test))]
mod native {
use std::cell::RefCell;
thread_local! {
static HANDLE: RefCell<Option<arboard::Clipboard>> =
RefCell::new(arboard::Clipboard::new().ok());
}
pub(super) fn with_native<R>(f: impl FnOnce(&mut arboard::Clipboard) -> R) -> Option<R> {
HANDLE.with(|cell| cell.borrow_mut().as_mut().map(f))
}
}
#[cfg(not(test))]
use native::with_native;
#[cfg(not(test))]
fn set_native(text: &str) -> bool {
with_native(|cb| cb.set_text(text.to_owned()).is_ok()).unwrap_or(false)
}
#[cfg(not(test))]
fn emit_osc52(text: &str) {
use std::io::Write;
let seq = osc52_sequence(text);
let mut stdout = std::io::stdout();
let _ = stdout.write_all(seq.as_bytes());
let _ = stdout.flush();
}
fn osc52_sequence(text: &str) -> String {
format!("\x1b]52;c;{}\x07", base64_encode(text.as_bytes()))
}
fn base64_encode(input: &[u8]) -> String {
const TABLE: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut out = String::with_capacity(input.len().div_ceil(3) * 4);
for chunk in input.chunks(3) {
let b0 = chunk[0] as u32;
let b1 = *chunk.get(1).unwrap_or(&0) as u32;
let b2 = *chunk.get(2).unwrap_or(&0) as u32;
let triple = (b0 << 16) | (b1 << 8) | b2;
out.push(TABLE[((triple >> 18) & 0x3f) as usize] as char);
out.push(TABLE[((triple >> 12) & 0x3f) as usize] as char);
out.push(if chunk.len() > 1 {
TABLE[((triple >> 6) & 0x3f) as usize] as char
} else {
'='
});
out.push(if chunk.len() > 2 {
TABLE[(triple & 0x3f) as usize] as char
} else {
'='
});
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn base64_matches_known_vectors() {
assert_eq!(base64_encode(b""), "");
assert_eq!(base64_encode(b"f"), "Zg==");
assert_eq!(base64_encode(b"fo"), "Zm8=");
assert_eq!(base64_encode(b"foo"), "Zm9v");
assert_eq!(base64_encode(b"foob"), "Zm9vYg==");
assert_eq!(base64_encode(b"fooba"), "Zm9vYmE=");
assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
}
#[test]
fn osc52_wraps_base64_in_escape() {
assert_eq!(osc52_sequence("foo"), "\x1b]52;c;Zm9v\x07");
}
}