1use std::io::Write;
8use std::process::{Command, Stdio};
9
10pub fn copy_to_clipboard(text: &str) -> bool {
16 clipboard_commands()
17 .into_iter()
18 .any(|(bin, args)| pipe_to(bin, &args, text))
19}
20
21pub fn open_in_browser(target: &str) -> bool {
24 let (bin, mut args): (&str, Vec<&str>) = if cfg!(target_os = "macos") {
25 ("open", vec![])
26 } else if cfg!(target_os = "windows") {
27 ("cmd", vec!["/C", "start", ""])
29 } else {
30 ("xdg-open", vec![])
31 };
32 args.push(target);
33 Command::new(bin)
34 .args(&args)
35 .stdout(Stdio::null())
36 .stderr(Stdio::null())
37 .stdin(Stdio::null())
38 .spawn()
39 .is_ok()
40}
41
42fn clipboard_commands() -> Vec<(&'static str, Vec<&'static str>)> {
44 #[cfg(target_os = "macos")]
45 {
46 vec![("pbcopy", vec![])]
47 }
48 #[cfg(target_os = "windows")]
49 {
50 vec![("clip", vec![])]
51 }
52 #[cfg(all(unix, not(target_os = "macos")))]
53 {
54 vec![
55 ("wl-copy", vec![]),
56 ("xclip", vec!["-selection", "clipboard"]),
57 ("xsel", vec!["--clipboard", "--input"]),
58 ("clip.exe", vec![]), ]
60 }
61}
62
63fn pipe_to(bin: &str, args: &[&str], text: &str) -> bool {
65 let Ok(mut child) = Command::new(bin)
66 .args(args)
67 .stdin(Stdio::piped())
68 .stdout(Stdio::null())
69 .stderr(Stdio::null())
70 .spawn()
71 else {
72 return false;
73 };
74 {
77 let Some(mut stdin) = child.stdin.take() else {
78 return false;
79 };
80 if stdin.write_all(text.as_bytes()).is_err() {
81 return false;
82 }
83 }
84 matches!(child.wait(), Ok(status) if status.success())
85}
86
87#[cfg(test)]
88mod tests {
89 use super::clipboard_commands;
90
91 #[test]
92 fn clipboard_candidates_are_present_for_this_platform() {
93 assert!(!clipboard_commands().is_empty());
95 }
96
97 #[test]
98 fn clipboard_candidate_binaries_are_non_empty() {
99 for (bin, _) in clipboard_commands() {
100 assert!(!bin.is_empty(), "clipboard binary name must not be empty");
101 }
102 }
103}