use std::io::Write;
use std::process::{Command, Stdio};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClipboardCopyCommand {
pub argv: Vec<String>,
}
impl ClipboardCopyCommand {
#[must_use]
pub fn detect() -> Option<Self> {
#[cfg(target_os = "macos")]
{
unix_has_executable("pbcopy").then(|| Self {
argv: vec!["pbcopy".to_owned()],
})
}
#[cfg(windows)]
{
windows_has_clip().then(|| Self {
argv: vec!["clip".to_owned()],
})
}
#[cfg(all(unix, not(target_os = "macos")))]
{
if unix_has_executable("wl-copy") {
return Some(Self {
argv: vec!["wl-copy".to_owned()],
});
}
if unix_has_executable("xclip") {
return Some(Self {
argv: vec![
"xclip".to_owned(),
"-selection".to_owned(),
"clipboard".to_owned(),
],
});
}
if unix_has_executable("xsel") {
return Some(Self {
argv: vec![
"xsel".to_owned(),
"--clipboard".to_owned(),
"--input".to_owned(),
],
});
}
None
}
#[cfg(not(any(unix, windows)))]
{
None
}
}
pub fn copy_utf8(&self, text: &str) -> std::io::Result<()> {
let (prog, args) = self.argv.split_first().ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::InvalidInput, "clipboard argv is empty")
})?;
let mut child = Command::new(prog)
.args(args)
.stdin(Stdio::piped())
.spawn()?;
let mut stdin = child
.stdin
.take()
.ok_or_else(|| std::io::Error::other("clipboard command has no stdin"))?;
stdin.write_all(text.as_bytes())?;
drop(stdin);
let status = child.wait()?;
if status.success() {
Ok(())
} else {
Err(std::io::Error::other(format!(
"clipboard command exited with {status}"
)))
}
}
}
#[cfg(unix)]
fn unix_has_executable(name: &str) -> bool {
Command::new("sh")
.args(["-c", &format!("command -v {name}")])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.is_ok_and(|s| s.success())
}
#[cfg(windows)]
fn windows_has_clip() -> bool {
Command::new("where")
.arg("clip")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.is_ok_and(|s| s.success())
}