use std::io::Write;
use std::sync::{Mutex, OnceLock};
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::Engine as _;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Transport {
Native,
Osc52,
}
pub fn osc52(text: &str) -> String {
format!("\x1b]52;c;{}\x07", BASE64.encode(text))
}
fn native() -> &'static Mutex<Option<arboard::Clipboard>> {
static NATIVE: OnceLock<Mutex<Option<arboard::Clipboard>>> = OnceLock::new();
NATIVE.get_or_init(|| Mutex::new(arboard::Clipboard::new().ok()))
}
pub fn copy(text: &str) -> Result<Transport, String> {
let copied = native().lock().ok().is_some_and(|mut guard| {
guard
.as_mut()
.is_some_and(|clip| clip.set_text(text.to_string()).is_ok())
});
if copied {
return Ok(Transport::Native);
}
let mut stdout = std::io::stdout();
stdout
.write_all(osc52(text).as_bytes())
.and_then(|()| stdout.flush())
.map_err(|error| error.to_string())?;
Ok(Transport::Osc52)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn osc52_wraps_base64_in_the_escape_sequence() {
assert_eq!(osc52("hi"), "\x1b]52;c;aGk=\x07");
assert_eq!(osc52(""), "\x1b]52;c;\x07");
}
}