use std::io::{Error, ErrorKind};
use crate::pipe::{Duration, LockFile, Result};
use arboard::Clipboard as ArClipboard;
pub const CLIPBOARD_LOCKFILE: &str = "suave_clipboard";
pub struct Clipboard<'a> {
lock: LockFile<'a>,
}
pub enum WriteKind {
Immediate,
Guarantee,
KeepTrying(usize),
KeepTryingTimeout(usize, Duration),
}
impl<'a> Clipboard<'a> {
pub async fn connect() -> Result<Clipboard<'a>> {
let lock = LockFile::temp(CLIPBOARD_LOCKFILE).await?;
Ok(Self { lock })
}
pub fn read(&self) -> Result<String> {
let mut cb = ArClipboard::new().map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
let response = cb.get().text();
match response {
Ok(contents) => Ok(contents),
Err(e) => Err(Error::new(ErrorKind::Other, e.to_string())),
}
}
pub async fn write(&self, content: impl Into<String>, options: WriteKind) -> Result<()> {
let mut clipboard =
ArClipboard::new().map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
match options {
WriteKind::Immediate => {
let _lock = self.lock.lock().await?;
clipboard
.set_text(&content.into())
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
}
WriteKind::Guarantee => {
let mut lock = self.lock.lock().await;
while lock.is_err() {
lock = self.lock.lock().await;
}
clipboard
.set_text(&content.into())
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
}
WriteKind::KeepTrying(count) => {
let mut ntimes = 0;
let mut lock = self.lock.lock().await;
while lock.is_err() {
ntimes += 1;
if ntimes >= count {
return Err(Error::new(ErrorKind::Other, "max retries reached."));
}
lock = self.lock.lock().await;
}
clipboard
.set_text(&content.into())
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
}
WriteKind::KeepTryingTimeout(count, timeout) => {
let mut ntimes = 0;
let mut lock = self.lock.lock().await;
while lock.is_err() {
let timeout = tokio::time::Instant::now() + timeout;
let _ = tokio::time::sleep_until(timeout).await;
ntimes += 1;
if ntimes >= count {
return Err(Error::new(ErrorKind::Other, "max retries reached."));
}
lock = self.lock.lock().await;
}
clipboard
.set_text(&content.into())
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
}
}
}
pub fn contents() -> Result<String> {
ArClipboard::new()
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?
.get_text()
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
pub async fn test_clipboard_read_no_error() -> Result<()> {
let clipboard = Clipboard::connect().await?;
let content = clipboard.read()?;
eprintln!("clipboard content: {}", content);
Ok(())
}
#[tokio::test]
pub async fn test_clipboard_write() -> Result<()> {
let clipboard = Clipboard::connect().await?;
clipboard
.write("Hello, World!", WriteKind::Guarantee)
.await?;
let new_content = clipboard.read()?;
assert!(new_content == "Hello, World!");
Ok(())
}
}